Upstream tarball 9574
[amule.git] / src / KnownFile.cpp
blob34c63b19846463255a1f8fe55116a216b700bfb6
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-2008 aMule Team ( admin@amule.org / http://www.amule.org )
7 // Copyright (c) 2002-2008 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.
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.
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
29 #include "KnownFile.h" // Do_not_auto_remove
32 #include <protocol/kad/Constants.h>
33 #include <protocol/ed2k/Client2Client/TCP.h>
34 #include <protocol/Protocols.h>
35 #include <tags/FileTags.h>
38 #include <wx/config.h>
41 #include "MemFile.h" // Needed for CMemFile
42 #include "updownclient.h" // Needed for CUpDownClient
43 #include "Packet.h" // Needed for CPacket
44 #include "Preferences.h" // Needed for CPreferences
45 #include "KnownFileList.h" // Needed for CKnownFileList
46 #include "amule.h" // Needed for theApp
47 #include "PartFile.h" // Needed for SavePartFile
48 #include "ClientList.h" // Needed for clientlist (buddy support)
49 #include "Logger.h"
50 #include "ScopedPtr.h" // Needed for CScopedArray and CScopedPtr
51 #include "GuiEvents.h" // Needed for Notify_*
52 #include "SearchFile.h" // Needed for CSearchFile
53 #include "FileArea.h" // Needed for CFileArea
55 #include "CryptoPP_Inc.h" // Needed for MD4
57 #include <common/Format.h>
59 CFileStatistic::CFileStatistic() :
60 requested(0),
61 transferred(0),
62 accepted(0),
63 alltimerequested(0),
64 alltimetransferred(0),
65 alltimeaccepted(0)
69 #ifndef CLIENT_GUI
71 void CFileStatistic::AddRequest(){
72 requested++;
73 alltimerequested++;
74 theApp->knownfiles->requested++;
75 theApp->sharedfiles->UpdateItem(fileParent);
78 void CFileStatistic::AddAccepted(){
79 accepted++;
80 alltimeaccepted++;
81 theApp->knownfiles->accepted++;
82 theApp->sharedfiles->UpdateItem(fileParent);
85 void CFileStatistic::AddTransferred(uint64 bytes){
86 transferred += bytes;
87 alltimetransferred += bytes;
88 theApp->knownfiles->transferred += bytes;
89 theApp->sharedfiles->UpdateItem(fileParent);
92 #endif // CLIENT_GUI
95 /* Abstract File (base class)*/
97 CAbstractFile::CAbstractFile()
99 m_iRating(0),
100 m_hasComment(false),
101 m_iUserRating(0),
102 m_nFileSize(0)
107 CAbstractFile::CAbstractFile(const CAbstractFile& other)
109 m_abyFileHash(other.m_abyFileHash),
110 m_strComment(other.m_strComment),
111 m_iRating(other.m_iRating),
112 m_hasComment(other.m_hasComment),
113 m_iUserRating(other.m_iUserRating),
114 m_taglist(other.m_taglist),
115 m_nFileSize(other.m_nFileSize),
116 m_fileName(other.m_fileName)
118 /* // TODO: Currently it's not safe to duplicate the entries, but isn't needed either.
119 CKadEntryPtrList::const_iterator it = other.m_kadNotes.begin();
120 for (; it != other.m_kadNotes.end(); ++it) {
121 m_kadNotes.push_back(new Kademlia::CEntry(**it));
127 void CAbstractFile::SetFileName(const CPath& fileName)
129 m_fileName = fileName;
132 uint32 CAbstractFile::GetIntTagValue(uint8 tagname) const
134 ArrayOfCTag::const_iterator it = m_taglist.begin();
135 for (; it != m_taglist.end(); ++it){
136 if (((*it).GetNameID() == tagname) && (*it).IsInt()) {
137 return (*it).GetInt();
140 return 0;
143 bool CAbstractFile::GetIntTagValue(uint8 tagname, uint32& ruValue) const
145 ArrayOfCTag::const_iterator it = m_taglist.begin();
146 for (; it != m_taglist.end(); ++it){
147 if (((*it).GetNameID() == tagname) && (*it).IsInt()){
148 ruValue = (*it).GetInt();
149 return true;
152 return false;
155 uint32 CAbstractFile::GetIntTagValue(const wxString& tagname) const
157 ArrayOfCTag::const_iterator it = m_taglist.begin();
158 for (; it != m_taglist.end(); ++it){
159 if ((*it).IsInt() && ((*it).GetName() == tagname)) {
160 return (*it).GetInt();
163 return 0;
166 const wxString& CAbstractFile::GetStrTagValue(uint8 tagname) const
168 ArrayOfCTag::const_iterator it = m_taglist.begin();
169 for (; it != m_taglist.end(); ++it){
170 if ((*it).GetNameID() == tagname && (*it).IsStr()) {
171 return (*it).GetStr();
174 return EmptyString;
177 const wxString& CAbstractFile::GetStrTagValue(const wxString& tagname) const
179 ArrayOfCTag::const_iterator it = m_taglist.begin();
180 for (; it != m_taglist.end(); ++it){
181 if ((*it).IsStr() && ((*it).GetName() == tagname)) {
182 return (*it).GetStr();
185 return EmptyString;
188 const CTag *CAbstractFile::GetTag(uint8 tagname, uint8 tagtype) const
190 ArrayOfCTag::const_iterator it = m_taglist.begin();
191 for (; it != m_taglist.end(); ++it){
192 if ((*it).GetNameID() == tagname && (*it).GetType() == tagtype) {
193 return &(*it);
196 return NULL;
199 const CTag *CAbstractFile::GetTag(const wxString& tagname, uint8 tagtype) const
201 ArrayOfCTag::const_iterator it = m_taglist.begin();
202 for (; it != m_taglist.end(); ++it){
203 if ((*it).GetType() == tagtype && (*it).GetName() == tagname) {
204 return &(*it);
207 return NULL;
210 const CTag *CAbstractFile::GetTag(uint8 tagname) const
212 ArrayOfCTag::const_iterator it = m_taglist.begin();
213 for (; it != m_taglist.end(); ++it){
214 if ((*it).GetNameID() == tagname) {
215 return &(*it);
218 return NULL;
221 const CTag *CAbstractFile::GetTag(const wxString& tagname) const
223 ArrayOfCTag::const_iterator it = m_taglist.begin();
224 for (; it != m_taglist.end(); ++it){
225 if ((*it).GetName() == tagname) {
226 return &(*it);
229 return NULL;
232 void CAbstractFile::AddTagUnique(const CTag &rTag)
234 ArrayOfCTag::iterator it = m_taglist.begin();
235 for (; it != m_taglist.end(); ++it) {
236 if ( ( ((*it).GetNameID() != 0 &&
237 (*it).GetNameID() == rTag.GetNameID()) ||
238 (!(*it).GetName().IsEmpty() &&
239 !rTag.GetName().IsEmpty() &&
240 (*it).GetName() == rTag.GetName()) ) &&
241 (*it).GetType() == rTag.GetType()){
242 m_taglist.erase(it);
243 m_taglist.insert(it, rTag);
244 return;
247 m_taglist.push_back(rTag);
250 #ifndef CLIENT_GUI
251 void CAbstractFile::AddNote(Kademlia::CEntry *pEntry)
253 CKadEntryPtrList::iterator it = m_kadNotes.begin();
254 for (; it != m_kadNotes.end(); ++it) {
255 Kademlia::CEntry* entry = *it;
256 if(entry->m_uIP == pEntry->m_uIP || entry->m_uSourceID == pEntry->m_uSourceID) {
257 delete pEntry;
258 return;
261 m_kadNotes.push_front(pEntry);
263 #else
264 void CAbstractFile::AddNote(Kademlia::CEntry *)
267 #endif
270 /* Known File */
272 CKnownFile::CKnownFile()
274 Init();
276 m_bAutoUpPriority = thePrefs::GetNewAutoUp();
277 m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
281 //#warning Experimental: Construct a CKnownFile from a CSearchFile
282 CKnownFile::CKnownFile(const CSearchFile &searchFile)
284 // This will copy the file hash
285 CAbstractFile(static_cast<const CAbstractFile &>(searchFile))
287 Init();
289 // Use CKnownFile::SetFileName()
290 SetFileName(searchFile.GetFileName());
292 // Use CKnownFile::SetFileSize()
293 SetFileSize(searchFile.GetFileSize());
295 m_bAutoUpPriority = thePrefs::GetNewAutoUp();
296 m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
300 void CKnownFile::Init()
302 m_nCompleteSourcesTime = time(NULL);
303 m_nCompleteSourcesCount = 0;
304 m_nCompleteSourcesCountLo = 0;
305 m_nCompleteSourcesCountHi = 0;
306 m_bCommentLoaded = false;
307 m_iPartCount = 0;
308 m_iED2KPartCount = 0;
309 m_iED2KPartHashCount = 0;
310 m_PublishedED2K = false;
311 kadFileSearchID = 0;
312 m_lastPublishTimeKadSrc = 0;
313 m_lastPublishTimeKadNotes = 0;
314 m_lastBuddyIP = 0;
315 m_lastDateChanged = 0;
317 statistic.fileParent = this;
319 #ifndef CLIENT_GUI
320 m_pAICHHashSet = new CAICHHashSet(this);
321 #endif
325 void CKnownFile::SetFileSize(uint64 nFileSize)
327 CAbstractFile::SetFileSize(nFileSize);
328 #ifndef CLIENT_GUI
329 m_pAICHHashSet->SetFileSize(nFileSize);
330 #endif
332 // Examples of parthashs, hashsets and filehashs for different filesizes
333 // according the ed2k protocol
334 //----------------------------------------------------------------------
336 //File size: 3 bytes
337 //File hash: 2D55E87D0E21F49B9AD25F98531F3724
338 //Nr. hashs: 0
341 //File size: 1*PARTSIZE
342 //File hash: A72CA8DF7F07154E217C236C89C17619
343 //Nr. hashs: 2
344 //Hash[ 0]: 4891ED2E5C9C49F442145A3A5F608299
345 //Hash[ 1]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
348 //File size: 1*PARTSIZE + 1 byte
349 //File hash: 2F620AE9D462CBB6A59FE8401D2B3D23
350 //Nr. hashs: 2
351 //Hash[ 0]: 121795F0BEDE02DDC7C5426D0995F53F
352 //Hash[ 1]: C329E527945B8FE75B3C5E8826755747
355 //File size: 2*PARTSIZE
356 //File hash: A54C5E562D5E03CA7D77961EB9A745A4
357 //Nr. hashs: 3
358 //Hash[ 0]: B3F5CE2A06BF403BFB9BFFF68BDDC4D9
359 //Hash[ 1]: 509AA30C9EA8FC136B1159DF2F35B8A9
360 //Hash[ 2]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
363 //File size: 3*PARTSIZE
364 //File hash: 5E249B96F9A46A18FC2489B005BF2667
365 //Nr. hashs: 4
366 //Hash[ 0]: 5319896A2ECAD43BF17E2E3575278E72
367 //Hash[ 1]: D86EF157D5E49C5ED502EDC15BB5F82B
368 //Hash[ 2]: 10F2D5B1FCB95C0840519C58D708480F
369 //Hash[ 3]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
372 //File size: 3*PARTSIZE + 1 byte
373 //File hash: 797ED552F34380CAFF8C958207E40355
374 //Nr. hashs: 4
375 //Hash[ 0]: FC7FD02CCD6987DCF1421F4C0AF94FB8
376 //Hash[ 1]: 2FE466AF8A7C06DA3365317B75A5ACFE
377 //Hash[ 2]: 873D3BF52629F7C1527C6E8E473C1C30
378 //Hash[ 3]: BCE50BEE7877BB07BB6FDA56BFE142FB
381 // File size Data parts ED2K parts ED2K part hashs
382 // ---------------------------------------------------------------
383 // 1..PARTSIZE-1 1 1 0(!)
384 // PARTSIZE 1 2(!) 2(!)
385 // PARTSIZE+1 2 2 2
386 // PARTSIZE*2 2 3(!) 3(!)
387 // PARTSIZE*2+1 3 3 3
389 if (nFileSize == 0){
390 //wxFAIL; // Kry - Why commented out by lemonfan? it can never be 0
391 m_iPartCount = 0;
392 m_iED2KPartCount = 0;
393 m_iED2KPartHashCount = 0;
394 m_sizeLastPart = 0;
395 return;
398 // nr. of data parts
399 m_iPartCount = nFileSize / PARTSIZE + 1;
400 // size of last part
401 m_sizeLastPart = nFileSize % PARTSIZE;
402 // file with size of n * PARTSIZE
403 if (m_sizeLastPart == 0) {
404 m_sizeLastPart = PARTSIZE;
405 m_iPartCount--;
408 // nr. of parts to be used with OP_FILESTATUS
409 m_iED2KPartCount = nFileSize / PARTSIZE + 1;
411 // nr. of parts to be used with OP_HASHSETANSWER
412 m_iED2KPartHashCount = nFileSize / PARTSIZE;
413 if (m_iED2KPartHashCount != 0) {
414 m_iED2KPartHashCount += 1;
419 #ifdef CLIENT_GUI
421 CKnownFile::CKnownFile(CEC_SharedFile_Tag *tag)
423 Init();
425 SetFileName(CPath(tag->FileName()));
426 m_abyFileHash = tag->ID();
427 SetFileSize(tag->SizeFull());
428 m_AvailPartFrequency.insert(m_AvailPartFrequency.end(), m_iPartCount, 0);
429 m_iUpPriority = tag->Prio();
430 if ( m_iUpPriority >= 10 ) {
431 m_iUpPriority-= 10;
432 m_bAutoUpPriority = true;
433 } else {
434 m_bAutoUpPriority = false;
437 m_AICHMasterHash = tag->GetAICHHash();
440 CKnownFile::~CKnownFile()
444 #else // ! CLIENT_GUI
446 CKnownFile::~CKnownFile()
448 SourceSet::iterator it = m_ClientUploadList.begin();
449 for ( ; it != m_ClientUploadList.end(); ++it ) {
450 (*it)->ClearUploadFileID();
453 delete m_pAICHHashSet;
456 void CKnownFile::AddUploadingClient(CUpDownClient* client)
458 m_ClientUploadList.insert(client);
460 UpdateAutoUpPriority();
464 void CKnownFile::RemoveUploadingClient(CUpDownClient* client)
466 if (m_ClientUploadList.erase(client)) {
467 UpdateAutoUpPriority();
472 void CKnownFile::SetFilePath(const CPath& filePath)
474 m_filePath = filePath;
478 // needed for memfiles. its probably better to switch everything to CFile...
479 bool CKnownFile::LoadHashsetFromFile(const CFileDataIO* file, bool checkhash)
481 CMD4Hash checkid = file->ReadHash();
483 uint16 parts = file->ReadUInt16();
484 for (uint16 i = 0; i < parts; ++i){
485 CMD4Hash cur_hash = file->ReadHash();
486 m_hashlist.push_back(cur_hash);
489 // SLUGFILLER: SafeHash - always check for valid m_hashlist
490 if (!checkhash){
491 m_abyFileHash = checkid;
492 if (parts <= 1) { // nothing to check
493 return true;
495 } else {
496 if ( m_abyFileHash != checkid ) {
497 return false; // wrong file?
498 } else {
499 if (parts != GetED2KPartHashCount()) {
500 return false;
504 // SLUGFILLER: SafeHash
506 // trust noone ;-)
507 // lol, useless comment but made me lmao
508 // wtf you guys are weird.
510 if (!m_hashlist.empty()) {
511 CreateHashFromHashlist(m_hashlist, &checkid);
514 if ( m_abyFileHash == checkid ) {
515 return true;
516 } else {
517 m_hashlist.clear();
518 return false;
523 bool CKnownFile::LoadTagsFromFile(const CFileDataIO* file)
525 uint32 tagcount = file->ReadUInt32();
526 for (uint32 j = 0; j != tagcount; ++j) {
527 CTag newtag(*file, true);
528 switch(newtag.GetNameID()){
529 case FT_FILENAME:
530 if (GetFileName().IsOk()) {
531 // Unlike eMule, we actually prefer the second
532 // filename tag, since we use it to specify the
533 // 'universial' filename (see CPath::ToUniv).
534 CPath path = CPath::FromUniv(newtag.GetStr());
536 // May be invalid, if from older versions where
537 // unicoded filenames be saved as empty-strings.
538 if (path.IsOk()) {
539 SetFileName(path);
541 } else {
542 SetFileName(CPath(newtag.GetStr()));
544 break;
546 case FT_FILESIZE:
547 SetFileSize(newtag.GetInt());
548 m_AvailPartFrequency.clear();
549 m_AvailPartFrequency.insert(
550 m_AvailPartFrequency.begin(),
551 GetPartCount(), 0);
552 break;
554 case FT_ATTRANSFERRED:
555 statistic.alltimetransferred += newtag.GetInt();
556 break;
558 case FT_ATTRANSFERREDHI:
559 statistic.alltimetransferred =
560 (((uint64)newtag.GetInt()) << 32) +
561 ((uint64)statistic.alltimetransferred);
562 break;
564 case FT_ATREQUESTED:
565 statistic.alltimerequested = newtag.GetInt();
566 break;
568 case FT_ATACCEPTED:
569 statistic.alltimeaccepted = newtag.GetInt();
570 break;
572 case FT_ULPRIORITY:
573 m_iUpPriority = newtag.GetInt();
574 if( m_iUpPriority == PR_AUTO ){
575 m_iUpPriority = PR_HIGH;
576 m_bAutoUpPriority = true;
577 } else {
578 if ( m_iUpPriority != PR_VERYLOW &&
579 m_iUpPriority != PR_LOW &&
580 m_iUpPriority != PR_NORMAL &&
581 m_iUpPriority != PR_HIGH &&
582 m_iUpPriority != PR_VERYHIGH &&
583 m_iUpPriority != PR_POWERSHARE) {
584 m_iUpPriority = PR_NORMAL;
587 m_bAutoUpPriority = false;
589 break;
591 case FT_PERMISSIONS:
592 // Ignore it, it's not used anymore.
593 break;
595 case FT_AICH_HASH: {
596 CAICHHash hash;
597 bool hashSizeOk =
598 hash.DecodeBase32(newtag.GetStr()) == CAICHHash::GetHashSize();
599 wxASSERT(hashSizeOk);
600 if (hashSizeOk) {
601 m_pAICHHashSet->SetMasterHash(hash, AICH_HASHSETCOMPLETE);
603 break;
606 case FT_KADLASTPUBLISHSRC:
607 SetLastPublishTimeKadSrc( newtag.GetInt(), 0 );
609 if(GetLastPublishTimeKadSrc() > (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES) {
610 //There may be a posibility of an older client that saved a random number here.. This will check for that..
611 SetLastPublishTimeKadSrc(0, 0);
613 break;
615 case FT_KADLASTPUBLISHNOTES:
616 SetLastPublishTimeKadNotes( newtag.GetInt() );
617 break;
619 case FT_KADLASTPUBLISHKEY:
620 // Just purge it
621 wxASSERT( newtag.IsInt() );
622 break;
624 default:
625 // Store them here and write them back on saving.
626 m_taglist.push_back(newtag);
630 return true;
634 bool CKnownFile::LoadDateFromFile(const CFileDataIO* file)
636 m_lastDateChanged = file->ReadUInt32();
638 return true;
642 bool CKnownFile::LoadFromFile(const CFileDataIO* file)
644 // SLUGFILLER: SafeHash - load first, verify later
645 bool ret1 = LoadDateFromFile(file);
646 bool ret2 = LoadHashsetFromFile(file,false);
647 bool ret3 = LoadTagsFromFile(file);
648 UpdatePartsInfo();
649 // Final hash-count verification, needs to be done after the tags are loaded.
650 return ret1 && ret2 && ret3 && GetED2KPartHashCount()==GetHashCount();
651 // SLUGFILLER: SafeHash
655 bool CKnownFile::WriteToFile(CFileDataIO* file)
657 wxCHECK(!IsPartFile(), false);
659 // date
660 file->WriteUInt32(m_lastDateChanged);
661 // hashset
662 file->WriteHash(m_abyFileHash);
664 uint16 parts = m_hashlist.size();
665 file->WriteUInt16(parts);
667 for (int i = 0; i < parts; ++i)
668 file->WriteHash(m_hashlist[i]);
670 //tags
671 const int iFixedTags = 8;
672 uint32 tagcount = iFixedTags;
673 if (HasProperAICHHashSet()) {
674 tagcount++;
676 // Float meta tags are currently not written. All older eMule versions < 0.28a have
677 // a bug in the meta tag reading+writing code. To achive maximum backward
678 // compatibility for met files with older eMule versions we just don't write float
679 // tags. This is OK, because we (eMule) do not use float tags. The only float tags
680 // we may have to handle is the '# Sent' tag from the Hybrid, which is pretty
681 // useless but may be received from us via the servers.
683 // The code for writing the float tags SHOULD BE ENABLED in SOME MONTHS (after most
684 // people are using the newer eMule versions which do not write broken float tags).
685 for (size_t j = 0; j < m_taglist.size(); ++j){
686 if (m_taglist[j].IsInt() || m_taglist[j].IsStr()) {
687 ++tagcount;
691 if (m_lastPublishTimeKadSrc) {
692 ++tagcount;
695 if (m_lastPublishTimeKadNotes){
696 ++tagcount;
699 // standard tags
701 file->WriteUInt32(tagcount);
703 // We still save the unicoded filename, for backwards
704 // compatibility with pre-2.2 and other clients.
705 CTagString nametag_unicode(FT_FILENAME, GetFileName().GetRaw());
706 // We write it with BOM to kep eMule compatibility
707 nametag_unicode.WriteTagToFile(file,utf8strOptBOM);
709 // The non-unicoded filename is written in an 'universial'
710 // format, which allows us to identify files, even if the
711 // system locale changes.
712 CTagString nametag(FT_FILENAME, CPath::ToUniv(GetFileName()));
713 nametag.WriteTagToFile(file);
715 CTagIntSized sizetag(FT_FILESIZE, GetFileSize(), IsLargeFile() ? 64 : 32);
716 sizetag.WriteTagToFile(file);
718 // statistic
719 uint32 tran;
720 tran=statistic.alltimetransferred & 0xFFFFFFFF;
721 CTagInt32 attag1(FT_ATTRANSFERRED, tran);
722 attag1.WriteTagToFile(file);
724 tran=statistic.alltimetransferred>>32;
725 CTagInt32 attag4(FT_ATTRANSFERREDHI, tran);
726 attag4.WriteTagToFile(file);
728 CTagInt32 attag2(FT_ATREQUESTED, statistic.GetAllTimeRequests());
729 attag2.WriteTagToFile(file);
731 CTagInt32 attag3(FT_ATACCEPTED, statistic.GetAllTimeAccepts());
732 attag3.WriteTagToFile(file);
734 // priority N permission
735 CTagInt32 priotag(FT_ULPRIORITY, IsAutoUpPriority() ? PR_AUTO : m_iUpPriority);
736 priotag.WriteTagToFile(file);
738 //AICH Filehash
739 if (HasProperAICHHashSet()) {
740 CTagString aichtag(FT_AICH_HASH, m_pAICHHashSet->GetMasterHash().GetString());
741 aichtag.WriteTagToFile(file);
744 // Kad sources
745 if (m_lastPublishTimeKadSrc){
746 CTagInt32 kadLastPubSrc(FT_KADLASTPUBLISHSRC, m_lastPublishTimeKadSrc);
747 kadLastPubSrc.WriteTagToFile(file);
750 // Kad notes
751 if (m_lastPublishTimeKadNotes){
752 CTagInt32 kadLastPubNotes(FT_KADLASTPUBLISHNOTES, m_lastPublishTimeKadNotes);
753 kadLastPubNotes.WriteTagToFile(file);
756 //other tags
757 for (size_t j = 0; j < m_taglist.size(); ++j){
758 if (m_taglist[j].IsInt() || m_taglist[j].IsStr()) {
759 m_taglist[j].WriteTagToFile(file);
762 return true;
766 void CKnownFile::CreateHashFromHashlist(const ArrayOfCMD4Hash& hashes, CMD4Hash* Output)
768 wxCHECK_RET(hashes.size(), wxT("No input to hash from in CreateHashFromHashlist"));
770 std::vector<byte> buffer(hashes.size() * MD4HASH_LENGTH);
771 std::vector<byte>::iterator it = buffer.begin();
773 for (size_t i = 0; i < hashes.size(); ++i) {
774 it = STLCopy_n(hashes[i].GetHash(), MD4HASH_LENGTH, it);
777 CreateHashFromInput(&buffer[0], buffer.size(), Output, NULL);
781 void CKnownFile::CreateHashFromFile(CFile& file, uint32 Length, CMD4Hash* Output, CAICHHashTree* pShaHashOut)
783 wxCHECK_RET(Length, wxT("No input to hash from in CreateHashFromFile"));
785 CFileArea area;
786 area.Read(file, Length);
788 CreateHashFromInput(area.GetBuffer(), Length, Output, pShaHashOut);
789 area.CheckError();
793 void CKnownFile::CreateHashFromInput(const byte* input, uint32 Length, CMD4Hash* Output, CAICHHashTree* pShaHashOut )
795 wxASSERT_MSG(Output || pShaHashOut, wxT("Nothing to do in CreateHashFromInput"));
796 wxCHECK_RET(input, wxT("No input to hash from in CreateHashFromInput"));
797 wxASSERT(Length <= PARTSIZE); // We never hash more than one PARTSIZE
799 CMemFile data(input, Length);
801 uint32 Required = Length;
802 byte X[64*128];
804 uint32 posCurrentEMBlock = 0;
805 uint32 nIACHPos = 0;
806 CScopedPtr<CAICHHashAlgo> pHashAlg(CAICHHashSet::GetNewHashAlgo());
808 // This is all AICH.
809 while (Required >= 64) {
810 uint32 len = Required / 64;
811 if (len > sizeof(X)/(64 * sizeof(X[0]))) {
812 len = sizeof(X)/(64 * sizeof(X[0]));
815 data.Read(&X, len * 64);
817 // SHA hash needs 180KB blocks
818 if (pShaHashOut) {
819 if (nIACHPos + len*64 >= EMBLOCKSIZE) {
820 uint32 nToComplete = EMBLOCKSIZE - nIACHPos;
821 pHashAlg->Add(X, nToComplete);
822 wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE );
823 pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg.get());
824 posCurrentEMBlock += EMBLOCKSIZE;
825 pHashAlg->Reset();
826 pHashAlg->Add(X+nToComplete,(len*64) - nToComplete);
827 nIACHPos = (len*64) - nToComplete;
829 else{
830 pHashAlg->Add(X, len*64);
831 nIACHPos += len*64;
835 Required -= len*64;
837 // bytes to read
838 Required = Length % 64;
839 if (Required != 0){
840 data.Read(&X,Required);
842 if (pShaHashOut != NULL){
843 if (nIACHPos + Required >= EMBLOCKSIZE){
844 uint32 nToComplete = EMBLOCKSIZE - nIACHPos;
845 pHashAlg->Add(X, nToComplete);
846 wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE );
847 pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg.get());
848 posCurrentEMBlock += EMBLOCKSIZE;
849 pHashAlg->Reset();
850 pHashAlg->Add(X+nToComplete, Required - nToComplete);
851 nIACHPos = Required - nToComplete;
853 else{
854 pHashAlg->Add(X, Required);
855 nIACHPos += Required;
859 if (pShaHashOut != NULL){
860 if(nIACHPos > 0){
861 pShaHashOut->SetBlockHash(nIACHPos, posCurrentEMBlock, pHashAlg.get());
862 posCurrentEMBlock += nIACHPos;
864 wxASSERT( posCurrentEMBlock == Length );
865 wxCHECK2( pShaHashOut->ReCalculateHash(pHashAlg.get(), false), );
868 if (Output != NULL){
869 #ifdef __WEAK_CRYPTO__
870 CryptoPP::Weak::MD4 md4_hasher;
871 #else
872 CryptoPP::MD4 md4_hasher;
873 #endif
874 md4_hasher.CalculateDigest(Output->GetHash(), input, Length);
879 const CMD4Hash& CKnownFile::GetPartHash(uint16 part) const {
880 wxASSERT( part < m_hashlist.size() );
882 return m_hashlist[part];
885 CPacket* CKnownFile::CreateSrcInfoPacket(const CUpDownClient* forClient, uint8 byRequestedVersion, uint16 nRequestedOptions)
887 // Kad reviewed
889 if (m_ClientUploadList.empty()) {
890 return NULL;
893 if ((((CKnownFile*)forClient->GetRequestFile() != this)
894 && ((CKnownFile*)forClient->GetUploadFile() != this)) || forClient->GetUploadFileID() != GetFileHash()) {
895 wxString file1 = _("Unknown");
896 if (forClient->GetRequestFile() && forClient->GetRequestFile()->GetFileName().IsOk()) {
897 file1 = forClient->GetRequestFile()->GetFileName().GetPrintable();
898 } else if (forClient->GetUploadFile() && forClient->GetUploadFile()->GetFileName().IsOk()) {
899 file1 = forClient->GetUploadFile()->GetFileName().GetPrintable();
901 wxString file2 = _("Unknown");
902 if (GetFileName().IsOk()) {
903 file2 = GetFileName().GetPrintable();
905 AddDebugLogLineM(false, logKnownFiles, wxT("File missmatch on source packet (K) Sending: ") + file1 + wxT(" From: ") + file2);
906 return NULL;
909 const BitVector& rcvstatus = forClient->GetUpPartStatus();
910 bool SupportsUploadChunksState = !rcvstatus.empty();
911 //wxASSERT(rcvstatus.size() == GetPartCount()); // Obviously!
912 if (rcvstatus.size() != GetPartCount()) {
913 // Yuck. Same file but different part count? Seriously fucked up.
914 AddDebugLogLineM(false, logKnownFiles, wxString::Format(wxT("Impossible situation: different partcounts for the same known file: %i (client) and %i (file)"),rcvstatus.size(),GetPartCount()));
915 return NULL;
918 CMemFile data(1024);
920 uint8 byUsedVersion;
921 bool bIsSX2Packet;
922 if (forClient->SupportsSourceExchange2() && byRequestedVersion > 0){
923 // the client uses SourceExchange2 and requested the highest version he knows
924 // and we send the highest version we know, but of course not higher than his request
925 byUsedVersion = std::min(byRequestedVersion, (uint8)SOURCEEXCHANGE2_VERSION);
926 bIsSX2Packet = true;
927 data.WriteUInt8(byUsedVersion);
929 // we don't support any special SX2 options yet, reserved for later use
930 if (nRequestedOptions != 0) {
931 AddDebugLogLineM(false, logKnownFiles, CFormat(wxT("Client requested unknown options for SourceExchange2: %u")) % nRequestedOptions);
933 } else {
934 byUsedVersion = forClient->GetSourceExchange1Version();
935 bIsSX2Packet = false;
936 if (forClient->SupportsSourceExchange2()) {
937 AddDebugLogLineM(false, logKnownFiles, wxT("Client which announced to support SX2 sent SX1 packet instead"));
941 uint16 nCount = 0;
943 data.WriteHash(forClient->GetUploadFileID());
944 data.WriteUInt16(nCount);
945 uint32 cDbgNoSrc = 0;
947 SourceSet::iterator it = m_ClientUploadList.begin();
948 for ( ; it != m_ClientUploadList.end(); it++ ) {
949 const CUpDownClient *cur_src = *it;
951 if ( cur_src->HasLowID() ||
952 cur_src == forClient ||
953 !( cur_src->GetUploadState() == US_UPLOADING ||
954 cur_src->GetUploadState() == US_ONUPLOADQUEUE)) {
955 continue;
958 bool bNeeded = false;
960 if ( SupportsUploadChunksState ) {
961 const BitVector& srcstatus = cur_src->GetUpPartStatus();
962 if ( !srcstatus.empty() ) {
963 //wxASSERT(srcstatus.size() == GetPartCount()); // Obviously!
964 if (srcstatus.size() != GetPartCount()) {
965 continue;
967 if ( cur_src->GetUpPartCount() == forClient->GetUpPartCount() ) {
968 for (int x = 0; x < GetPartCount(); x++ ) {
969 if ( srcstatus.at(x) && !rcvstatus.at(x) ) {
970 // We know the receiving client needs
971 // a chunk from this client.
972 bNeeded = true;
973 break;
977 } else {
978 cDbgNoSrc++;
979 // This client doesn't support upload chunk status.
980 // So just send it and hope for the best.
981 bNeeded = true;
983 } else {
984 // remote client does not support upload chunk status,
985 // search sources which have at least one complete part
986 // we could even sort the list of sources by available
987 // chunks to return as much sources as possible which
988 // have the most available chunks. but this could be
989 // a noticeable performance problem.
990 const BitVector& srcstatus = cur_src->GetUpPartStatus();
991 if ( !srcstatus.empty() ) {
992 //wxASSERT(srcstatus.size() == GetPartCount());
993 if (srcstatus.size() != GetPartCount()) {
994 continue;
996 for (int x = 0; x < GetPartCount(); x++ ) {
997 if ( srcstatus.at(x) ) {
998 // this client has at least one chunk
999 bNeeded = true;
1000 break;
1003 } else {
1004 // This client doesn't support upload chunk status.
1005 // So just send it and hope for the best.
1006 bNeeded = true;
1010 if ( bNeeded ) {
1011 nCount++;
1012 uint32 dwID;
1013 if(byUsedVersion >= 3) {
1014 dwID = cur_src->GetUserIDHybrid();
1015 } else {
1016 dwID = cur_src->GetIP();
1018 data.WriteUInt32(dwID);
1019 data.WriteUInt16(cur_src->GetUserPort());
1020 data.WriteUInt32(cur_src->GetServerIP());
1021 data.WriteUInt16(cur_src->GetServerPort());
1023 if (byUsedVersion >= 2) {
1024 data.WriteHash(cur_src->GetUserHash());
1027 if (byUsedVersion >= 4){
1028 // CryptSettings - SourceExchange V4
1029 // 5 Reserved (!)
1030 // 1 CryptLayer Required
1031 // 1 CryptLayer Requested
1032 // 1 CryptLayer Supported
1033 const uint8 uSupportsCryptLayer = cur_src->SupportsCryptLayer() ? 1 : 0;
1034 const uint8 uRequestsCryptLayer = cur_src->RequestsCryptLayer() ? 1 : 0;
1035 const uint8 uRequiresCryptLayer = cur_src->RequiresCryptLayer() ? 1 : 0;
1036 const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0);
1037 data.WriteUInt8(byCryptOptions);
1040 if (nCount > 500) {
1041 break;
1046 if (!nCount) {
1047 return 0;
1050 data.Seek(bIsSX2Packet ? 17 : 16, wxFromStart);
1051 data.WriteUInt16(nCount);
1053 CPacket* result = new CPacket(data, OP_EMULEPROT, bIsSX2Packet ? OP_ANSWERSOURCES2 : OP_ANSWERSOURCES);
1055 if ( result->GetPacketSize() > 354 ) {
1056 result->PackPacket();
1059 return result;
1063 // Updates priority of file if autopriority is activated
1064 void CKnownFile::UpdateAutoUpPriority()
1066 if (IsAutoUpPriority()) {
1067 uint32 queued = GetQueuedCount();
1068 uint8 priority = PR_NORMAL;
1070 if (queued > 20) {
1071 priority = PR_LOW;
1072 } else if (queued > 1) {
1073 priority = PR_NORMAL;
1074 } else {
1075 priority = PR_HIGH;
1078 if (GetUpPriority() != priority) {
1079 SetUpPriority(priority, false);
1080 Notify_SharedFilesUpdateItem(this);
1085 void CKnownFile::SetFileComment(const wxString& strNewComment)
1087 if (m_strComment != strNewComment) {
1088 SetLastPublishTimeKadNotes(0);
1089 wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1091 wxConfigBase* cfg = wxConfigBase::Get();
1092 cfg->Write( strCfgPath + wxT("Comment"), strNewComment);
1094 m_strComment = strNewComment;
1096 SourceSet::iterator it = m_ClientUploadList.begin();
1097 for ( ; it != m_ClientUploadList.end(); it++ ) {
1098 (*it)->SetCommentDirty();
1104 // For File rate
1105 void CKnownFile::SetFileRating(int8 iNewRating)
1107 if (m_iRating != iNewRating) {
1108 SetLastPublishTimeKadNotes(0);
1109 wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1110 wxConfigBase* cfg = wxConfigBase::Get();
1111 cfg->Write( strCfgPath + wxT("Rate"), (int)iNewRating);
1112 m_iRating = iNewRating;
1114 SourceSet::iterator it = m_ClientUploadList.begin();
1115 for ( ; it != m_ClientUploadList.end(); it++ ) {
1116 (*it)->SetCommentDirty();
1122 void CKnownFile::SetUpPriority(uint8 iNewUpPriority, bool m_bsave){
1123 m_iUpPriority = iNewUpPriority;
1124 if( IsPartFile() && m_bsave ) {
1125 ((CPartFile*)this)->SavePartFile();
1129 void CKnownFile::SetPublishedED2K(bool val){
1130 m_PublishedED2K = val;
1131 Notify_SharedFilesUpdateItem(this);
1134 bool CKnownFile::PublishNotes()
1136 if(m_lastPublishTimeKadNotes > (uint32)time(NULL)) {
1137 return false;
1140 if(!GetFileComment().IsEmpty()) {
1141 m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1142 return true;
1145 if(GetFileRating() != 0) {
1146 m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1147 return true;
1150 return false;
1153 bool CKnownFile::PublishSrc()
1155 uint32 lastBuddyIP = 0;
1157 if( theApp->IsFirewalled() ) {
1158 CUpDownClient* buddy = theApp->clientlist->GetBuddy();
1159 if( buddy ) {
1160 lastBuddyIP = theApp->clientlist->GetBuddy()->GetIP();
1161 if( lastBuddyIP != m_lastBuddyIP ) {
1162 SetLastPublishTimeKadSrc( (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES, lastBuddyIP );
1163 return true;
1165 } else {
1166 return false;
1170 if(m_lastPublishTimeKadSrc > (uint32)time(NULL)) {
1171 return false;
1174 SetLastPublishTimeKadSrc((uint32)time(NULL)+KADEMLIAREPUBLISHTIMES,lastBuddyIP);
1175 return true;
1179 void CKnownFile::UpdatePartsInfo()
1181 // Cache part count
1182 uint16 partcount = GetPartCount();
1183 bool flag = (time(NULL) - m_nCompleteSourcesTime > 0);
1185 // Ensure the frequency-list is ready
1186 if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1187 m_AvailPartFrequency.clear();
1188 m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1191 if (flag) {
1192 ArrayOfUInts16 count;
1193 count.reserve(m_ClientUploadList.size());
1195 SourceSet::iterator it = m_ClientUploadList.begin();
1196 for ( ; it != m_ClientUploadList.end(); it++ ) {
1197 if ( !(*it)->GetUpPartStatus().empty() && (*it)->GetUpPartCount() == partcount ) {
1198 count.push_back((*it)->GetUpCompleteSourcesCount());
1202 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo = m_nCompleteSourcesCountHi = 0;
1204 if( partcount > 0) {
1205 m_nCompleteSourcesCount = m_AvailPartFrequency[0];
1207 for (uint16 i = 1; i < partcount; ++i) {
1208 if( m_nCompleteSourcesCount > m_AvailPartFrequency[i]) {
1209 m_nCompleteSourcesCount = m_AvailPartFrequency[i];
1212 count.push_back(m_nCompleteSourcesCount);
1214 int32 n = count.size();
1215 if (n > 0) {
1216 std::sort(count.begin(), count.end(), std::less<uint16>());
1218 // calculate range
1219 int i = n >> 1; // (n / 2)
1220 int j = (n * 3) >> 2; // (n * 3) / 4
1221 int k = (n * 7) >> 3; // (n * 7) / 8
1223 // For complete files, trust the people your uploading to more...
1225 // For low guess and normal guess count
1226 // - If we see more sources then the guessed low and
1227 // normal, use what we see.
1228 // - If we see less sources then the guessed low,
1229 // adjust network accounts for 100%, we account for
1230 // 0% with what we see and make sure we are still
1231 // above the normal.
1232 // For high guess
1233 // Adjust 100% network and 0% what we see.
1234 if (n < 20) {
1235 if ( count[i] < m_nCompleteSourcesCount ) {
1236 m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1237 } else {
1238 m_nCompleteSourcesCountLo = count[i];
1240 m_nCompleteSourcesCount= m_nCompleteSourcesCountLo;
1241 m_nCompleteSourcesCountHi = count[j];
1242 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1243 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1245 } else {
1246 // Many sources..
1247 // For low guess
1248 // Use what we see.
1249 // For normal guess
1250 // Adjust network accounts for 100%, we account for
1251 // 0% with what we see and make sure we are still above the low.
1252 // For high guess
1253 // Adjust network accounts for 100%, we account for 0%
1254 // with what we see and make sure we are still above the normal.
1256 m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1257 m_nCompleteSourcesCount = count[j];
1258 if( m_nCompleteSourcesCount < m_nCompleteSourcesCountLo ) {
1259 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo;
1261 m_nCompleteSourcesCountHi= count[k];
1262 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1263 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1267 m_nCompleteSourcesTime = time(NULL) + (60);
1270 Notify_SharedFilesUpdateItem(this);
1274 void CKnownFile::UpdateUpPartsFrequency( CUpDownClient* client, bool increment )
1276 if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1277 m_AvailPartFrequency.clear();
1278 m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1279 if ( !increment ) {
1280 return;
1284 const BitVector& freq = client->GetUpPartStatus();
1285 unsigned int size = freq.size();
1286 if ( size != m_AvailPartFrequency.size() ) {
1287 return;
1290 if ( increment ) {
1291 for ( unsigned int i = 0; i < size; ++i ) {
1292 if ( freq[i] ) {
1293 m_AvailPartFrequency[i]++;
1296 } else {
1297 for ( unsigned int i = 0; i < size; ++i ) {
1298 if ( freq[i] ) {
1299 m_AvailPartFrequency[i]--;
1305 void CKnownFile::ClearPriority() {
1306 if ( !m_bAutoUpPriority ) return;
1307 m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
1308 UpdateAutoUpPriority();
1311 void CKnownFile::SetFileName(const CPath& filename)
1313 CAbstractFile::SetFileName(filename);
1314 #ifndef CLIENT_GUI
1315 wordlist.clear();
1316 Kademlia::CSearchManager::GetWords(GetFileName().GetPrintable(), &wordlist);
1317 #endif
1320 #endif // CLIENT_GUI
1322 //For File Comment //
1323 void CKnownFile::LoadComment()
1325 #ifndef CLIENT_GUI
1326 wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1328 wxConfigBase* cfg = wxConfigBase::Get();
1330 m_strComment = cfg->Read( strCfgPath + wxT("Comment"), wxEmptyString);
1331 m_iRating = cfg->Read( strCfgPath + wxT("Rate"), 0l);
1332 m_bCommentLoaded = true;
1334 #else
1335 m_strComment = wxT("Comments are not allowed on remote gui yet");
1336 m_bCommentLoaded = true;
1337 m_iRating =0;
1338 #endif
1343 wxString CKnownFile::GetAICHMasterHash() const
1345 #ifdef CLIENT_GUI
1346 return m_AICHMasterHash;
1347 #else
1348 if (HasProperAICHHashSet()) {
1349 return m_pAICHHashSet->GetMasterHash().GetString();
1352 return wxEmptyString;
1353 #endif
1357 bool CKnownFile::HasProperAICHHashSet() const
1359 #ifdef CLIENT_GUI
1360 return m_AICHMasterHash.Length() != 0;
1361 #else
1362 return m_pAICHHashSet->HasValidMasterHash() &&
1363 (m_pAICHHashSet->GetStatus() == AICH_HASHSETCOMPLETE ||
1364 m_pAICHHashSet->GetStatus() == AICH_VERIFIED);
1365 #endif
1368 wxString CKnownFile::GetFeedback() const
1370 return wxString(_("File name")) + wxT(": ") + GetFileName().GetPrintable() + wxT("\n")
1371 + _("File size") + wxT(": ") + CastItoXBytes(GetFileSize()) + wxT("\n")
1372 + _("Share ratio") + wxString::Format(wxT(": %.2f%%\n"), (((double)statistic.GetAllTimeTransferred() / (double)GetFileSize()) * 100.0))
1373 + _("Uploaded") + wxT(": ") + CastItoXBytes(statistic.GetTransferred()) + wxT(" (") + CastItoXBytes(statistic.GetAllTimeTransferred()) + wxT(")\n")
1374 + _("Requested") + CFormat(wxT(": %u (%u)\n")) % statistic.GetRequests() % statistic.GetAllTimeRequests()
1375 + _("Accepted") + CFormat(wxT(": %u (%u)\n")) % statistic.GetAccepts() % statistic.GetAllTimeAccepts()
1376 + _("On Queue") + CFormat(wxT(": %u\n")) % GetQueuedCount()
1377 + _("Complete sources") + CFormat(wxT(": %u\n")) % m_nCompleteSourcesCount;
1379 // File_checked_for_headers