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<uint8_t> buffer(hashes.size() * MD4HASH_LENGTH);
792 	std::vector<uint8_t>::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 uint8_t * input,uint32 Length,CMD4Hash * Output,CAICHHashTree * pShaHashOut)814 void CKnownFile::CreateHashFromInput(const uint8_t* 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 	uint8  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 		CryptoPP::Weak::MD4 md4_hasher;
891 		md4_hasher.CalculateDigest(Output->GetHash(), input, Length);
892 	}
893 }
894 
895 
GetPartHash(uint16 part) const896 const CMD4Hash& CKnownFile::GetPartHash(uint16 part) const {
897 	wxASSERT( part < m_hashlist.size() );
898 
899 	return m_hashlist[part];
900 }
901 
CreateSrcInfoPacket(const CUpDownClient * forClient,uint8 byRequestedVersion,uint16 nRequestedOptions)902 CPacket* CKnownFile::CreateSrcInfoPacket(const CUpDownClient* forClient, uint8 byRequestedVersion, uint16 nRequestedOptions)
903 {
904 	// Kad reviewed
905 
906 	if (m_ClientUploadList.empty()) {
907 		return NULL;
908 	}
909 
910 	if (((static_cast<CKnownFile*>(forClient->GetRequestFile()) != this)
911 		&& (forClient->GetUploadFile() != this)) || forClient->GetUploadFileID() != GetFileHash()) {
912 		wxString file1 = _("Unknown");
913 		if (forClient->GetRequestFile() && forClient->GetRequestFile()->GetFileName().IsOk()) {
914 			file1 = forClient->GetRequestFile()->GetFileName().GetPrintable();
915 		} else if (forClient->GetUploadFile() && forClient->GetUploadFile()->GetFileName().IsOk()) {
916 			file1 = forClient->GetUploadFile()->GetFileName().GetPrintable();
917 		}
918 		wxString file2 = _("Unknown");
919 		if (GetFileName().IsOk()) {
920 			file2 = GetFileName().GetPrintable();
921 		}
922 		AddDebugLogLineN(logKnownFiles, wxT("File mismatch on source packet (K) Sending: ") + file1 + wxT("  From: ") + file2);
923 		return NULL;
924 	}
925 
926 	const BitVector& rcvstatus = forClient->GetUpPartStatus();
927 	bool SupportsUploadChunksState = !rcvstatus.empty();
928 	//wxASSERT(rcvstatus.size() == GetPartCount()); // Obviously!
929 	if (rcvstatus.size() != GetPartCount()) {
930 		// Yuck. Same file but different part count? Seriously fucked up.
931 		AddDebugLogLineN(logKnownFiles, CFormat(wxT("Impossible situation: different partcounts for the same known file: %i (client) and %i (file)")) % rcvstatus.size() % GetPartCount());
932 		return NULL;
933 	}
934 
935 	CMemFile data(1024);
936 
937 	uint8 byUsedVersion;
938 	bool bIsSX2Packet;
939 	if (forClient->SupportsSourceExchange2() && byRequestedVersion > 0){
940 		// the client uses SourceExchange2 and requested the highest version he knows
941 		// and we send the highest version we know, but of course not higher than his request
942 		byUsedVersion = std::min(byRequestedVersion, (uint8)SOURCEEXCHANGE2_VERSION);
943 		bIsSX2Packet = true;
944 		data.WriteUInt8(byUsedVersion);
945 
946 		// we don't support any special SX2 options yet, reserved for later use
947 		if (nRequestedOptions != 0) {
948 			AddDebugLogLineN(logKnownFiles, CFormat(wxT("Client requested unknown options for SourceExchange2: %u")) % nRequestedOptions);
949 		}
950 	} else {
951 		byUsedVersion = forClient->GetSourceExchange1Version();
952 		bIsSX2Packet = false;
953 		if (forClient->SupportsSourceExchange2()) {
954 			AddDebugLogLineN(logKnownFiles, wxT("Client which announced to support SX2 sent SX1 packet instead"));
955 		}
956 	}
957 
958 	uint16 nCount = 0;
959 
960 	data.WriteHash(forClient->GetUploadFileID());
961 	data.WriteUInt16(nCount);
962 	uint32 cDbgNoSrc = 0;
963 
964 	SourceSet::iterator it = m_ClientUploadList.begin();
965 	for ( ; it != m_ClientUploadList.end(); ++it ) {
966 		const CUpDownClient *cur_src = it->GetClient();
967 
968 		if (	cur_src->HasLowID() ||
969 			cur_src == forClient ||
970 			!(	cur_src->GetUploadState() == US_UPLOADING ||
971 				cur_src->GetUploadState() == US_ONUPLOADQUEUE)) {
972 			continue;
973 		}
974 
975 		bool bNeeded = false;
976 
977 		if ( SupportsUploadChunksState ) {
978 			const BitVector& srcstatus = cur_src->GetUpPartStatus();
979 			if ( !srcstatus.empty() ) {
980 				//wxASSERT(srcstatus.size() == GetPartCount()); // Obviously!
981 				if (srcstatus.size() != GetPartCount()) {
982 					continue;
983 				}
984 				if ( cur_src->GetUpPartCount() == forClient->GetUpPartCount() ) {
985 					for (int x = 0; x < GetPartCount(); x++ ) {
986 						if ( srcstatus.get(x) && !rcvstatus.get(x) ) {
987 							// We know the receiving client needs
988 							// a chunk from this client.
989 							bNeeded = true;
990 							break;
991 						}
992 					}
993 				}
994 			} else {
995 				cDbgNoSrc++;
996 				// This client doesn't support upload chunk status.
997 				// So just send it and hope for the best.
998 				bNeeded = true;
999 			}
1000 		} else {
1001 			// remote client does not support upload chunk status,
1002 			// search sources which have at least one complete part
1003 			// we could even sort the list of sources by available
1004 			// chunks to return as much sources as possible which
1005 			// have the most available chunks. but this could be
1006 			// a noticeable performance problem.
1007 			const BitVector& srcstatus = cur_src->GetUpPartStatus();
1008 			if ( !srcstatus.empty() ) {
1009 				//wxASSERT(srcstatus.size() == GetPartCount());
1010 				if (srcstatus.size() != GetPartCount()) {
1011 					continue;
1012 				}
1013 				for (int x = 0; x < GetPartCount(); x++ ) {
1014 					if ( srcstatus.get(x) ) {
1015 						// this client has at least one chunk
1016 						bNeeded = true;
1017 						break;
1018 					}
1019 				}
1020 			} else {
1021 				// This client doesn't support upload chunk status.
1022 				// So just send it and hope for the best.
1023 				bNeeded = true;
1024 			}
1025 		}
1026 
1027 		if ( bNeeded ) {
1028 			nCount++;
1029 			uint32 dwID;
1030 			if(byUsedVersion >= 3) {
1031 				dwID = cur_src->GetUserIDHybrid();
1032 			} else {
1033 				dwID = cur_src->GetIP();
1034 			}
1035 			data.WriteUInt32(dwID);
1036 			data.WriteUInt16(cur_src->GetUserPort());
1037 			data.WriteUInt32(cur_src->GetServerIP());
1038 			data.WriteUInt16(cur_src->GetServerPort());
1039 
1040 			if (byUsedVersion >= 2) {
1041 			    data.WriteHash(cur_src->GetUserHash());
1042 			}
1043 
1044 			if (byUsedVersion >= 4){
1045 				// CryptSettings - SourceExchange V4
1046 				// 5 Reserved (!)
1047 				// 1 CryptLayer Required
1048 				// 1 CryptLayer Requested
1049 				// 1 CryptLayer Supported
1050 				const uint8 uSupportsCryptLayer	= cur_src->SupportsCryptLayer() ? 1 : 0;
1051 				const uint8 uRequestsCryptLayer	= cur_src->RequestsCryptLayer() ? 1 : 0;
1052 				const uint8 uRequiresCryptLayer	= cur_src->RequiresCryptLayer() ? 1 : 0;
1053 				const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0);
1054 				data.WriteUInt8(byCryptOptions);
1055 			}
1056 
1057 			if (nCount > 500) {
1058 				break;
1059 			}
1060 		}
1061 	}
1062 
1063 	if (!nCount) {
1064 		return 0;
1065 	}
1066 
1067 	data.Seek(bIsSX2Packet ? 17 : 16, wxFromStart);
1068 	data.WriteUInt16(nCount);
1069 
1070 	CPacket* result = new CPacket(data, OP_EMULEPROT, bIsSX2Packet ? OP_ANSWERSOURCES2 : OP_ANSWERSOURCES);
1071 
1072 	if ( result->GetPacketSize() > 354 ) {
1073 		result->PackPacket();
1074 	}
1075 
1076 	return result;
1077 }
1078 
1079 
CreateOfferedFilePacket(CMemFile * files,CServer * pServer,CUpDownClient * pClient)1080 void CKnownFile::CreateOfferedFilePacket(
1081 	CMemFile *files,
1082 	CServer *pServer,
1083 	CUpDownClient *pClient) {
1084 
1085 	// This function is used for offering files to the local server and for sending
1086 	// shared files to some other client. In each case we send our IP+Port only, if
1087 	// we have a HighID.
1088 
1089 	wxASSERT(!(pClient && pServer));
1090 
1091 	SetPublishedED2K(true);
1092 	files->WriteHash(GetFileHash());
1093 
1094 	uint32 nClientID = 0;
1095 	uint16 nClientPort = 0;
1096 
1097 	if (pServer) {
1098 		if (pServer->GetTCPFlags() & SRV_TCPFLG_COMPRESSION) {
1099 			#define FILE_COMPLETE_ID		0xfbfbfbfb
1100 			#define FILE_COMPLETE_PORT	0xfbfb
1101 			#define FILE_INCOMPLETE_ID	0xfcfcfcfc
1102 			#define FILE_INCOMPLETE_PORT	0xfcfc
1103 			// complete   file: ip 251.251.251 (0xfbfbfbfb) port 0xfbfb
1104 			// incomplete file: op 252.252.252 (0xfcfcfcfc) port 0xfcfc
1105 			if (GetStatus() == PS_COMPLETE) {
1106 				nClientID = FILE_COMPLETE_ID;
1107 				nClientPort = FILE_COMPLETE_PORT;
1108 			} else {
1109 				nClientID = FILE_INCOMPLETE_ID;
1110 				nClientPort = FILE_INCOMPLETE_PORT;
1111 			}
1112 		} else {
1113 			if (theApp->IsConnectedED2K() && !::IsLowID(theApp->GetED2KID())){
1114 				nClientID = theApp->GetID();
1115 				nClientPort = thePrefs::GetPort();
1116 			}
1117 		}
1118 	} else {
1119 		// Do not merge this with the above case - this one
1120 		// also checks Kad status.
1121 		if (theApp->IsConnected() && !theApp->IsFirewalled()) {
1122 			nClientID = theApp->GetID();
1123 			nClientPort = thePrefs::GetPort();
1124 		}
1125 	}
1126 
1127 	files->WriteUInt32(nClientID);
1128 	files->WriteUInt16(nClientPort);
1129 
1130 	TagPtrList tags;
1131 
1132 	// The printable filename is used because it's destined for another user.
1133 	tags.push_back(new CTagString(FT_FILENAME, GetFileName().GetPrintable()));
1134 
1135 	if (pClient && pClient->GetVBTTags()) {
1136 		tags.push_back(new CTagVarInt(FT_FILESIZE, GetFileSize()));
1137 	} else {
1138 		if (!IsLargeFile()){
1139 			tags.push_back(new CTagInt32(FT_FILESIZE, GetFileSize()));
1140 		} else {
1141 			// Large file
1142 			// we send 2*32 bit tags to servers, but a real 64 bit tag to other clients.
1143 			if (pServer) {
1144 				if (!pServer->SupportsLargeFilesTCP()){
1145 					wxFAIL;
1146 					tags.push_back(new CTagInt32(FT_FILESIZE, 0));
1147 				} else {
1148 					tags.push_back(new CTagInt32(FT_FILESIZE, (uint32)GetFileSize()));
1149 					tags.push_back(new CTagInt32(FT_FILESIZE_HI, (uint32)(GetFileSize() >> 32)));
1150 				}
1151 			} else {
1152 				if (!pClient->SupportsLargeFiles()) {
1153 					wxFAIL;
1154 					tags.push_back(new CTagInt32(FT_FILESIZE, 0));
1155 				} else {
1156 					tags.push_back(new CTagInt64(FT_FILESIZE, GetFileSize()));
1157 				}
1158 			}
1159 		}
1160 	}
1161 
1162 	if (GetFileRating()) {
1163 		tags.push_back(new CTagVarInt(FT_FILERATING, GetFileRating(), (pClient && pClient->GetVBTTags()) ? 0 : 32));
1164 	}
1165 
1166 	// NOTE: Archives and CD-Images are published+searched with file type "Pro"
1167 	bool bAddedFileType = false;
1168 	if (pServer && (pServer->GetTCPFlags() & SRV_TCPFLG_TYPETAGINTEGER)) {
1169 		// Send integer file type tags to newer servers
1170 		EED2KFileType eFileType = GetED2KFileTypeSearchID(GetED2KFileTypeID(GetFileName()));
1171 		if (eFileType >= ED2KFT_AUDIO && eFileType <= ED2KFT_CDIMAGE) {
1172 			tags.push_back(new CTagInt32(FT_FILETYPE, eFileType));
1173 			bAddedFileType = true;
1174 		}
1175 	}
1176 	if (!bAddedFileType) {
1177 		// Send string file type tags to:
1178 		//	- newer servers, in case there is no integer type available for the file type (e.g. emulecollection)
1179 		//	- older servers
1180 		//	- all clients
1181 		wxString strED2KFileType(GetED2KFileTypeSearchTerm(GetED2KFileTypeID(GetFileName())));
1182 		if (!strED2KFileType.IsEmpty()) {
1183 			tags.push_back(new CTagString(FT_FILETYPE, strED2KFileType));
1184 		}
1185 	}
1186 
1187 	// There, we could add MetaData info, if we ever get to have that.
1188 
1189 	EUtf8Str eStrEncode;
1190 
1191 	bool unicode_support =
1192 		// eservers that support UNICODE.
1193 		(pServer && (pServer->GetUnicodeSupport()))
1194 		||
1195 		// clients that support unicode
1196 		(pClient && pClient->GetUnicodeSupport());
1197 	eStrEncode = unicode_support ? utf8strRaw : utf8strNone;
1198 
1199 	files->WriteUInt32(tags.size());
1200 
1201 	// Sadly, eMule doesn't use a MISCOPTIONS flag on hello packet for this, so we
1202 	// have to identify the support for new tags by version.
1203 	bool new_ed2k =
1204 		// eMule client > 0.42f
1205 		(pClient && pClient->IsEmuleClient() && pClient->GetVersion()  >= MAKE_CLIENT_VERSION(0,42,7))
1206 		||
1207 		// aMule >= 2.0.0rc8. Sadly, there's no way to check the rcN number, so I checked
1208 		// the rc8 changelog. On rc8 OSInfo was introduced, so...
1209 		(pClient && pClient->GetClientSoft() == SO_AMULE && !pClient->GetClientOSInfo().IsEmpty())
1210 		||
1211 		// eservers use a flag for this, at least.
1212 		(pServer && (pServer->GetTCPFlags() & SRV_TCPFLG_NEWTAGS));
1213 
1214 	for (TagPtrList::iterator it = tags.begin(); it != tags.end(); ++it ) {
1215 		CTag* pTag = *it;
1216 		if (new_ed2k) {
1217 			pTag->WriteNewEd2kTag(files, eStrEncode);
1218 		} else {
1219 			pTag->WriteTagToFile(files, eStrEncode);
1220 		}
1221 		delete pTag;
1222 	}
1223 }
1224 
1225 
1226 // Updates priority of file if autopriority is activated
UpdateAutoUpPriority()1227 void CKnownFile::UpdateAutoUpPriority()
1228 {
1229 	if (IsAutoUpPriority()) {
1230 		uint32 queued = GetQueuedCount();
1231 		uint8 priority = PR_NORMAL;
1232 
1233 		if (queued > 20) {
1234 			priority = PR_LOW;
1235 		} else if (queued > 1) {
1236 			priority = PR_NORMAL;
1237 		} else {
1238 			priority = PR_HIGH;
1239 		}
1240 
1241 		if (GetUpPriority() != priority) {
1242 			SetUpPriority(priority, false);
1243 			Notify_SharedFilesUpdateItem(this);
1244 		}
1245 	}
1246 }
1247 
SetFileCommentRating(const wxString & strNewComment,int8 iNewRating)1248 void CKnownFile::SetFileCommentRating(const wxString& strNewComment, int8 iNewRating)
1249 {
1250 	if (m_strComment != strNewComment || m_iRating != iNewRating) {
1251 		SetLastPublishTimeKadNotes(0);
1252 		wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1253 
1254 		wxConfigBase* cfg = wxConfigBase::Get();
1255 		if (strNewComment.IsEmpty() && iNewRating == 0) {
1256 			cfg->DeleteGroup(strCfgPath);
1257 		} else {
1258 			cfg->Write( strCfgPath + wxT("Comment"), strNewComment);
1259 			cfg->Write( strCfgPath + wxT("Rate"), (int)iNewRating);
1260 		}
1261 
1262 		m_strComment = strNewComment;
1263 		m_iRating = iNewRating;
1264 
1265 		SourceSet::iterator it = m_ClientUploadList.begin();
1266 		for ( ; it != m_ClientUploadList.end(); ++it ) {
1267 			it->SetCommentDirty();
1268 		}
1269 	}
1270 }
1271 
1272 
SetUpPriority(uint8 iNewUpPriority,bool m_bsave)1273 void CKnownFile::SetUpPriority(uint8 iNewUpPriority, bool m_bsave){
1274 	m_iUpPriority = iNewUpPriority;
1275 	if( IsPartFile() && m_bsave ) {
1276 		static_cast<CPartFile*>(this)->SavePartFile();
1277 	}
1278 }
1279 
SetPublishedED2K(bool val)1280 void CKnownFile::SetPublishedED2K(bool val){
1281 	m_PublishedED2K = val;
1282 	Notify_SharedFilesUpdateItem(this);
1283 }
1284 
PublishNotes()1285 bool CKnownFile::PublishNotes()
1286 {
1287 	if(m_lastPublishTimeKadNotes > (uint32)time(NULL)) {
1288 		return false;
1289 	}
1290 
1291 	if(!GetFileComment().IsEmpty()) {
1292 		m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1293 		return true;
1294 	}
1295 
1296 	if(GetFileRating() != 0) {
1297 		m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1298 		return true;
1299 	}
1300 
1301 	return false;
1302 }
1303 
PublishSrc()1304 bool CKnownFile::PublishSrc()
1305 {
1306 	uint32 lastBuddyIP = 0;
1307 
1308 	if( theApp->IsFirewalled() ) {
1309 		CUpDownClient* buddy = theApp->clientlist->GetBuddy();
1310 		if( buddy ) {
1311 			lastBuddyIP = theApp->clientlist->GetBuddy()->GetIP();
1312 			if( lastBuddyIP != m_lastBuddyIP ) {
1313 				SetLastPublishTimeKadSrc( (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES, lastBuddyIP );
1314 				return true;
1315 			}
1316 		} else {
1317 			return false;
1318 		}
1319 	}
1320 
1321 	if(m_lastPublishTimeKadSrc > (uint32)time(NULL)) {
1322 		return false;
1323 	}
1324 
1325 	SetLastPublishTimeKadSrc((uint32)time(NULL)+KADEMLIAREPUBLISHTIMES,lastBuddyIP);
1326 	return true;
1327 
1328 }
1329 
UpdatePartsInfo()1330 void CKnownFile::UpdatePartsInfo()
1331 {
1332 	// Cache part count
1333 	uint16 partcount = GetPartCount();
1334 	bool flag = (time(NULL) - m_nCompleteSourcesTime > 0);
1335 
1336 	// Ensure the frequency-list is ready
1337 	if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1338 		m_AvailPartFrequency.clear();
1339 		m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1340 	}
1341 
1342 	if (flag) {
1343 		ArrayOfUInts16 count;
1344 		count.reserve(m_ClientUploadList.size());
1345 
1346 		SourceSet::iterator it = m_ClientUploadList.begin();
1347 		for ( ; it != m_ClientUploadList.end(); ++it ) {
1348 			CUpDownClient* client = it->GetClient();
1349 			if ( !client->GetUpPartStatus().empty() && client->GetUpPartCount() == partcount ) {
1350 				count.push_back(client->GetUpCompleteSourcesCount());
1351 			}
1352 		}
1353 
1354 		m_nCompleteSourcesCount = m_nCompleteSourcesCountLo = m_nCompleteSourcesCountHi = 0;
1355 
1356 		if( partcount > 0) {
1357 			m_nCompleteSourcesCount = m_AvailPartFrequency[0];
1358 		}
1359 		for (uint16 i = 1; i < partcount; ++i) {
1360 			if( m_nCompleteSourcesCount > m_AvailPartFrequency[i]) {
1361 				m_nCompleteSourcesCount = m_AvailPartFrequency[i];
1362 			}
1363 		}
1364 		count.push_back(m_nCompleteSourcesCount);
1365 
1366 		int32 n = count.size();
1367 		if (n > 0) {
1368 			std::sort(count.begin(), count.end(), std::less<uint16>());
1369 
1370 			// calculate range
1371 			int i = n >> 1;			// (n / 2)
1372 			int j = (n * 3) >> 2;	// (n * 3) / 4
1373 			int k = (n * 7) >> 3;	// (n * 7) / 8
1374 
1375 			// For complete files, trust the people your uploading to more...
1376 
1377 			// For low guess and normal guess count
1378 			//	- If we see more sources then the guessed low and
1379 			//	normal, use what we see.
1380 			//	- If we see less sources then the guessed low,
1381 			//	adjust network accounts for 100%, we account for
1382 			//	0% with what we see and make sure we are still
1383 			//	above the normal.
1384 			// For high guess
1385 			//	Adjust 100% network and 0% what we see.
1386 			if (n < 20) {
1387 				if ( count[i] < m_nCompleteSourcesCount ) {
1388 					m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1389 				} else {
1390 					m_nCompleteSourcesCountLo = count[i];
1391 				}
1392 				m_nCompleteSourcesCount= m_nCompleteSourcesCountLo;
1393 				m_nCompleteSourcesCountHi = count[j];
1394 				if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1395 					m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1396 				}
1397 			} else {
1398 			// Many sources..
1399 			// For low guess
1400 			//	Use what we see.
1401 			// For normal guess
1402 			//	Adjust network accounts for 100%, we account for
1403 			//	0% with what we see and make sure we are still above the low.
1404 			// For high guess
1405 			//	Adjust network accounts for 100%, we account for 0%
1406 			//	with what we see and make sure we are still above the normal.
1407 
1408 				m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1409 				m_nCompleteSourcesCount = count[j];
1410 				if( m_nCompleteSourcesCount < m_nCompleteSourcesCountLo ) {
1411 					m_nCompleteSourcesCount = m_nCompleteSourcesCountLo;
1412 				}
1413 				m_nCompleteSourcesCountHi= count[k];
1414 				if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1415 					m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1416 				}
1417 			}
1418 		}
1419 		m_nCompleteSourcesTime = time(NULL) + (60);
1420 	}
1421 
1422 	Notify_SharedFilesUpdateItem(this);
1423 }
1424 
1425 
UpdateUpPartsFrequency(CUpDownClient * client,bool increment)1426 void CKnownFile::UpdateUpPartsFrequency( CUpDownClient* client, bool increment )
1427 {
1428 	if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1429 		m_AvailPartFrequency.clear();
1430 		m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1431 		if ( !increment ) {
1432 			return;
1433 		}
1434 	}
1435 
1436 	const BitVector& freq = client->GetUpPartStatus();
1437 	unsigned int size = freq.size();
1438 	if ( size != m_AvailPartFrequency.size() ) {
1439 		return;
1440 	}
1441 
1442 	if ( increment ) {
1443 		for ( unsigned int i = 0; i < size; ++i ) {
1444 			if ( freq.get(i) ) {
1445 				m_AvailPartFrequency[i]++;
1446 			}
1447 		}
1448 	} else {
1449 		for ( unsigned int i = 0; i < size; ++i ) {
1450 			if ( freq.get(i) ) {
1451 				m_AvailPartFrequency[i]--;
1452 			}
1453 		}
1454 	}
1455 }
1456 
ClearPriority()1457 void CKnownFile::ClearPriority() {
1458 	if ( !m_bAutoUpPriority ) return;
1459 	m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
1460 	UpdateAutoUpPriority();
1461 }
1462 
GuessAndRemoveExt(CPath & name)1463 static void GuessAndRemoveExt(CPath& name)
1464 {
1465 	wxString ext = name.GetExt();
1466 
1467 	// Remove common two-part extensions, such as "tar.gz"
1468 	if (ext == wxT("gz") || ext == wxT("bz2")) {
1469 		name = name.RemoveExt();
1470 		if (name.GetExt() == wxT("tar")) {
1471 			name = name.RemoveExt();
1472 		}
1473 	// might be an extension if length == 3
1474 	// and also remove some common non-three-character extensions
1475 	} else if (ext.Length() == 3  ||
1476 		   ext == wxT("7z")   ||
1477 		   ext == wxT("rm")   ||
1478 		   ext == wxT("jpeg") ||
1479 		   ext == wxT("mpeg")
1480 		   ) {
1481 		name = name.RemoveExt();
1482 	}
1483 }
1484 
SetFileName(const CPath & filename)1485 void CKnownFile::SetFileName(const CPath& filename)
1486 {
1487 	CAbstractFile::SetFileName(filename);
1488 	wordlist.clear();
1489 	// Don't publish extension. That'd kill the node indexing e.g. "avi".
1490 	CPath tmpName = GetFileName();
1491 	GuessAndRemoveExt(tmpName);
1492 	Kademlia::CSearchManager::GetWords(tmpName.GetPrintable(), &wordlist);
1493 }
1494 
1495 #endif // CLIENT_GUI
1496 
1497 //For File Comment //
LoadComment() const1498 void CKnownFile::LoadComment() const
1499 {
1500 	#ifndef CLIENT_GUI
1501 	wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1502 
1503 	wxConfigBase* cfg = wxConfigBase::Get();
1504 
1505 	m_strComment = cfg->Read( strCfgPath + wxT("Comment"), wxEmptyString);
1506 	m_iRating = cfg->Read( strCfgPath + wxT("Rate"), 0l);
1507 	#endif
1508 
1509 	m_bCommentLoaded = true;
1510 }
1511 
1512 
GetAICHMasterHash() const1513 wxString CKnownFile::GetAICHMasterHash() const
1514 {
1515 #ifdef CLIENT_GUI
1516 	return m_AICHMasterHash;
1517 #else
1518 	if (HasProperAICHHashSet()) {
1519 		return m_pAICHHashSet->GetMasterHash().GetString();
1520 	}
1521 
1522 	return wxEmptyString;
1523 #endif
1524 }
1525 
1526 
HasProperAICHHashSet() const1527 bool CKnownFile::HasProperAICHHashSet() const
1528 {
1529 #ifdef CLIENT_GUI
1530 	return m_AICHMasterHash.Length() != 0;
1531 #else
1532 	return m_pAICHHashSet->HasValidMasterHash() &&
1533 		(m_pAICHHashSet->GetStatus() == AICH_HASHSETCOMPLETE ||
1534 		 m_pAICHHashSet->GetStatus() == AICH_VERIFIED);
1535 #endif
1536 }
1537 
GetFeedback() const1538 wxString CKnownFile::GetFeedback() const
1539 {
1540 	return	  wxString(_("File name")) + wxT(": ") + GetFileName().GetPrintable() + wxT("\n")
1541 		+ _("File size") + wxT(": ") + CastItoXBytes(GetFileSize()) + wxT("\n")
1542 		+ _("Share ratio") + CFormat(wxT(": %.2f%%\n")) % (((double)statistic.GetAllTimeTransferred() / (double)GetFileSize()) * 100.0)
1543 		+ _("Uploaded") + wxT(": ") + CastItoXBytes(statistic.GetTransferred()) + wxT(" (") + CastItoXBytes(statistic.GetAllTimeTransferred()) + wxT(")\n")
1544 		+ _("Requested") + CFormat(wxT(": %u (%u)\n")) % statistic.GetRequests() % statistic.GetAllTimeRequests()
1545 		+ _("Accepted") + CFormat(wxT(": %u (%u)\n")) % statistic.GetAccepts() % statistic.GetAllTimeAccepts()
1546 		+ _("On Queue") + CFormat(wxT(": %u\n")) % GetQueuedCount()
1547 		+ _("Complete sources") + CFormat(wxT(": %u\n")) % m_nCompleteSourcesCount;
1548 }
1549 
1550 // File_checked_for_headers
1551