1 //
2 // This file is part of the aMule Project.
3 //
4 // Parts of this file are based on work from pan One (http://home-3.tiscali.nl/~meost/pms/)
5 //
6 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
7 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
8 //
9 // Any parts of this program derived from the xMule, lMule or eMule project,
10 // or contributed by third-party developers are copyrighted by their
11 // respective authors.
12 //
13 // This program is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation; either version 2 of the License, or
16 // (at your option) any later version.
17 //
18 // This program is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
26 //
27 
28 
29 #include "KnownFile.h"		// Do_not_auto_remove
30 
31 #include <protocol/kad/Constants.h>
32 #include <protocol/ed2k/Client2Client/TCP.h>
33 #include <protocol/ed2k/ClientSoftware.h>
34 #include <protocol/Protocols.h>
35 #include <tags/FileTags.h>
36 
37 #include <wx/config.h>
38 
39 #ifdef CLIENT_GUI
40 #include "UpDownClientEC.h"	// Needed for CUpDownClient
41 #else
42 #include "updownclient.h"	// Needed for CUpDownClient
43 #endif
44 
45 #include "MemFile.h"		// Needed for CMemFile
46 #include "Packet.h"		// Needed for CPacket
47 #include "Preferences.h"	// Needed for CPreferences
48 #include "KnownFileList.h"	// Needed for CKnownFileList
49 #include "amule.h"		// Needed for theApp
50 #include "PartFile.h"		// Needed for SavePartFile
51 #include "ClientList.h"	// Needed for clientlist (buddy support)
52 #include "Logger.h"
53 #include "ScopedPtr.h"		// Needed for CScopedArray and CScopedPtr
54 #include "GuiEvents.h"		// Needed for Notify_*
55 #include "SearchFile.h"		// Needed for CSearchFile
56 #include "FileArea.h"		// Needed for CFileArea
57 #include "FileAutoClose.h"	// Needed for CFileAutoClose
58 #include "Server.h"			// Needed for CServer
59 
60 #include "CryptoPP_Inc.h"       // Needed for MD4
61 
62 #include <common/Format.h>
63 
CFileStatistic(CKnownFile * parent)64 CFileStatistic::CFileStatistic(CKnownFile *parent)
65 	: fileParent(parent),
66 	  requested(0),
67 	  transferred(0),
68 	  accepted(0),
69 	  alltimerequested(0),
70 	  alltimetransferred(0),
71 	  alltimeaccepted(0)
72 {}
73 
74 #ifndef CLIENT_GUI
75 
AddRequest()76 void CFileStatistic::AddRequest()
77 {
78 	requested++;
79 	alltimerequested++;
80 	theApp->knownfiles->requested++;
81 	theApp->sharedfiles->UpdateItem(fileParent);
82 }
83 
AddAccepted()84 void CFileStatistic::AddAccepted()
85 {
86 	accepted++;
87 	alltimeaccepted++;
88 	theApp->knownfiles->accepted++;
89 	theApp->sharedfiles->UpdateItem(fileParent);
90 }
91 
AddTransferred(uint64 bytes)92 void CFileStatistic::AddTransferred(uint64 bytes)
93 {
94 	transferred += bytes;
95 	alltimetransferred += bytes;
96 	theApp->knownfiles->transferred += bytes;
97 	theApp->sharedfiles->UpdateItem(fileParent);
98 }
99 
100 #endif // CLIENT_GUI
101 
102 
103 /* Abstract File (base class)*/
104 
CAbstractFile()105 CAbstractFile::CAbstractFile()
106 	: m_iRating(0),
107 	  m_hasComment(false),
108 	  m_iUserRating(0),
109 	  m_nFileSize(0)
110 {}
111 
112 
CAbstractFile(const CAbstractFile & other)113 CAbstractFile::CAbstractFile(const CAbstractFile& other)
114 	: m_abyFileHash(other.m_abyFileHash),
115 	  m_strComment(other.m_strComment),
116 	  m_iRating(other.m_iRating),
117 	  m_hasComment(other.m_hasComment),
118 	  m_iUserRating(other.m_iUserRating),
119 	  m_taglist(other.m_taglist),
120 	  m_kadNotes(),
121 	  m_nFileSize(other.m_nFileSize),
122 	  m_fileName(other.m_fileName)
123 {
124 /* // TODO: Currently it's not safe to duplicate the entries, but isn't needed either.
125 	CKadEntryPtrList::const_iterator it = other.m_kadNotes.begin();
126 	for (; it != other.m_kadNotes.end(); ++it) {
127 		m_kadNotes.push_back(new Kademlia::CEntry(**it));
128 	}
129 */
130 }
131 
132 
SetFileName(const CPath & fileName)133 void CAbstractFile::SetFileName(const CPath& fileName)
134 {
135 	m_fileName = fileName;
136 }
137 
GetIntTagValue(uint8 tagname) const138 uint32 CAbstractFile::GetIntTagValue(uint8 tagname) const
139 {
140 	ArrayOfCTag::const_iterator it = m_taglist.begin();
141 	for (; it != m_taglist.end(); ++it){
142 		if (((*it).GetNameID() == tagname) && (*it).IsInt()) {
143 			return (*it).GetInt();
144 		}
145 	}
146 	return 0;
147 }
148 
GetIntTagValue(uint8 tagname,uint32 & ruValue) const149 bool CAbstractFile::GetIntTagValue(uint8 tagname, uint32& ruValue) const
150 {
151 	ArrayOfCTag::const_iterator it = m_taglist.begin();
152 	for (; it != m_taglist.end(); ++it){
153 		if (((*it).GetNameID() == tagname) && (*it).IsInt()){
154 			ruValue = (*it).GetInt();
155 			return true;
156 		}
157 	}
158 	return false;
159 }
160 
GetIntTagValue(const wxString & tagname) const161 uint32 CAbstractFile::GetIntTagValue(const wxString& tagname) const
162 {
163 	ArrayOfCTag::const_iterator it = m_taglist.begin();
164 	for (; it != m_taglist.end(); ++it){
165 		if ((*it).IsInt() && ((*it).GetName() == tagname)) {
166 			return (*it).GetInt();
167 		}
168 	}
169 	return 0;
170 }
171 
GetStrTagValue(uint8 tagname) const172 const wxString& CAbstractFile::GetStrTagValue(uint8 tagname) const
173 {
174 	ArrayOfCTag::const_iterator it = m_taglist.begin();
175 	for (; it != m_taglist.end(); ++it){
176 		if ((*it).GetNameID() == tagname && (*it).IsStr()) {
177 			return (*it).GetStr();
178 		}
179 	}
180 	return EmptyString;
181 }
182 
GetStrTagValue(const wxString & tagname) const183 const wxString& CAbstractFile::GetStrTagValue(const wxString& tagname) const
184 {
185 	ArrayOfCTag::const_iterator it = m_taglist.begin();
186 	for (; it != m_taglist.end(); ++it){
187 		if ((*it).IsStr() && ((*it).GetName() == tagname)) {
188 			return (*it).GetStr();
189 		}
190 	}
191 	return EmptyString;
192 }
193 
GetTag(uint8 tagname,uint8 tagtype) const194 const CTag *CAbstractFile::GetTag(uint8 tagname, uint8 tagtype) const
195 {
196 	ArrayOfCTag::const_iterator it = m_taglist.begin();
197 	for (; it != m_taglist.end(); ++it){
198 		if ((*it).GetNameID() == tagname && (*it).GetType() == tagtype) {
199 			return &(*it);
200 		}
201 	}
202 	return NULL;
203 }
204 
GetTag(const wxString & tagname,uint8 tagtype) const205 const CTag *CAbstractFile::GetTag(const wxString& tagname, uint8 tagtype) const
206 {
207 	ArrayOfCTag::const_iterator it = m_taglist.begin();
208 	for (; it != m_taglist.end(); ++it){
209 		if ((*it).GetType() == tagtype && (*it).GetName() == tagname) {
210 			return &(*it);
211 		}
212 	}
213 	return NULL;
214 }
215 
GetTag(uint8 tagname) const216 const CTag *CAbstractFile::GetTag(uint8 tagname) const
217 {
218 	ArrayOfCTag::const_iterator it = m_taglist.begin();
219 	for (; it != m_taglist.end(); ++it){
220 		if ((*it).GetNameID() == tagname) {
221 			return &(*it);
222 		}
223 	}
224 	return NULL;
225 }
226 
GetTag(const wxString & tagname) const227 const CTag *CAbstractFile::GetTag(const wxString& tagname) const
228 {
229 	ArrayOfCTag::const_iterator it = m_taglist.begin();
230 	for (; it != m_taglist.end(); ++it){
231 		if ((*it).GetName() == tagname) {
232 			return &(*it);
233 		}
234 	}
235 	return NULL;
236 }
237 
AddTagUnique(const CTag & rTag)238 void CAbstractFile::AddTagUnique(const CTag &rTag)
239 {
240 	ArrayOfCTag::iterator it = m_taglist.begin();
241 	for (; it != m_taglist.end(); ++it) {
242 		if ( ( ((*it).GetNameID() != 0 &&
243 			(*it).GetNameID() == rTag.GetNameID()) ||
244 		       (!(*it).GetName().IsEmpty() &&
245 			!rTag.GetName().IsEmpty() &&
246 			(*it).GetName() == rTag.GetName()) ) &&
247 		     (*it).GetType() == rTag.GetType())
248 		{
249 			it = m_taglist.erase(it);
250 			m_taglist.insert(it, rTag);
251 			return;
252 		}
253 	}
254 	m_taglist.push_back(rTag);
255 }
256 
257 #ifndef CLIENT_GUI
AddNote(Kademlia::CEntry * pEntry)258 void CAbstractFile::AddNote(Kademlia::CEntry *pEntry)
259 {
260 	CKadEntryPtrList::iterator it = m_kadNotes.begin();
261 	for (; it != m_kadNotes.end(); ++it) {
262 		Kademlia::CEntry* entry = *it;
263 		if(entry->m_uIP == pEntry->m_uIP || entry->m_uSourceID == pEntry->m_uSourceID) {
264 			delete pEntry;
265 			return;
266 		}
267 	}
268 	m_kadNotes.push_front(pEntry);
269 }
270 #else
AddNote(Kademlia::CEntry *)271 void CAbstractFile::AddNote(Kademlia::CEntry *)
272 {
273 }
274 #endif
275 
276 
277 /* Known File */
278 
CKnownFile()279 CKnownFile::CKnownFile()
280 	: statistic(this)
281 {
282 	Init();
283 }
284 
CKnownFile(uint32 ecid)285 CKnownFile::CKnownFile(uint32 ecid)
286 	: CECID(ecid),
287 	  statistic(this)
288 {
289 	Init();
290 }
291 
292 
293 //#warning Experimental: Construct a CKnownFile from a CSearchFile
CKnownFile(const CSearchFile & searchFile)294 CKnownFile::CKnownFile(const CSearchFile &searchFile)
295 :
296 	// This will copy the file hash
297 	CAbstractFile(static_cast<const CAbstractFile &>(searchFile)),
298 	statistic(this)
299 {
300 	Init();
301 
302 	// Use CKnownFile::SetFileName()
303 	SetFileName(searchFile.GetFileName());
304 
305 	// Use CKnownFile::SetFileSize()
306 	SetFileSize(searchFile.GetFileSize());
307 }
308 
309 
Init()310 void CKnownFile::Init()
311 {
312 	m_showSources = false;
313 	m_showPeers = false;
314 	m_nCompleteSourcesTime = time(NULL);
315 	m_nCompleteSourcesCount = 0;
316 	m_nCompleteSourcesCountLo = 0;
317 	m_nCompleteSourcesCountHi = 0;
318 	m_bCommentLoaded = false;
319 	m_iPartCount = 0;
320 	m_iED2KPartCount = 0;
321 	m_iED2KPartHashCount = 0;
322 	m_PublishedED2K = false;
323 	kadFileSearchID = 0;
324 	m_lastPublishTimeKadSrc = 0;
325 	m_lastPublishTimeKadNotes = 0;
326 	m_lastBuddyIP = 0;
327 	m_lastDateChanged = 0;
328 	m_bAutoUpPriority = thePrefs::GetNewAutoUp();
329 	m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
330 	m_hashingProgress = 0;
331 
332 #ifndef CLIENT_GUI
333 	m_pAICHHashSet = new CAICHHashSet(this);
334 #endif
335 }
336 
337 
SetFileSize(uint64 nFileSize)338 void CKnownFile::SetFileSize(uint64 nFileSize)
339 {
340 	CAbstractFile::SetFileSize(nFileSize);
341 #ifndef CLIENT_GUI
342 	m_pAICHHashSet->SetFileSize(nFileSize);
343 #endif
344 
345 	// Examples of parthashs, hashsets and filehashs for different filesizes
346 	// according the ed2k protocol
347 	//----------------------------------------------------------------------
348 	//
349 	//File size: 3 bytes
350 	//File hash: 2D55E87D0E21F49B9AD25F98531F3724
351 	//Nr. hashs: 0
352 	//
353 	//
354 	//File size: 1*PARTSIZE
355 	//File hash: A72CA8DF7F07154E217C236C89C17619
356 	//Nr. hashs: 2
357 	//Hash[  0]: 4891ED2E5C9C49F442145A3A5F608299
358 	//Hash[  1]: 31D6CFE0D16AE931B73C59D7E0C089C0	*special part hash*
359 	//
360 	//
361 	//File size: 1*PARTSIZE + 1 byte
362 	//File hash: 2F620AE9D462CBB6A59FE8401D2B3D23
363 	//Nr. hashs: 2
364 	//Hash[  0]: 121795F0BEDE02DDC7C5426D0995F53F
365 	//Hash[  1]: C329E527945B8FE75B3C5E8826755747
366 	//
367 	//
368 	//File size: 2*PARTSIZE
369 	//File hash: A54C5E562D5E03CA7D77961EB9A745A4
370 	//Nr. hashs: 3
371 	//Hash[  0]: B3F5CE2A06BF403BFB9BFFF68BDDC4D9
372 	//Hash[  1]: 509AA30C9EA8FC136B1159DF2F35B8A9
373 	//Hash[  2]: 31D6CFE0D16AE931B73C59D7E0C089C0	*special part hash*
374 	//
375 	//
376 	//File size: 3*PARTSIZE
377 	//File hash: 5E249B96F9A46A18FC2489B005BF2667
378 	//Nr. hashs: 4
379 	//Hash[  0]: 5319896A2ECAD43BF17E2E3575278E72
380 	//Hash[  1]: D86EF157D5E49C5ED502EDC15BB5F82B
381 	//Hash[  2]: 10F2D5B1FCB95C0840519C58D708480F
382 	//Hash[  3]: 31D6CFE0D16AE931B73C59D7E0C089C0	*special part hash*
383 	//
384 	//
385 	//File size: 3*PARTSIZE + 1 byte
386 	//File hash: 797ED552F34380CAFF8C958207E40355
387 	//Nr. hashs: 4
388 	//Hash[  0]: FC7FD02CCD6987DCF1421F4C0AF94FB8
389 	//Hash[  1]: 2FE466AF8A7C06DA3365317B75A5ACFE
390 	//Hash[  2]: 873D3BF52629F7C1527C6E8E473C1C30
391 	//Hash[  3]: BCE50BEE7877BB07BB6FDA56BFE142FB
392 	//
393 
394 	// File size       Data parts      ED2K parts      ED2K part hashs
395 	// ---------------------------------------------------------------
396 	// 1..PARTSIZE-1   1               1               0(!)
397 	// PARTSIZE        1               2(!)            2(!)
398 	// PARTSIZE+1      2               2               2
399 	// PARTSIZE*2      2               3(!)            3(!)
400 	// PARTSIZE*2+1    3               3               3
401 
402 	if (nFileSize == 0){
403 		//wxFAIL; // Kry - Why commented out by lemonfan? it can never be 0
404 		m_iPartCount = 0;
405 		m_iED2KPartCount = 0;
406 		m_iED2KPartHashCount = 0;
407 		m_sizeLastPart = 0;
408 		return;
409 	}
410 
411 	// nr. of data parts
412 	m_iPartCount = nFileSize / PARTSIZE + 1;
413 	// size of last part
414 	m_sizeLastPart = nFileSize % PARTSIZE;
415 	// file with size of n * PARTSIZE
416 	if (m_sizeLastPart == 0) {
417 		m_sizeLastPart = PARTSIZE;
418 		m_iPartCount--;
419 	}
420 
421 	// nr. of parts to be used with OP_FILESTATUS
422 	m_iED2KPartCount = nFileSize / PARTSIZE + 1;
423 
424 	// nr. of parts to be used with OP_HASHSETANSWER
425 	m_iED2KPartHashCount = nFileSize / PARTSIZE;
426 	if (m_iED2KPartHashCount != 0) {
427 		m_iED2KPartHashCount += 1;
428 	}
429 }
430 
431 
AddUploadingClient(CUpDownClient * client)432 void CKnownFile::AddUploadingClient(CUpDownClient* client)
433 {
434 	m_ClientUploadList.insert(CCLIENTREF(client, wxT("CKnownFile::AddUploadingClient m_ClientUploadList")));
435 
436 	SourceItemType type = UNAVAILABLE_SOURCE;
437 	switch (client->GetUploadState()) {
438 		case US_UPLOADING:
439 		case US_ONUPLOADQUEUE:
440 			type = AVAILABLE_SOURCE;
441 			break;
442 		default: {
443 			// Any other state is UNAVAILABLE_SOURCE by default.
444 		}
445 	}
446 
447 	Notify_SharedCtrlAddClient(this, CCLIENTREF(client, wxT("CKnownFile::AddUploadingClient Notify_SharedCtrlAddClient")), type);
448 
449 	UpdateAutoUpPriority();
450 }
451 
452 
RemoveUploadingClient(CUpDownClient * client)453 void CKnownFile::RemoveUploadingClient(CUpDownClient* client)
454 {
455 	if (m_ClientUploadList.erase(CCLIENTREF(client, wxEmptyString))) {
456 		Notify_SharedCtrlRemoveClient(client->ECID(), this);
457 		UpdateAutoUpPriority();
458 	}
459 }
460 
461 
462 #ifdef CLIENT_GUI
463 
CKnownFile(const CEC_SharedFile_Tag * tag)464 CKnownFile::CKnownFile(const CEC_SharedFile_Tag *tag)
465 	: CECID(tag->ID()),
466 	  statistic(this)
467 {
468 	Init();
469 
470 	m_abyFileHash = tag->FileHash();
471 	SetFileSize(tag->SizeFull());
472 	m_AvailPartFrequency.insert(m_AvailPartFrequency.end(), m_iPartCount, 0);
473 	m_queuedCount = 0;
474 }
475 
~CKnownFile()476 CKnownFile::~CKnownFile()
477 {}
478 
UpdateAutoUpPriority()479 void CKnownFile::UpdateAutoUpPriority()
480 {}
481 
482 
483 #else // ! CLIENT_GUI
484 
~CKnownFile()485 CKnownFile::~CKnownFile()
486 {
487 	SourceSet::iterator it = m_ClientUploadList.begin();
488 	for ( ; it != m_ClientUploadList.end(); ++it ) {
489 		it->ClearUploadFileID();
490 	}
491 
492 	delete m_pAICHHashSet;
493 }
494 
495 
SetFilePath(const CPath & filePath)496 void CKnownFile::SetFilePath(const CPath& filePath)
497 {
498 	m_filePath = filePath;
499 }
500 
501 
502 // needed for memfiles. its probably better to switch everything to CFile...
LoadHashsetFromFile(const CFileDataIO * file,bool checkhash)503 bool CKnownFile::LoadHashsetFromFile(const CFileDataIO* file, bool checkhash)
504 {
505 	CMD4Hash checkid = file->ReadHash();
506 
507 	uint16 parts = file->ReadUInt16();
508 	m_hashlist.clear();
509 	for (uint16 i = 0; i < parts; ++i){
510 		CMD4Hash cur_hash = file->ReadHash();
511 		m_hashlist.push_back(cur_hash);
512 	}
513 
514 	// SLUGFILLER: SafeHash - always check for valid m_hashlist
515 	if (!checkhash){
516 		m_abyFileHash = checkid;
517 		if (parts <= 1) {	// nothing to check
518 			return true;
519 		}
520 	} else {
521 		if ( m_abyFileHash != checkid ) {
522 			return false;	// wrong file?
523 		} else {
524 			if (parts != GetED2KPartHashCount()) {
525 				return false;
526 			}
527 		}
528 	}
529 	// SLUGFILLER: SafeHash
530 
531 	// trust noone ;-)
532 	// lol, useless comment but made me lmao
533 	// wtf you guys are weird.
534 
535 	if (!m_hashlist.empty()) {
536 		CreateHashFromHashlist(m_hashlist, &checkid);
537 	}
538 
539 	if ( m_abyFileHash == checkid ) {
540 		return true;
541 	} else {
542 		m_hashlist.clear();
543 		return false;
544 	}
545 }
546 
547 
LoadTagsFromFile(const CFileDataIO * file)548 bool CKnownFile::LoadTagsFromFile(const CFileDataIO* file)
549 {
550 	uint32 tagcount = file->ReadUInt32();
551 	m_taglist.clear();
552 	for (uint32 j = 0; j != tagcount; ++j) {
553 		CTag newtag(*file, true);
554 		switch(newtag.GetNameID()){
555 			case FT_FILENAME:
556 				if (GetFileName().IsOk()) {
557 					// Unlike eMule, we actually prefer the second
558 					// filename tag, since we use it to specify the
559 					// 'universial' filename (see CPath::ToUniv).
560 					CPath path = CPath::FromUniv(newtag.GetStr());
561 
562 					// May be invalid, if from older versions where
563 					// unicoded filenames be saved as empty-strings.
564 					if (path.IsOk()) {
565 						SetFileName(path);
566 					}
567 				} else {
568 					SetFileName(CPath(newtag.GetStr()));
569 				}
570 				break;
571 
572 			case FT_FILESIZE:
573 				SetFileSize(newtag.GetInt());
574 				m_AvailPartFrequency.clear();
575 				m_AvailPartFrequency.insert(
576 					m_AvailPartFrequency.begin(),
577 					GetPartCount(), 0);
578 				break;
579 
580 			case FT_ATTRANSFERRED:
581 				statistic.SetAllTimeTransferred(statistic.GetAllTimeTransferred() + newtag.GetInt());
582 				break;
583 
584 			case FT_ATTRANSFERREDHI:
585 				statistic.SetAllTimeTransferred(statistic.GetAllTimeTransferred() + (((uint64)newtag.GetInt()) << 32));
586 				break;
587 
588 			case FT_ATREQUESTED:
589 				statistic.SetAllTimeRequests(newtag.GetInt());
590 				break;
591 
592 			case FT_ATACCEPTED:
593 				statistic.SetAllTimeAccepts(newtag.GetInt());
594 				break;
595 
596 			case FT_ULPRIORITY:
597 				m_iUpPriority = newtag.GetInt();
598 				if( m_iUpPriority == PR_AUTO ){
599 					m_iUpPriority = PR_HIGH;
600 					m_bAutoUpPriority = true;
601 				} else {
602 					if (	m_iUpPriority != PR_VERYLOW &&
603 						m_iUpPriority != PR_LOW &&
604 						m_iUpPriority != PR_NORMAL &&
605 						m_iUpPriority != PR_HIGH &&
606 						m_iUpPriority != PR_VERYHIGH &&
607 						m_iUpPriority != PR_POWERSHARE) {
608 						m_iUpPriority = PR_NORMAL;
609 					}
610 
611 					m_bAutoUpPriority = false;
612 				}
613 				break;
614 
615 			case FT_PERMISSIONS:
616 			case FT_KADLASTPUBLISHKEY:
617 			case FT_PARTFILENAME:
618 				// Old tags, not used anymore. Just purge them.
619 				break;
620 
621 			case FT_AICH_HASH: {
622 				CAICHHash hash;
623 				bool hashSizeOk =
624 					hash.DecodeBase32(newtag.GetStr()) == CAICHHash::GetHashSize();
625 				wxASSERT(hashSizeOk);
626 				if (hashSizeOk) {
627 					m_pAICHHashSet->SetMasterHash(hash, AICH_HASHSETCOMPLETE);
628 				}
629 				break;
630 			}
631 
632 			case FT_KADLASTPUBLISHSRC:
633 				SetLastPublishTimeKadSrc( newtag.GetInt(), 0 );
634 
635 				if(GetLastPublishTimeKadSrc() > (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES) {
636 					//There may be a posibility of an older client that saved a random number here.. This will check for that..
637 					SetLastPublishTimeKadSrc(0, 0);
638 				}
639 				break;
640 
641 			case FT_KADLASTPUBLISHNOTES:
642 				SetLastPublishTimeKadNotes( newtag.GetInt() );
643 				break;
644 
645 			default:
646 				// Store them here and write them back on saving.
647 				m_taglist.push_back(newtag);
648 		}
649 	}
650 
651 	return true;
652 }
653 
654 
LoadDateFromFile(const CFileDataIO * file)655 bool CKnownFile::LoadDateFromFile(const CFileDataIO* file)
656 {
657 	m_lastDateChanged = file->ReadUInt32();
658 
659 	return true;
660 }
661 
662 
LoadFromFile(const CFileDataIO * file)663 bool CKnownFile::LoadFromFile(const CFileDataIO* file)
664 {
665 	// SLUGFILLER: SafeHash - load first, verify later
666 	bool ret1 = LoadDateFromFile(file);
667 	bool ret2 = LoadHashsetFromFile(file,false);
668 	bool ret3 = LoadTagsFromFile(file);
669 	UpdatePartsInfo();
670 	// Final hash-count verification, needs to be done after the tags are loaded.
671 	return ret1 && ret2 && ret3 && GetED2KPartHashCount()==GetHashCount();
672 	// SLUGFILLER: SafeHash
673 }
674 
675 
WriteToFile(CFileDataIO * file)676 bool CKnownFile::WriteToFile(CFileDataIO* file)
677 {
678 	wxCHECK(!IsPartFile(), false);
679 
680 	// date
681 	file->WriteUInt32(m_lastDateChanged);
682 	// hashset
683 	file->WriteHash(m_abyFileHash);
684 
685 	uint16 parts = m_hashlist.size();
686 	file->WriteUInt16(parts);
687 
688 	for (int i = 0; i < parts; ++i)
689 		file->WriteHash(m_hashlist[i]);
690 
691 	//tags
692 	const int iFixedTags = 8;
693 	uint32 tagcount = iFixedTags;
694 	if (HasProperAICHHashSet()) {
695 		tagcount++;
696 	}
697 	// Float meta tags are currently not written. All older eMule versions < 0.28a have
698 	// a bug in the meta tag reading+writing code. To achive maximum backward
699 	// compatibility for met files with older eMule versions we just don't write float
700 	// tags. This is OK, because we (eMule) do not use float tags. The only float tags
701 	// we may have to handle is the '# Sent' tag from the Hybrid, which is pretty
702 	// useless but may be received from us via the servers.
703 	//
704 	// The code for writing the float tags SHOULD BE ENABLED in SOME MONTHS (after most
705 	// people are using the newer eMule versions which do not write broken float tags).
706 	for (size_t j = 0; j < m_taglist.size(); ++j){
707 		if (m_taglist[j].IsInt() || m_taglist[j].IsStr()) {
708 			++tagcount;
709 		}
710 	}
711 
712 	if (m_lastPublishTimeKadSrc) {
713 		++tagcount;
714 	}
715 
716 	if (m_lastPublishTimeKadNotes){
717 		++tagcount;
718 	}
719 
720 	// standard tags
721 
722 	file->WriteUInt32(tagcount);
723 
724 	// We still save the unicoded filename, for backwards
725 	// compatibility with pre-2.2 and other clients.
726 	CTagString nametag_unicode(FT_FILENAME, GetFileName().GetRaw());
727 	// We write it with BOM to keep eMule compatibility
728 	nametag_unicode.WriteTagToFile(file,utf8strOptBOM);
729 
730 	// The non-unicoded filename is written in an 'universial'
731 	// format, which allows us to identify files, even if the
732 	// system locale changes.
733 	CTagString nametag(FT_FILENAME, CPath::ToUniv(GetFileName()));
734 	nametag.WriteTagToFile(file);
735 
736 	CTagIntSized sizetag(FT_FILESIZE, GetFileSize(), IsLargeFile() ? 64 : 32);
737 	sizetag.WriteTagToFile(file);
738 
739 	// statistic
740 	uint32 tran;
741 	tran = statistic.GetAllTimeTransferred() & 0xFFFFFFFF;
742 	CTagInt32 attag1(FT_ATTRANSFERRED, tran);
743 	attag1.WriteTagToFile(file);
744 
745 	tran = statistic.GetAllTimeTransferred() >> 32;
746 	CTagInt32 attag4(FT_ATTRANSFERREDHI, tran);
747 	attag4.WriteTagToFile(file);
748 
749 	CTagInt32 attag2(FT_ATREQUESTED, statistic.GetAllTimeRequests());
750 	attag2.WriteTagToFile(file);
751 
752 	CTagInt32 attag3(FT_ATACCEPTED, statistic.GetAllTimeAccepts());
753 	attag3.WriteTagToFile(file);
754 
755 	// priority N permission
756 	CTagInt32 priotag(FT_ULPRIORITY, IsAutoUpPriority() ? PR_AUTO : m_iUpPriority);
757 	priotag.WriteTagToFile(file);
758 
759 	//AICH Filehash
760 	if (HasProperAICHHashSet()) {
761 		CTagString aichtag(FT_AICH_HASH, m_pAICHHashSet->GetMasterHash().GetString());
762 		aichtag.WriteTagToFile(file);
763 	}
764 
765 	// Kad sources
766 	if (m_lastPublishTimeKadSrc){
767 		CTagInt32 kadLastPubSrc(FT_KADLASTPUBLISHSRC, m_lastPublishTimeKadSrc);
768 		kadLastPubSrc.WriteTagToFile(file);
769 	}
770 
771 	// Kad notes
772 	if (m_lastPublishTimeKadNotes){
773 		CTagInt32 kadLastPubNotes(FT_KADLASTPUBLISHNOTES, m_lastPublishTimeKadNotes);
774 		kadLastPubNotes.WriteTagToFile(file);
775 	}
776 
777 	//other tags
778 	for (size_t j = 0; j < m_taglist.size(); ++j){
779 		if (m_taglist[j].IsInt() || m_taglist[j].IsStr()) {
780 			m_taglist[j].WriteTagToFile(file);
781 		}
782 	}
783 	return true;
784 }
785 
786 
CreateHashFromHashlist(const ArrayOfCMD4Hash & hashes,CMD4Hash * Output)787 void CKnownFile::CreateHashFromHashlist(const ArrayOfCMD4Hash& hashes, CMD4Hash* Output)
788 {
789 	wxCHECK_RET(hashes.size(), wxT("No input to hash from in CreateHashFromHashlist"));
790 
791 	std::vector<byte> buffer(hashes.size() * MD4HASH_LENGTH);
792 	std::vector<byte>::iterator it = buffer.begin();
793 
794 	for (size_t i = 0; i < hashes.size(); ++i) {
795 		it = STLCopy_n(hashes[i].GetHash(), MD4HASH_LENGTH, it);
796 	}
797 
798 	CreateHashFromInput(&buffer[0], buffer.size(), Output, NULL);
799 }
800 
801 
CreateHashFromFile(CFileAutoClose & file,uint64 offset,uint32 Length,CMD4Hash * Output,CAICHHashTree * pShaHashOut)802 void CKnownFile::CreateHashFromFile(CFileAutoClose& file, uint64 offset, uint32 Length, CMD4Hash* Output, CAICHHashTree* pShaHashOut)
803 {
804 	wxCHECK_RET(Length, wxT("No input to hash from in CreateHashFromFile"));
805 
806 	CFileArea area;
807 	area.ReadAt(file, offset, Length);
808 
809 	CreateHashFromInput(area.GetBuffer(), Length, Output, pShaHashOut);
810 	area.CheckError();
811 }
812 
813 
CreateHashFromInput(const byte * input,uint32 Length,CMD4Hash * Output,CAICHHashTree * pShaHashOut)814 void CKnownFile::CreateHashFromInput(const byte* input, uint32 Length, CMD4Hash* Output, CAICHHashTree* pShaHashOut )
815 {
816 	wxASSERT_MSG(Output || pShaHashOut, wxT("Nothing to do in CreateHashFromInput"));
817 	{ wxCHECK_RET(input, wxT("No input to hash from in CreateHashFromInput")); }
818 	wxASSERT(Length <= PARTSIZE); // We never hash more than one PARTSIZE
819 
820 	CMemFile data(input, Length);
821 
822 	uint32 Required = Length;
823 	byte   X[64*128];
824 
825 	uint32	posCurrentEMBlock = 0;
826 	uint32	nIACHPos = 0;
827 	CScopedPtr<CAICHHashAlgo> pHashAlg(CAICHHashSet::GetNewHashAlgo());
828 
829 	// This is all AICH.
830 	while (Required >= 64) {
831 		uint32 len = Required / 64;
832 		if (len > sizeof(X)/(64 * sizeof(X[0]))) {
833 			len = sizeof(X)/(64 * sizeof(X[0]));
834 		}
835 
836 		data.Read(&X, len * 64);
837 
838 		// SHA hash needs 180KB blocks
839 		if (pShaHashOut) {
840 			if (nIACHPos + len*64 >= EMBLOCKSIZE) {
841 				uint32 nToComplete = EMBLOCKSIZE - nIACHPos;
842 				pHashAlg->Add(X, nToComplete);
843 				wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE );
844 				pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg.get());
845 				posCurrentEMBlock += EMBLOCKSIZE;
846 				pHashAlg->Reset();
847 				pHashAlg->Add(X+nToComplete,(len*64) - nToComplete);
848 				nIACHPos = (len*64) - nToComplete;
849 			}
850 			else{
851 				pHashAlg->Add(X, len*64);
852 				nIACHPos += len*64;
853 			}
854 		}
855 
856 		Required -= len*64;
857 	}
858 	// bytes to read
859 	Required = Length % 64;
860 	if (Required != 0){
861 		data.Read(&X,Required);
862 
863 		if (pShaHashOut != NULL){
864 			if (nIACHPos + Required >= EMBLOCKSIZE){
865 				uint32 nToComplete = EMBLOCKSIZE - nIACHPos;
866 				pHashAlg->Add(X, nToComplete);
867 				wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE );
868 				pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg.get());
869 				posCurrentEMBlock += EMBLOCKSIZE;
870 				pHashAlg->Reset();
871 				pHashAlg->Add(X+nToComplete, Required - nToComplete);
872 				nIACHPos = Required - nToComplete;
873 			}
874 			else{
875 				pHashAlg->Add(X, Required);
876 				nIACHPos += Required;
877 			}
878 		}
879 	}
880 	if (pShaHashOut != NULL){
881 		if(nIACHPos > 0){
882 			pShaHashOut->SetBlockHash(nIACHPos, posCurrentEMBlock, pHashAlg.get());
883 			posCurrentEMBlock += nIACHPos;
884 		}
885 		wxASSERT( posCurrentEMBlock == Length );
886 		wxCHECK2( pShaHashOut->ReCalculateHash(pHashAlg.get(), false), );
887 	}
888 
889 	if (Output != NULL){
890 		#ifdef __WEAK_CRYPTO__
891 			CryptoPP::Weak::MD4 md4_hasher;
892 		#else
893 			CryptoPP::MD4 md4_hasher;
894 		#endif
895 		 md4_hasher.CalculateDigest(Output->GetHash(), input, Length);
896 	}
897 }
898 
899 
GetPartHash(uint16 part) const900 const CMD4Hash& CKnownFile::GetPartHash(uint16 part) const {
901 	wxASSERT( part < m_hashlist.size() );
902 
903 	return m_hashlist[part];
904 }
905 
CreateSrcInfoPacket(const CUpDownClient * forClient,uint8 byRequestedVersion,uint16 nRequestedOptions)906 CPacket* CKnownFile::CreateSrcInfoPacket(const CUpDownClient* forClient, uint8 byRequestedVersion, uint16 nRequestedOptions)
907 {
908 	// Kad reviewed
909 
910 	if (m_ClientUploadList.empty()) {
911 		return NULL;
912 	}
913 
914 	if (((static_cast<CKnownFile*>(forClient->GetRequestFile()) != this)
915 		&& (forClient->GetUploadFile() != this)) || forClient->GetUploadFileID() != GetFileHash()) {
916 		wxString file1 = _("Unknown");
917 		if (forClient->GetRequestFile() && forClient->GetRequestFile()->GetFileName().IsOk()) {
918 			file1 = forClient->GetRequestFile()->GetFileName().GetPrintable();
919 		} else if (forClient->GetUploadFile() && forClient->GetUploadFile()->GetFileName().IsOk()) {
920 			file1 = forClient->GetUploadFile()->GetFileName().GetPrintable();
921 		}
922 		wxString file2 = _("Unknown");
923 		if (GetFileName().IsOk()) {
924 			file2 = GetFileName().GetPrintable();
925 		}
926 		AddDebugLogLineN(logKnownFiles, wxT("File mismatch on source packet (K) Sending: ") + file1 + wxT("  From: ") + file2);
927 		return NULL;
928 	}
929 
930 	const BitVector& rcvstatus = forClient->GetUpPartStatus();
931 	bool SupportsUploadChunksState = !rcvstatus.empty();
932 	//wxASSERT(rcvstatus.size() == GetPartCount()); // Obviously!
933 	if (rcvstatus.size() != GetPartCount()) {
934 		// Yuck. Same file but different part count? Seriously fucked up.
935 		AddDebugLogLineN(logKnownFiles, CFormat(wxT("Impossible situation: different partcounts for the same known file: %i (client) and %i (file)")) % rcvstatus.size() % GetPartCount());
936 		return NULL;
937 	}
938 
939 	CMemFile data(1024);
940 
941 	uint8 byUsedVersion;
942 	bool bIsSX2Packet;
943 	if (forClient->SupportsSourceExchange2() && byRequestedVersion > 0){
944 		// the client uses SourceExchange2 and requested the highest version he knows
945 		// and we send the highest version we know, but of course not higher than his request
946 		byUsedVersion = std::min(byRequestedVersion, (uint8)SOURCEEXCHANGE2_VERSION);
947 		bIsSX2Packet = true;
948 		data.WriteUInt8(byUsedVersion);
949 
950 		// we don't support any special SX2 options yet, reserved for later use
951 		if (nRequestedOptions != 0) {
952 			AddDebugLogLineN(logKnownFiles, CFormat(wxT("Client requested unknown options for SourceExchange2: %u")) % nRequestedOptions);
953 		}
954 	} else {
955 		byUsedVersion = forClient->GetSourceExchange1Version();
956 		bIsSX2Packet = false;
957 		if (forClient->SupportsSourceExchange2()) {
958 			AddDebugLogLineN(logKnownFiles, wxT("Client which announced to support SX2 sent SX1 packet instead"));
959 		}
960 	}
961 
962 	uint16 nCount = 0;
963 
964 	data.WriteHash(forClient->GetUploadFileID());
965 	data.WriteUInt16(nCount);
966 	uint32 cDbgNoSrc = 0;
967 
968 	SourceSet::iterator it = m_ClientUploadList.begin();
969 	for ( ; it != m_ClientUploadList.end(); ++it ) {
970 		const CUpDownClient *cur_src = it->GetClient();
971 
972 		if (	cur_src->HasLowID() ||
973 			cur_src == forClient ||
974 			!(	cur_src->GetUploadState() == US_UPLOADING ||
975 				cur_src->GetUploadState() == US_ONUPLOADQUEUE)) {
976 			continue;
977 		}
978 
979 		bool bNeeded = false;
980 
981 		if ( SupportsUploadChunksState ) {
982 			const BitVector& srcstatus = cur_src->GetUpPartStatus();
983 			if ( !srcstatus.empty() ) {
984 				//wxASSERT(srcstatus.size() == GetPartCount()); // Obviously!
985 				if (srcstatus.size() != GetPartCount()) {
986 					continue;
987 				}
988 				if ( cur_src->GetUpPartCount() == forClient->GetUpPartCount() ) {
989 					for (int x = 0; x < GetPartCount(); x++ ) {
990 						if ( srcstatus.get(x) && !rcvstatus.get(x) ) {
991 							// We know the receiving client needs
992 							// a chunk from this client.
993 							bNeeded = true;
994 							break;
995 						}
996 					}
997 				}
998 			} else {
999 				cDbgNoSrc++;
1000 				// This client doesn't support upload chunk status.
1001 				// So just send it and hope for the best.
1002 				bNeeded = true;
1003 			}
1004 		} else {
1005 			// remote client does not support upload chunk status,
1006 			// search sources which have at least one complete part
1007 			// we could even sort the list of sources by available
1008 			// chunks to return as much sources as possible which
1009 			// have the most available chunks. but this could be
1010 			// a noticeable performance problem.
1011 			const BitVector& srcstatus = cur_src->GetUpPartStatus();
1012 			if ( !srcstatus.empty() ) {
1013 				//wxASSERT(srcstatus.size() == GetPartCount());
1014 				if (srcstatus.size() != GetPartCount()) {
1015 					continue;
1016 				}
1017 				for (int x = 0; x < GetPartCount(); x++ ) {
1018 					if ( srcstatus.get(x) ) {
1019 						// this client has at least one chunk
1020 						bNeeded = true;
1021 						break;
1022 					}
1023 				}
1024 			} else {
1025 				// This client doesn't support upload chunk status.
1026 				// So just send it and hope for the best.
1027 				bNeeded = true;
1028 			}
1029 		}
1030 
1031 		if ( bNeeded ) {
1032 			nCount++;
1033 			uint32 dwID;
1034 			if(byUsedVersion >= 3) {
1035 				dwID = cur_src->GetUserIDHybrid();
1036 			} else {
1037 				dwID = cur_src->GetIP();
1038 			}
1039 			data.WriteUInt32(dwID);
1040 			data.WriteUInt16(cur_src->GetUserPort());
1041 			data.WriteUInt32(cur_src->GetServerIP());
1042 			data.WriteUInt16(cur_src->GetServerPort());
1043 
1044 			if (byUsedVersion >= 2) {
1045 			    data.WriteHash(cur_src->GetUserHash());
1046 			}
1047 
1048 			if (byUsedVersion >= 4){
1049 				// CryptSettings - SourceExchange V4
1050 				// 5 Reserved (!)
1051 				// 1 CryptLayer Required
1052 				// 1 CryptLayer Requested
1053 				// 1 CryptLayer Supported
1054 				const uint8 uSupportsCryptLayer	= cur_src->SupportsCryptLayer() ? 1 : 0;
1055 				const uint8 uRequestsCryptLayer	= cur_src->RequestsCryptLayer() ? 1 : 0;
1056 				const uint8 uRequiresCryptLayer	= cur_src->RequiresCryptLayer() ? 1 : 0;
1057 				const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0);
1058 				data.WriteUInt8(byCryptOptions);
1059 			}
1060 
1061 			if (nCount > 500) {
1062 				break;
1063 			}
1064 		}
1065 	}
1066 
1067 	if (!nCount) {
1068 		return 0;
1069 	}
1070 
1071 	data.Seek(bIsSX2Packet ? 17 : 16, wxFromStart);
1072 	data.WriteUInt16(nCount);
1073 
1074 	CPacket* result = new CPacket(data, OP_EMULEPROT, bIsSX2Packet ? OP_ANSWERSOURCES2 : OP_ANSWERSOURCES);
1075 
1076 	if ( result->GetPacketSize() > 354 ) {
1077 		result->PackPacket();
1078 	}
1079 
1080 	return result;
1081 }
1082 
1083 
CreateOfferedFilePacket(CMemFile * files,CServer * pServer,CUpDownClient * pClient)1084 void CKnownFile::CreateOfferedFilePacket(
1085 	CMemFile *files,
1086 	CServer *pServer,
1087 	CUpDownClient *pClient) {
1088 
1089 	// This function is used for offering files to the local server and for sending
1090 	// shared files to some other client. In each case we send our IP+Port only, if
1091 	// we have a HighID.
1092 
1093 	wxASSERT(!(pClient && pServer));
1094 
1095 	SetPublishedED2K(true);
1096 	files->WriteHash(GetFileHash());
1097 
1098 	uint32 nClientID = 0;
1099 	uint16 nClientPort = 0;
1100 
1101 	if (pServer) {
1102 		if (pServer->GetTCPFlags() & SRV_TCPFLG_COMPRESSION) {
1103 			#define FILE_COMPLETE_ID		0xfbfbfbfb
1104 			#define FILE_COMPLETE_PORT	0xfbfb
1105 			#define FILE_INCOMPLETE_ID	0xfcfcfcfc
1106 			#define FILE_INCOMPLETE_PORT	0xfcfc
1107 			// complete   file: ip 251.251.251 (0xfbfbfbfb) port 0xfbfb
1108 			// incomplete file: op 252.252.252 (0xfcfcfcfc) port 0xfcfc
1109 			if (GetStatus() == PS_COMPLETE) {
1110 				nClientID = FILE_COMPLETE_ID;
1111 				nClientPort = FILE_COMPLETE_PORT;
1112 			} else {
1113 				nClientID = FILE_INCOMPLETE_ID;
1114 				nClientPort = FILE_INCOMPLETE_PORT;
1115 			}
1116 		} else {
1117 			if (theApp->IsConnectedED2K() && !::IsLowID(theApp->GetED2KID())){
1118 				nClientID = theApp->GetID();
1119 				nClientPort = thePrefs::GetPort();
1120 			}
1121 		}
1122 	} else {
1123 		// Do not merge this with the above case - this one
1124 		// also checks Kad status.
1125 		if (theApp->IsConnected() && !theApp->IsFirewalled()) {
1126 			nClientID = theApp->GetID();
1127 			nClientPort = thePrefs::GetPort();
1128 		}
1129 	}
1130 
1131 	files->WriteUInt32(nClientID);
1132 	files->WriteUInt16(nClientPort);
1133 
1134 	TagPtrList tags;
1135 
1136 	// The printable filename is used because it's destined for another user.
1137 	tags.push_back(new CTagString(FT_FILENAME, GetFileName().GetPrintable()));
1138 
1139 	if (pClient && pClient->GetVBTTags()) {
1140 		tags.push_back(new CTagVarInt(FT_FILESIZE, GetFileSize()));
1141 	} else {
1142 		if (!IsLargeFile()){
1143 			tags.push_back(new CTagInt32(FT_FILESIZE, GetFileSize()));
1144 		} else {
1145 			// Large file
1146 			// we send 2*32 bit tags to servers, but a real 64 bit tag to other clients.
1147 			if (pServer) {
1148 				if (!pServer->SupportsLargeFilesTCP()){
1149 					wxFAIL;
1150 					tags.push_back(new CTagInt32(FT_FILESIZE, 0));
1151 				} else {
1152 					tags.push_back(new CTagInt32(FT_FILESIZE, (uint32)GetFileSize()));
1153 					tags.push_back(new CTagInt32(FT_FILESIZE_HI, (uint32)(GetFileSize() >> 32)));
1154 				}
1155 			} else {
1156 				if (!pClient->SupportsLargeFiles()) {
1157 					wxFAIL;
1158 					tags.push_back(new CTagInt32(FT_FILESIZE, 0));
1159 				} else {
1160 					tags.push_back(new CTagInt64(FT_FILESIZE, GetFileSize()));
1161 				}
1162 			}
1163 		}
1164 	}
1165 
1166 	if (GetFileRating()) {
1167 		tags.push_back(new CTagVarInt(FT_FILERATING, GetFileRating(), (pClient && pClient->GetVBTTags()) ? 0 : 32));
1168 	}
1169 
1170 	// NOTE: Archives and CD-Images are published+searched with file type "Pro"
1171 	bool bAddedFileType = false;
1172 	if (pServer && (pServer->GetTCPFlags() & SRV_TCPFLG_TYPETAGINTEGER)) {
1173 		// Send integer file type tags to newer servers
1174 		EED2KFileType eFileType = GetED2KFileTypeSearchID(GetED2KFileTypeID(GetFileName()));
1175 		if (eFileType >= ED2KFT_AUDIO && eFileType <= ED2KFT_CDIMAGE) {
1176 			tags.push_back(new CTagInt32(FT_FILETYPE, eFileType));
1177 			bAddedFileType = true;
1178 		}
1179 	}
1180 	if (!bAddedFileType) {
1181 		// Send string file type tags to:
1182 		//	- newer servers, in case there is no integer type available for the file type (e.g. emulecollection)
1183 		//	- older servers
1184 		//	- all clients
1185 		wxString strED2KFileType(GetED2KFileTypeSearchTerm(GetED2KFileTypeID(GetFileName())));
1186 		if (!strED2KFileType.IsEmpty()) {
1187 			tags.push_back(new CTagString(FT_FILETYPE, strED2KFileType));
1188 		}
1189 	}
1190 
1191 	// There, we could add MetaData info, if we ever get to have that.
1192 
1193 	EUtf8Str eStrEncode;
1194 
1195 	bool unicode_support =
1196 		// eservers that support UNICODE.
1197 		(pServer && (pServer->GetUnicodeSupport()))
1198 		||
1199 		// clients that support unicode
1200 		(pClient && pClient->GetUnicodeSupport());
1201 	eStrEncode = unicode_support ? utf8strRaw : utf8strNone;
1202 
1203 	files->WriteUInt32(tags.size());
1204 
1205 	// Sadly, eMule doesn't use a MISCOPTIONS flag on hello packet for this, so we
1206 	// have to identify the support for new tags by version.
1207 	bool new_ed2k =
1208 		// eMule client > 0.42f
1209 		(pClient && pClient->IsEmuleClient() && pClient->GetVersion()  >= MAKE_CLIENT_VERSION(0,42,7))
1210 		||
1211 		// aMule >= 2.0.0rc8. Sadly, there's no way to check the rcN number, so I checked
1212 		// the rc8 changelog. On rc8 OSInfo was introduced, so...
1213 		(pClient && pClient->GetClientSoft() == SO_AMULE && !pClient->GetClientOSInfo().IsEmpty())
1214 		||
1215 		// eservers use a flag for this, at least.
1216 		(pServer && (pServer->GetTCPFlags() & SRV_TCPFLG_NEWTAGS));
1217 
1218 	for (TagPtrList::iterator it = tags.begin(); it != tags.end(); ++it ) {
1219 		CTag* pTag = *it;
1220 		if (new_ed2k) {
1221 			pTag->WriteNewEd2kTag(files, eStrEncode);
1222 		} else {
1223 			pTag->WriteTagToFile(files, eStrEncode);
1224 		}
1225 		delete pTag;
1226 	}
1227 }
1228 
1229 
1230 // Updates priority of file if autopriority is activated
UpdateAutoUpPriority()1231 void CKnownFile::UpdateAutoUpPriority()
1232 {
1233 	if (IsAutoUpPriority()) {
1234 		uint32 queued = GetQueuedCount();
1235 		uint8 priority = PR_NORMAL;
1236 
1237 		if (queued > 20) {
1238 			priority = PR_LOW;
1239 		} else if (queued > 1) {
1240 			priority = PR_NORMAL;
1241 		} else {
1242 			priority = PR_HIGH;
1243 		}
1244 
1245 		if (GetUpPriority() != priority) {
1246 			SetUpPriority(priority, false);
1247 			Notify_SharedFilesUpdateItem(this);
1248 		}
1249 	}
1250 }
1251 
SetFileCommentRating(const wxString & strNewComment,int8 iNewRating)1252 void CKnownFile::SetFileCommentRating(const wxString& strNewComment, int8 iNewRating)
1253 {
1254 	if (m_strComment != strNewComment || m_iRating != iNewRating) {
1255 		SetLastPublishTimeKadNotes(0);
1256 		wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1257 
1258 		wxConfigBase* cfg = wxConfigBase::Get();
1259 		if (strNewComment.IsEmpty() && iNewRating == 0) {
1260 			cfg->DeleteGroup(strCfgPath);
1261 		} else {
1262 			cfg->Write( strCfgPath + wxT("Comment"), strNewComment);
1263 			cfg->Write( strCfgPath + wxT("Rate"), (int)iNewRating);
1264 		}
1265 
1266 		m_strComment = strNewComment;
1267 		m_iRating = iNewRating;
1268 
1269 		SourceSet::iterator it = m_ClientUploadList.begin();
1270 		for ( ; it != m_ClientUploadList.end(); ++it ) {
1271 			it->SetCommentDirty();
1272 		}
1273 	}
1274 }
1275 
1276 
SetUpPriority(uint8 iNewUpPriority,bool m_bsave)1277 void CKnownFile::SetUpPriority(uint8 iNewUpPriority, bool m_bsave){
1278 	m_iUpPriority = iNewUpPriority;
1279 	if( IsPartFile() && m_bsave ) {
1280 		static_cast<CPartFile*>(this)->SavePartFile();
1281 	}
1282 }
1283 
SetPublishedED2K(bool val)1284 void CKnownFile::SetPublishedED2K(bool val){
1285 	m_PublishedED2K = val;
1286 	Notify_SharedFilesUpdateItem(this);
1287 }
1288 
PublishNotes()1289 bool CKnownFile::PublishNotes()
1290 {
1291 	if(m_lastPublishTimeKadNotes > (uint32)time(NULL)) {
1292 		return false;
1293 	}
1294 
1295 	if(!GetFileComment().IsEmpty()) {
1296 		m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1297 		return true;
1298 	}
1299 
1300 	if(GetFileRating() != 0) {
1301 		m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1302 		return true;
1303 	}
1304 
1305 	return false;
1306 }
1307 
PublishSrc()1308 bool CKnownFile::PublishSrc()
1309 {
1310 	uint32 lastBuddyIP = 0;
1311 
1312 	if( theApp->IsFirewalled() ) {
1313 		CUpDownClient* buddy = theApp->clientlist->GetBuddy();
1314 		if( buddy ) {
1315 			lastBuddyIP = theApp->clientlist->GetBuddy()->GetIP();
1316 			if( lastBuddyIP != m_lastBuddyIP ) {
1317 				SetLastPublishTimeKadSrc( (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES, lastBuddyIP );
1318 				return true;
1319 			}
1320 		} else {
1321 			return false;
1322 		}
1323 	}
1324 
1325 	if(m_lastPublishTimeKadSrc > (uint32)time(NULL)) {
1326 		return false;
1327 	}
1328 
1329 	SetLastPublishTimeKadSrc((uint32)time(NULL)+KADEMLIAREPUBLISHTIMES,lastBuddyIP);
1330 	return true;
1331 
1332 }
1333 
UpdatePartsInfo()1334 void CKnownFile::UpdatePartsInfo()
1335 {
1336 	// Cache part count
1337 	uint16 partcount = GetPartCount();
1338 	bool flag = (time(NULL) - m_nCompleteSourcesTime > 0);
1339 
1340 	// Ensure the frequency-list is ready
1341 	if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1342 		m_AvailPartFrequency.clear();
1343 		m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1344 	}
1345 
1346 	if (flag) {
1347 		ArrayOfUInts16 count;
1348 		count.reserve(m_ClientUploadList.size());
1349 
1350 		SourceSet::iterator it = m_ClientUploadList.begin();
1351 		for ( ; it != m_ClientUploadList.end(); ++it ) {
1352 			CUpDownClient* client = it->GetClient();
1353 			if ( !client->GetUpPartStatus().empty() && client->GetUpPartCount() == partcount ) {
1354 				count.push_back(client->GetUpCompleteSourcesCount());
1355 			}
1356 		}
1357 
1358 		m_nCompleteSourcesCount = m_nCompleteSourcesCountLo = m_nCompleteSourcesCountHi = 0;
1359 
1360 		if( partcount > 0) {
1361 			m_nCompleteSourcesCount = m_AvailPartFrequency[0];
1362 		}
1363 		for (uint16 i = 1; i < partcount; ++i) {
1364 			if( m_nCompleteSourcesCount > m_AvailPartFrequency[i]) {
1365 				m_nCompleteSourcesCount = m_AvailPartFrequency[i];
1366 			}
1367 		}
1368 		count.push_back(m_nCompleteSourcesCount);
1369 
1370 		int32 n = count.size();
1371 		if (n > 0) {
1372 			std::sort(count.begin(), count.end(), std::less<uint16>());
1373 
1374 			// calculate range
1375 			int i = n >> 1;			// (n / 2)
1376 			int j = (n * 3) >> 2;	// (n * 3) / 4
1377 			int k = (n * 7) >> 3;	// (n * 7) / 8
1378 
1379 			// For complete files, trust the people your uploading to more...
1380 
1381 			// For low guess and normal guess count
1382 			//	- If we see more sources then the guessed low and
1383 			//	normal, use what we see.
1384 			//	- If we see less sources then the guessed low,
1385 			//	adjust network accounts for 100%, we account for
1386 			//	0% with what we see and make sure we are still
1387 			//	above the normal.
1388 			// For high guess
1389 			//	Adjust 100% network and 0% what we see.
1390 			if (n < 20) {
1391 				if ( count[i] < m_nCompleteSourcesCount ) {
1392 					m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1393 				} else {
1394 					m_nCompleteSourcesCountLo = count[i];
1395 				}
1396 				m_nCompleteSourcesCount= m_nCompleteSourcesCountLo;
1397 				m_nCompleteSourcesCountHi = count[j];
1398 				if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1399 					m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1400 				}
1401 			} else {
1402 			// Many sources..
1403 			// For low guess
1404 			//	Use what we see.
1405 			// For normal guess
1406 			//	Adjust network accounts for 100%, we account for
1407 			//	0% with what we see and make sure we are still above the low.
1408 			// For high guess
1409 			//	Adjust network accounts for 100%, we account for 0%
1410 			//	with what we see and make sure we are still above the normal.
1411 
1412 				m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1413 				m_nCompleteSourcesCount = count[j];
1414 				if( m_nCompleteSourcesCount < m_nCompleteSourcesCountLo ) {
1415 					m_nCompleteSourcesCount = m_nCompleteSourcesCountLo;
1416 				}
1417 				m_nCompleteSourcesCountHi= count[k];
1418 				if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1419 					m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1420 				}
1421 			}
1422 		}
1423 		m_nCompleteSourcesTime = time(NULL) + (60);
1424 	}
1425 
1426 	Notify_SharedFilesUpdateItem(this);
1427 }
1428 
1429 
UpdateUpPartsFrequency(CUpDownClient * client,bool increment)1430 void CKnownFile::UpdateUpPartsFrequency( CUpDownClient* client, bool increment )
1431 {
1432 	if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1433 		m_AvailPartFrequency.clear();
1434 		m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1435 		if ( !increment ) {
1436 			return;
1437 		}
1438 	}
1439 
1440 	const BitVector& freq = client->GetUpPartStatus();
1441 	unsigned int size = freq.size();
1442 	if ( size != m_AvailPartFrequency.size() ) {
1443 		return;
1444 	}
1445 
1446 	if ( increment ) {
1447 		for ( unsigned int i = 0; i < size; ++i ) {
1448 			if ( freq.get(i) ) {
1449 				m_AvailPartFrequency[i]++;
1450 			}
1451 		}
1452 	} else {
1453 		for ( unsigned int i = 0; i < size; ++i ) {
1454 			if ( freq.get(i) ) {
1455 				m_AvailPartFrequency[i]--;
1456 			}
1457 		}
1458 	}
1459 }
1460 
ClearPriority()1461 void CKnownFile::ClearPriority() {
1462 	if ( !m_bAutoUpPriority ) return;
1463 	m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
1464 	UpdateAutoUpPriority();
1465 }
1466 
GuessAndRemoveExt(CPath & name)1467 static void GuessAndRemoveExt(CPath& name)
1468 {
1469 	wxString ext = name.GetExt();
1470 
1471 	// Remove common two-part extensions, such as "tar.gz"
1472 	if (ext == wxT("gz") || ext == wxT("bz2")) {
1473 		name = name.RemoveExt();
1474 		if (name.GetExt() == wxT("tar")) {
1475 			name = name.RemoveExt();
1476 		}
1477 	// might be an extension if length == 3
1478 	// and also remove some common non-three-character extensions
1479 	} else if (ext.Length() == 3  ||
1480 		   ext == wxT("7z")   ||
1481 		   ext == wxT("rm")   ||
1482 		   ext == wxT("jpeg") ||
1483 		   ext == wxT("mpeg")
1484 		   ) {
1485 		name = name.RemoveExt();
1486 	}
1487 }
1488 
SetFileName(const CPath & filename)1489 void CKnownFile::SetFileName(const CPath& filename)
1490 {
1491 	CAbstractFile::SetFileName(filename);
1492 	wordlist.clear();
1493 	// Don't publish extension. That'd kill the node indexing e.g. "avi".
1494 	CPath tmpName = GetFileName();
1495 	GuessAndRemoveExt(tmpName);
1496 	Kademlia::CSearchManager::GetWords(tmpName.GetPrintable(), &wordlist);
1497 }
1498 
1499 #endif // CLIENT_GUI
1500 
1501 //For File Comment //
LoadComment() const1502 void CKnownFile::LoadComment() const
1503 {
1504 	#ifndef CLIENT_GUI
1505 	wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1506 
1507 	wxConfigBase* cfg = wxConfigBase::Get();
1508 
1509 	m_strComment = cfg->Read( strCfgPath + wxT("Comment"), wxEmptyString);
1510 	m_iRating = cfg->Read( strCfgPath + wxT("Rate"), 0l);
1511 	#endif
1512 
1513 	m_bCommentLoaded = true;
1514 }
1515 
1516 
GetAICHMasterHash() const1517 wxString CKnownFile::GetAICHMasterHash() const
1518 {
1519 #ifdef CLIENT_GUI
1520 	return m_AICHMasterHash;
1521 #else
1522 	if (HasProperAICHHashSet()) {
1523 		return m_pAICHHashSet->GetMasterHash().GetString();
1524 	}
1525 
1526 	return wxEmptyString;
1527 #endif
1528 }
1529 
1530 
HasProperAICHHashSet() const1531 bool CKnownFile::HasProperAICHHashSet() const
1532 {
1533 #ifdef CLIENT_GUI
1534 	return m_AICHMasterHash.Length() != 0;
1535 #else
1536 	return m_pAICHHashSet->HasValidMasterHash() &&
1537 		(m_pAICHHashSet->GetStatus() == AICH_HASHSETCOMPLETE ||
1538 		 m_pAICHHashSet->GetStatus() == AICH_VERIFIED);
1539 #endif
1540 }
1541 
GetFeedback() const1542 wxString CKnownFile::GetFeedback() const
1543 {
1544 	return	  wxString(_("File name")) + wxT(": ") + GetFileName().GetPrintable() + wxT("\n")
1545 		+ _("File size") + wxT(": ") + CastItoXBytes(GetFileSize()) + wxT("\n")
1546 		+ _("Share ratio") + CFormat(wxT(": %.2f%%\n")) % (((double)statistic.GetAllTimeTransferred() / (double)GetFileSize()) * 100.0)
1547 		+ _("Uploaded") + wxT(": ") + CastItoXBytes(statistic.GetTransferred()) + wxT(" (") + CastItoXBytes(statistic.GetAllTimeTransferred()) + wxT(")\n")
1548 		+ _("Requested") + CFormat(wxT(": %u (%u)\n")) % statistic.GetRequests() % statistic.GetAllTimeRequests()
1549 		+ _("Accepted") + CFormat(wxT(": %u (%u)\n")) % statistic.GetAccepts() % statistic.GetAllTimeAccepts()
1550 		+ _("On Queue") + CFormat(wxT(": %u\n")) % GetQueuedCount()
1551 		+ _("Complete sources") + CFormat(wxT(": %u\n")) % m_nCompleteSourcesCount;
1552 }
1553 
1554 // File_checked_for_headers
1555