1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
10 //
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
24 //
25 
26 #include "SearchFile.h"			// Interface declarations.
27 
28 #include <tags/FileTags.h>
29 
30 #include "amule.h"				// Needed for theApp
31 #include "CanceledFileList.h"
32 #include "MemFile.h"			// Needed for CMemFile
33 #include "Preferences.h"		// Needed for thePrefs
34 #include "GuiEvents.h"
35 #include "Logger.h"
36 #include "PartFile.h"			// Needed for CPartFile::CanAddSource
37 #include "DownloadQueue.h"		// Needed for CDownloadQueue
38 #include "KnownFileList.h"		// Needed for CKnownFileList
39 
CSearchFile(const CMemFile & data,bool optUTF8,wxUIntPtr searchID,uint32_t serverIP,uint16_t serverPort,const wxString & directory,bool kademlia)40 CSearchFile::CSearchFile(const CMemFile& data, bool optUTF8, wxUIntPtr searchID, uint32_t serverIP, uint16_t serverPort, const wxString& directory, bool kademlia)
41 	: m_parent(NULL),
42 	  m_showChildren(false),
43 	  m_searchID(searchID),
44 	  m_sourceCount(0),
45 	  m_completeSourceCount(0),
46 	  m_kademlia(kademlia),
47 	  m_downloadStatus(NEW),
48 	  m_directory(directory),
49 	  m_clientServerIP(serverIP),
50 	  m_clientServerPort(serverPort),
51 	  m_kadPublishInfo(0)
52 {
53 	m_abyFileHash = data.ReadHash();
54 	SetDownloadStatus();
55 	m_clientID = data.ReadUInt32();
56 	m_clientPort = data.ReadUInt16();
57 
58 	if (!m_clientID || !m_clientPort || !IsGoodIP(m_clientID, thePrefs::FilterLanIPs())) {
59 		m_clientID = 0;
60 		m_clientPort = 0;
61 	}
62 
63 	uint32 tagcount = data.ReadUInt32();
64 	for (unsigned int i = 0; i < tagcount; ++i) {
65 		CTag tag(data, optUTF8);
66 		switch (tag.GetNameID()) {
67 			case FT_FILENAME:
68 				SetFileName(CPath(tag.GetStr()));
69 				break;
70 			case FT_FILESIZE:
71 				SetFileSize(tag.GetInt());
72 				break;
73 			case FT_FILESIZE_HI:
74 				SetFileSize((((uint64)tag.GetInt()) << 32) + GetFileSize());
75 				break;
76 			case FT_FILERATING:
77 				m_iUserRating = (tag.GetInt() & 0xF) / 3;
78 				break;
79 			case FT_SOURCES:
80 				m_sourceCount = tag.GetInt();
81 				break;
82 			case FT_COMPLETE_SOURCES:
83 				m_completeSourceCount = tag.GetInt();
84 				break;
85 			case FT_PERMISSIONS:
86 			case FT_KADLASTPUBLISHKEY:
87 			case FT_PARTFILENAME:
88 				// Just ignore
89 				break;
90 			default:
91 				AddTagUnique(tag);
92 		}
93 	}
94 
95 	if (!GetFileName().IsOk()) {
96 		throw CInvalidPacket(wxT("No filename in search result"));
97 	}
98 }
99 
100 
CSearchFile(const CSearchFile & other)101 CSearchFile::CSearchFile(const CSearchFile& other)
102 	: CAbstractFile(other),
103 	  CECID(),	// create a new ID for now
104 	  m_parent(other.m_parent),
105 	  m_showChildren(other.m_showChildren),
106 	  m_searchID(other.m_searchID),
107 	  m_sourceCount(other.m_sourceCount),
108 	  m_completeSourceCount(other.m_completeSourceCount),
109 	  m_kademlia(other.m_kademlia),
110 	  m_downloadStatus(other.m_downloadStatus),
111 	  m_directory(other.m_directory),
112 	  m_clients(other.m_clients),
113 	  m_clientID(other.m_clientID),
114 	  m_clientPort(other.m_clientPort),
115 	  m_clientServerIP(other.m_clientServerIP),
116 	  m_clientServerPort(other.m_clientServerPort),
117 	  m_kadPublishInfo(other.m_kadPublishInfo)
118 {
119 	for (size_t i = 0; i < other.m_children.size(); ++i) {
120 		m_children.push_back(new CSearchFile(*other.m_children.at(i)));
121 	}
122 }
123 
124 
~CSearchFile()125 CSearchFile::~CSearchFile()
126 {
127 	for (size_t i = 0; i < m_children.size(); ++i) {
128 		delete m_children.at(i);
129 	}
130 }
131 
132 
AddClient(const ClientStruct & client)133 void CSearchFile::AddClient(const ClientStruct& client)
134 {
135 	for (std::list<ClientStruct>::const_iterator it = m_clients.begin(); it != m_clients.end(); ++it) {
136 		if (client.m_ip == it->m_ip && client.m_port == it->m_port) return;
137 	}
138 	m_clients.push_back(client);
139 }
140 
MergeResults(const CSearchFile & other)141 void CSearchFile::MergeResults(const CSearchFile& other)
142 {
143 	// Sources
144 	if (m_kademlia) {
145 		m_sourceCount = std::max(m_sourceCount, other.m_sourceCount);
146 		m_completeSourceCount = std::max(m_completeSourceCount, other.m_completeSourceCount);
147 	} else {
148 		m_sourceCount += other.m_sourceCount;
149 		m_completeSourceCount += other.m_completeSourceCount;
150 	}
151 
152 	// Publish info
153 	if (m_kadPublishInfo == 0) {
154 		if (other.m_kadPublishInfo != 0) {
155 			m_kadPublishInfo = other.m_kadPublishInfo;
156 		}
157 	} else {
158 		if (other.m_kadPublishInfo != 0) {
159 			m_kadPublishInfo =
160 				std::max(m_kadPublishInfo & 0xFF000000, other.m_kadPublishInfo & 0xFF000000) |
161 				std::max(m_kadPublishInfo & 0x00FF0000, other.m_kadPublishInfo & 0x00FF0000) |
162 				(((m_kadPublishInfo & 0x0000FFFF) + (other.m_kadPublishInfo & 0x0000FFFF)) >> 1);
163 		}
164 	}
165 
166 	// Rating
167 	if (m_iUserRating != 0) {
168 		if (other.m_iUserRating != 0) {
169 			m_iUserRating = (m_iUserRating + other.m_iUserRating) / 2;
170 		}
171 	} else {
172 		if (other.m_iUserRating != 0) {
173 			m_iUserRating = other.m_iUserRating;
174 		}
175 	}
176 
177 	// copy possible available sources from new result
178 	if (other.GetClientID() && other.GetClientPort()) {
179 		// pre-filter sources which would be dropped by CPartFile::AddSources
180 		if (CPartFile::CanAddSource(other.GetClientID(), other.GetClientPort(), other.GetClientServerIP(), other.GetClientServerPort())) {
181 			CSearchFile::ClientStruct client(other.GetClientID(), other.GetClientPort(), other.GetClientServerIP(), other.GetClientServerPort());
182 			AddClient(client);
183 		}
184 	}
185 }
186 
187 
AddChild(CSearchFile * file)188 void CSearchFile::AddChild(CSearchFile* file)
189 {
190 	wxCHECK_RET(file, wxT("Not a valid child!"));
191 	wxCHECK_RET(!file->GetParent(), wxT("Search-result can only be child of one other result"));
192 	wxCHECK_RET(!file->HasChildren(), wxT("Result already has children, cannot become child."));
193 	wxCHECK_RET(!GetParent(), wxT("A child cannot have children of its own"));
194 	wxCHECK_RET(GetFileHash() == file->GetFileHash(), wxT("Mismatching child/parent hashes"));
195 	wxCHECK_RET(GetFileSize() == file->GetFileSize(), wxT("Mismatching child/parent sizes"));
196 
197 	// If no children exists, then we add the current item.
198 	if (GetChildren().empty()) {
199 		// Merging duplicate names instead of adding a new one
200 		if (file->GetFileName() == GetFileName()) {
201 			AddDebugLogLineN(logSearch, CFormat(wxT("Merged results for '%s'")) % GetFileName());
202 			MergeResults(*file);
203 			delete file;
204 			return;
205 		} else {
206 			// The first child will always be the first result we received.
207 			AddDebugLogLineN(logSearch, CFormat(wxT("Created initial child for result '%s'")) % GetFileName());
208 			m_children.push_back(new CSearchFile(*this));
209 			m_children.back()->m_parent = this;
210 		}
211 	}
212 
213 	file->m_parent = this;
214 
215 	for (size_t i = 0; i < m_children.size(); ++i) {
216 		CSearchFile* other = m_children.at(i);
217 		// Merge duplicate filenames
218 		if (other->GetFileName() == file->GetFileName()) {
219 			other->MergeResults(*file);
220 			UpdateParent();
221 			delete file;
222 			return;
223 		}
224 	}
225 
226 	// New unique child.
227 	m_children.push_back(file);
228 	UpdateParent();
229 
230 	if (ShowChildren()) {
231 		Notify_Search_Add_Result(file);
232 	}
233 }
234 
235 
UpdateParent()236 void CSearchFile::UpdateParent()
237 {
238 	wxCHECK_RET(!m_parent, wxT("UpdateParent called on child item"));
239 
240 	uint32_t sourceCount = 0;		// ed2k: sum of all sources, kad: the max sources found
241 	uint32_t completeSourceCount = 0;	// ed2k: sum of all sources, kad: the max sources found
242 	uint32_t differentNames = 0;		// max known different names
243 	uint32_t publishersKnown = 0;		// max publishers known
244 	uint32_t trustValue = 0;		// average trust value
245 	unsigned publishInfoTags = 0;
246 	unsigned ratingCount = 0;
247 	unsigned ratingTotal = 0;
248 	CSearchResultList::const_iterator best = m_children.begin();
249 	for (CSearchResultList::const_iterator it = m_children.begin(); it != m_children.end(); ++it) {
250 		const CSearchFile* child = *it;
251 
252 		// Locate the most common name
253 		if (child->GetSourceCount() > (*best)->GetSourceCount()) {
254 			best = it;
255 		}
256 
257 		// Sources
258 		if (m_kademlia) {
259 			sourceCount = std::max(sourceCount, child->m_sourceCount);
260 			completeSourceCount = std::max(completeSourceCount, child->m_completeSourceCount);
261 		} else {
262 			sourceCount += child->m_sourceCount;
263 			completeSourceCount += child->m_completeSourceCount;
264 		}
265 
266 		// Publish info
267 		if (child->GetKadPublishInfo() != 0) {
268 			differentNames = std::max(differentNames, (child->GetKadPublishInfo() & 0xFF000000) >> 24);
269 			publishersKnown = std::max(publishersKnown, (child->GetKadPublishInfo() & 0x00FF0000) >> 16);
270 			trustValue += child->GetKadPublishInfo() & 0x0000FFFF;
271 			publishInfoTags++;
272 		}
273 
274 		// Rating
275 		if (child->HasRating()) {
276 			ratingCount++;
277 			ratingTotal += child->UserRating();
278 		}
279 
280 		// Available sources
281 		if (child->GetClientID() && child->GetClientPort()) {
282 			CSearchFile::ClientStruct client(child->GetClientID(), child->GetClientPort(), child->GetClientServerIP(), child->GetClientServerPort());
283 			AddClient(client);
284 		}
285 		for (std::list<ClientStruct>::const_iterator cit = child->m_clients.begin(); cit != child->m_clients.end(); ++cit) {
286 			AddClient(*cit);
287 		}
288 	}
289 
290 	m_sourceCount = sourceCount;
291 	m_completeSourceCount = completeSourceCount;
292 
293 	if (publishInfoTags > 0) {
294 		m_kadPublishInfo = ((differentNames & 0x000000FF) << 24) | ((publishersKnown & 0x000000FF) << 16) | ((trustValue / publishInfoTags) & 0x0000FFFF);
295 	} else {
296 		m_kadPublishInfo = 0;
297 	}
298 
299 	if (ratingCount > 0) {
300 		m_iUserRating = ratingTotal / ratingCount;
301 	} else {
302 		m_iUserRating = 0;
303 	}
304 
305 	SetFileName((*best)->GetFileName());
306 }
307 
SetDownloadStatus()308 void CSearchFile::SetDownloadStatus()
309 {
310 	bool isPart		= theApp->downloadqueue->GetFileByID(m_abyFileHash) != NULL;
311 	bool isKnown	= theApp->knownfiles->FindKnownFileByID(m_abyFileHash) != NULL;
312 	bool isCanceled	= theApp->canceledfiles->IsCanceledFile(m_abyFileHash);
313 
314 	if (isCanceled && isPart) {
315 		m_downloadStatus = QUEUEDCANCELED;
316 	} else if (isCanceled) {
317 		m_downloadStatus = CANCELED;
318 	} else if (isPart) {
319 		m_downloadStatus = QUEUED;
320 	} else if (isKnown) {
321 		m_downloadStatus = DOWNLOADED;
322 	} else {
323 		m_downloadStatus = NEW;
324 	}
325 	// Update status of children too
326 	for (CSearchResultList::iterator it = m_children.begin(); it != m_children.end(); ++it) {
327 		Notify_Search_Update_Sources(*it);
328 	}
329 }
330 
331 // File_checked_for_headers
332