Upstream tarball 20080522
[amule.git] / src / KnownFile.cpp
blob02553deee07846c513f17616281f6b84502f2558
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 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
54 #include "CryptoPP_Inc.h" // Needed for MD4
56 #include <common/Format.h>
58 CFileStatistic::CFileStatistic() :
59 requested(0),
60 transferred(0),
61 accepted(0),
62 alltimerequested(0),
63 alltimetransferred(0),
64 alltimeaccepted(0)
68 #ifndef CLIENT_GUI
70 void CFileStatistic::AddRequest(){
71 requested++;
72 alltimerequested++;
73 theApp->knownfiles->requested++;
74 theApp->sharedfiles->UpdateItem(fileParent);
77 void CFileStatistic::AddAccepted(){
78 accepted++;
79 alltimeaccepted++;
80 theApp->knownfiles->accepted++;
81 theApp->sharedfiles->UpdateItem(fileParent);
84 void CFileStatistic::AddTransferred(uint64 bytes){
85 transferred += bytes;
86 alltimetransferred += bytes;
87 theApp->knownfiles->transferred += bytes;
88 theApp->sharedfiles->UpdateItem(fileParent);
91 #endif // CLIENT_GUI
94 /* Abstract File (base class)*/
96 CAbstractFile::CAbstractFile()
98 m_iRating(0),
99 m_hasComment(false),
100 m_iUserRating(0),
101 m_nFileSize(0)
106 CAbstractFile::CAbstractFile(const CAbstractFile& other)
108 m_abyFileHash(other.m_abyFileHash),
109 m_strComment(other.m_strComment),
110 m_iRating(other.m_iRating),
111 m_hasComment(other.m_hasComment),
112 m_iUserRating(other.m_iUserRating),
113 m_taglist(other.m_taglist),
114 m_nFileSize(other.m_nFileSize),
115 m_fileName(other.m_fileName)
117 /* // TODO: Currently it's not safe to duplicate the entries, but isn't needed either.
118 CKadEntryPtrList::const_iterator it = other.m_kadNotes.begin();
119 for (; it != other.m_kadNotes.end(); ++it) {
120 m_kadNotes.push_back(new Kademlia::CEntry(**it));
126 void CAbstractFile::SetFileName(const CPath& fileName)
128 m_fileName = fileName;
131 uint32 CAbstractFile::GetIntTagValue(uint8 tagname) const
133 ArrayOfCTag::const_iterator it = m_taglist.begin();
134 for (; it != m_taglist.end(); ++it){
135 if (((*it).GetNameID() == tagname) && (*it).IsInt()) {
136 return (*it).GetInt();
139 return 0;
142 bool CAbstractFile::GetIntTagValue(uint8 tagname, uint32& ruValue) const
144 ArrayOfCTag::const_iterator it = m_taglist.begin();
145 for (; it != m_taglist.end(); ++it){
146 if (((*it).GetNameID() == tagname) && (*it).IsInt()){
147 ruValue = (*it).GetInt();
148 return true;
151 return false;
154 uint32 CAbstractFile::GetIntTagValue(const wxString& tagname) const
156 ArrayOfCTag::const_iterator it = m_taglist.begin();
157 for (; it != m_taglist.end(); ++it){
158 if ((*it).IsInt() && ((*it).GetName() == tagname)) {
159 return (*it).GetInt();
162 return 0;
165 const wxString& CAbstractFile::GetStrTagValue(uint8 tagname) const
167 ArrayOfCTag::const_iterator it = m_taglist.begin();
168 for (; it != m_taglist.end(); ++it){
169 if ((*it).GetNameID() == tagname && (*it).IsStr()) {
170 return (*it).GetStr();
173 return EmptyString;
176 const wxString& CAbstractFile::GetStrTagValue(const wxString& tagname) const
178 ArrayOfCTag::const_iterator it = m_taglist.begin();
179 for (; it != m_taglist.end(); ++it){
180 if ((*it).IsStr() && ((*it).GetName() == tagname)) {
181 return (*it).GetStr();
184 return EmptyString;
187 const CTag *CAbstractFile::GetTag(uint8 tagname, uint8 tagtype) const
189 ArrayOfCTag::const_iterator it = m_taglist.begin();
190 for (; it != m_taglist.end(); ++it){
191 if ((*it).GetNameID() == tagname && (*it).GetType() == tagtype) {
192 return &(*it);
195 return NULL;
198 const CTag *CAbstractFile::GetTag(const wxString& tagname, uint8 tagtype) const
200 ArrayOfCTag::const_iterator it = m_taglist.begin();
201 for (; it != m_taglist.end(); ++it){
202 if ((*it).GetType() == tagtype && (*it).GetName() == tagname) {
203 return &(*it);
206 return NULL;
209 const CTag *CAbstractFile::GetTag(uint8 tagname) const
211 ArrayOfCTag::const_iterator it = m_taglist.begin();
212 for (; it != m_taglist.end(); ++it){
213 if ((*it).GetNameID() == tagname) {
214 return &(*it);
217 return NULL;
220 const CTag *CAbstractFile::GetTag(const wxString& tagname) const
222 ArrayOfCTag::const_iterator it = m_taglist.begin();
223 for (; it != m_taglist.end(); ++it){
224 if ((*it).GetName() == tagname) {
225 return &(*it);
228 return NULL;
231 void CAbstractFile::AddTagUnique(const CTag &rTag)
233 ArrayOfCTag::iterator it = m_taglist.begin();
234 for (; it != m_taglist.end(); ++it) {
235 if ( ( ((*it).GetNameID() != 0 &&
236 (*it).GetNameID() == rTag.GetNameID()) ||
237 (!(*it).GetName().IsEmpty() &&
238 !rTag.GetName().IsEmpty() &&
239 (*it).GetName() == rTag.GetName()) ) &&
240 (*it).GetType() == rTag.GetType()){
241 m_taglist.erase(it);
242 m_taglist.insert(it, rTag);
243 return;
246 m_taglist.push_back(rTag);
249 #ifndef CLIENT_GUI
250 void CAbstractFile::AddNote(Kademlia::CEntry *pEntry)
252 CKadEntryPtrList::iterator it = m_kadNotes.begin();
253 for (; it != m_kadNotes.end(); ++it) {
254 Kademlia::CEntry* entry = *it;
255 if(entry->m_uIP == pEntry->m_uIP || entry->m_uSourceID == pEntry->m_uSourceID) {
256 delete pEntry;
257 return;
260 m_kadNotes.push_front(pEntry);
262 #else
263 void CAbstractFile::AddNote(Kademlia::CEntry *)
266 #endif
269 /* Known File */
271 CKnownFile::CKnownFile()
273 Init();
275 m_bAutoUpPriority = thePrefs::GetNewAutoUp();
276 m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
280 //#warning Experimental: Construct a CKnownFile from a CSearchFile
281 CKnownFile::CKnownFile(const CSearchFile &searchFile)
283 // This will copy the file hash
284 CAbstractFile(static_cast<const CAbstractFile &>(searchFile))
286 Init();
288 // Use CKnownFile::SetFileName()
289 SetFileName(searchFile.GetFileName());
291 // Use CKnownFile::SetFileSize()
292 SetFileSize(searchFile.GetFileSize());
294 m_bAutoUpPriority = thePrefs::GetNewAutoUp();
295 m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
299 void CKnownFile::Init()
301 m_nCompleteSourcesTime = time(NULL);
302 m_nCompleteSourcesCount = 0;
303 m_nCompleteSourcesCountLo = 0;
304 m_nCompleteSourcesCountHi = 0;
305 m_bCommentLoaded = false;
306 m_iPartCount = 0;
307 m_iED2KPartCount = 0;
308 m_iED2KPartHashCount = 0;
309 m_PublishedED2K = false;
310 kadFileSearchID = 0;
311 m_lastPublishTimeKadSrc = 0;
312 m_lastPublishTimeKadNotes = 0;
313 m_lastBuddyIP = 0;
314 m_lastDateChanged = 0;
316 statistic.fileParent = this;
318 #ifndef CLIENT_GUI
319 m_pAICHHashSet = new CAICHHashSet(this);
320 #endif
324 #ifdef CLIENT_GUI
326 CKnownFile::CKnownFile(CEC_SharedFile_Tag *tag)
328 Init();
330 SetFileName(CPath(tag->FileName()));
331 m_abyFileHash = tag->ID();
332 SetFileSize(tag->SizeFull());
333 m_iPartCount = (GetFileSize() + (PARTSIZE - 1)) / PARTSIZE;
334 m_AvailPartFrequency.insert(m_AvailPartFrequency.end(), m_iPartCount, 0);
335 m_iUpPriority = tag->Prio();
336 if ( m_iUpPriority >= 10 ) {
337 m_iUpPriority-= 10;
338 m_bAutoUpPriority = true;
339 } else {
340 m_bAutoUpPriority = false;
343 m_AICHMasterHash = tag->GetAICHHash();
346 CKnownFile::~CKnownFile()
350 #else // ! CLIENT_GUI
352 CKnownFile::~CKnownFile()
354 SourceSet::iterator it = m_ClientUploadList.begin();
355 for ( ; it != m_ClientUploadList.end(); ++it ) {
356 (*it)->ClearUploadFileID();
359 delete m_pAICHHashSet;
362 void CKnownFile::AddUploadingClient(CUpDownClient* client)
364 m_ClientUploadList.insert(client);
366 UpdateAutoUpPriority();
370 void CKnownFile::RemoveUploadingClient(CUpDownClient* client)
372 if (m_ClientUploadList.erase(client)) {
373 UpdateAutoUpPriority();
378 void CKnownFile::SetFilePath(const CPath& filePath)
380 m_filePath = filePath;
384 void CKnownFile::SetFileSize(uint64 nFileSize)
386 CAbstractFile::SetFileSize(nFileSize);
387 m_pAICHHashSet->SetFileSize(nFileSize);
389 // Examples of parthashs, hashsets and filehashs for different filesizes
390 // according the ed2k protocol
391 //----------------------------------------------------------------------
393 //File size: 3 bytes
394 //File hash: 2D55E87D0E21F49B9AD25F98531F3724
395 //Nr. hashs: 0
398 //File size: 1*PARTSIZE
399 //File hash: A72CA8DF7F07154E217C236C89C17619
400 //Nr. hashs: 2
401 //Hash[ 0]: 4891ED2E5C9C49F442145A3A5F608299
402 //Hash[ 1]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
405 //File size: 1*PARTSIZE + 1 byte
406 //File hash: 2F620AE9D462CBB6A59FE8401D2B3D23
407 //Nr. hashs: 2
408 //Hash[ 0]: 121795F0BEDE02DDC7C5426D0995F53F
409 //Hash[ 1]: C329E527945B8FE75B3C5E8826755747
412 //File size: 2*PARTSIZE
413 //File hash: A54C5E562D5E03CA7D77961EB9A745A4
414 //Nr. hashs: 3
415 //Hash[ 0]: B3F5CE2A06BF403BFB9BFFF68BDDC4D9
416 //Hash[ 1]: 509AA30C9EA8FC136B1159DF2F35B8A9
417 //Hash[ 2]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
420 //File size: 3*PARTSIZE
421 //File hash: 5E249B96F9A46A18FC2489B005BF2667
422 //Nr. hashs: 4
423 //Hash[ 0]: 5319896A2ECAD43BF17E2E3575278E72
424 //Hash[ 1]: D86EF157D5E49C5ED502EDC15BB5F82B
425 //Hash[ 2]: 10F2D5B1FCB95C0840519C58D708480F
426 //Hash[ 3]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
429 //File size: 3*PARTSIZE + 1 byte
430 //File hash: 797ED552F34380CAFF8C958207E40355
431 //Nr. hashs: 4
432 //Hash[ 0]: FC7FD02CCD6987DCF1421F4C0AF94FB8
433 //Hash[ 1]: 2FE466AF8A7C06DA3365317B75A5ACFE
434 //Hash[ 2]: 873D3BF52629F7C1527C6E8E473C1C30
435 //Hash[ 3]: BCE50BEE7877BB07BB6FDA56BFE142FB
438 // File size Data parts ED2K parts ED2K part hashs
439 // ---------------------------------------------------------------
440 // 1..PARTSIZE-1 1 1 0(!)
441 // PARTSIZE 1 2(!) 2(!)
442 // PARTSIZE+1 2 2 2
443 // PARTSIZE*2 2 3(!) 3(!)
444 // PARTSIZE*2+1 3 3 3
446 if (nFileSize == 0){
447 //wxASSERT(0); // Kry - Why commented out by lemonfan? it can never be 0
448 m_iPartCount = 0;
449 m_iED2KPartCount = 0;
450 m_iED2KPartHashCount = 0;
451 return;
454 // nr. of data parts
455 m_iPartCount = (nFileSize + (PARTSIZE - 1)) / PARTSIZE;
457 // nr. of parts to be used with OP_FILESTATUS
458 m_iED2KPartCount = nFileSize / PARTSIZE + 1;
460 // nr. of parts to be used with OP_HASHSETANSWER
461 m_iED2KPartHashCount = nFileSize / PARTSIZE;
462 if (m_iED2KPartHashCount != 0) {
463 m_iED2KPartHashCount += 1;
468 // needed for memfiles. its probably better to switch everything to CFile...
469 bool CKnownFile::LoadHashsetFromFile(const CFileDataIO* file, bool checkhash)
471 CMD4Hash checkid = file->ReadHash();
473 uint16 parts = file->ReadUInt16();
474 for (uint16 i = 0; i < parts; ++i){
475 CMD4Hash cur_hash = file->ReadHash();
476 m_hashlist.push_back(cur_hash);
479 // SLUGFILLER: SafeHash - always check for valid m_hashlist
480 if (!checkhash){
481 m_abyFileHash = checkid;
482 if (parts <= 1) { // nothing to check
483 return true;
485 } else {
486 if ( m_abyFileHash != checkid ) {
487 return false; // wrong file?
488 } else {
489 if (parts != GetED2KPartHashCount()) {
490 return false;
494 // SLUGFILLER: SafeHash
496 // trust noone ;-)
497 // lol, useless comment but made me lmao
498 // wtf you guys are weird.
500 if (!m_hashlist.empty()) {
501 CreateHashFromHashlist(m_hashlist, &checkid);
504 if ( m_abyFileHash == checkid ) {
505 return true;
506 } else {
507 m_hashlist.clear();
508 return false;
513 bool CKnownFile::LoadTagsFromFile(const CFileDataIO* file)
515 uint32 tagcount = file->ReadUInt32();
516 for (uint32 j = 0; j != tagcount; ++j) {
517 CTag newtag(*file, true);
518 switch(newtag.GetNameID()){
519 case FT_FILENAME:
520 if (GetFileName().IsOk()) {
521 // Unlike eMule, we actually prefer the second
522 // filename tag, since we use it to specify the
523 // 'universial' filename (see CPath::ToUniv).
524 CPath path = CPath::FromUniv(newtag.GetStr());
526 // May be invalid, if from older versions where
527 // unicoded filenames be saved as empty-strings.
528 if (path.IsOk()) {
529 SetFileName(path);
531 } else {
532 SetFileName(CPath(newtag.GetStr()));
534 break;
536 case FT_FILESIZE:
537 SetFileSize(newtag.GetInt());
538 m_AvailPartFrequency.clear();
539 m_AvailPartFrequency.insert(
540 m_AvailPartFrequency.begin(),
541 GetPartCount(), 0);
542 break;
544 case FT_ATTRANSFERRED:
545 statistic.alltimetransferred += newtag.GetInt();
546 break;
548 case FT_ATTRANSFERREDHI:
549 statistic.alltimetransferred =
550 (((uint64)newtag.GetInt()) << 32) +
551 ((uint64)statistic.alltimetransferred);
552 break;
554 case FT_ATREQUESTED:
555 statistic.alltimerequested = newtag.GetInt();
556 break;
558 case FT_ATACCEPTED:
559 statistic.alltimeaccepted = newtag.GetInt();
560 break;
562 case FT_ULPRIORITY:
563 m_iUpPriority = newtag.GetInt();
564 if( m_iUpPriority == PR_AUTO ){
565 m_iUpPriority = PR_HIGH;
566 m_bAutoUpPriority = true;
567 } else {
568 if ( m_iUpPriority != PR_VERYLOW &&
569 m_iUpPriority != PR_LOW &&
570 m_iUpPriority != PR_NORMAL &&
571 m_iUpPriority != PR_HIGH &&
572 m_iUpPriority != PR_VERYHIGH &&
573 m_iUpPriority != PR_POWERSHARE) {
574 m_iUpPriority = PR_NORMAL;
577 m_bAutoUpPriority = false;
579 break;
581 case FT_PERMISSIONS:
582 // Ignore it, it's not used anymore.
583 break;
585 case FT_AICH_HASH: {
586 CAICHHash hash;
587 bool hashSizeOk =
588 hash.DecodeBase32(newtag.GetStr()) == CAICHHash::GetHashSize();
589 wxASSERT(hashSizeOk);
590 if (hashSizeOk) {
591 m_pAICHHashSet->SetMasterHash(hash, AICH_HASHSETCOMPLETE);
593 break;
596 case FT_KADLASTPUBLISHSRC:
597 SetLastPublishTimeKadSrc( newtag.GetInt(), 0 );
599 if(GetLastPublishTimeKadSrc() > (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES) {
600 //There may be a posibility of an older client that saved a random number here.. This will check for that..
601 SetLastPublishTimeKadSrc(0, 0);
603 break;
605 case FT_KADLASTPUBLISHNOTES:
606 SetLastPublishTimeKadNotes( newtag.GetInt() );
607 break;
609 case FT_KADLASTPUBLISHKEY:
610 // Just purge it
611 wxASSERT( newtag.IsInt() );
612 break;
614 default:
615 // Store them here and write them back on saving.
616 m_taglist.push_back(newtag);
620 return true;
624 bool CKnownFile::LoadDateFromFile(const CFileDataIO* file)
626 m_lastDateChanged = file->ReadUInt32();
628 return true;
632 bool CKnownFile::LoadFromFile(const CFileDataIO* file)
634 // SLUGFILLER: SafeHash - load first, verify later
635 bool ret1 = LoadDateFromFile(file);
636 bool ret2 = LoadHashsetFromFile(file,false);
637 bool ret3 = LoadTagsFromFile(file);
638 UpdatePartsInfo();
639 // Final hash-count verification, needs to be done after the tags are loaded.
640 return ret1 && ret2 && ret3 && GetED2KPartHashCount()==GetHashCount();
641 // SLUGFILLER: SafeHash
645 bool CKnownFile::WriteToFile(CFileDataIO* file)
647 wxCHECK(!IsPartFile(), false);
649 // date
650 file->WriteUInt32(m_lastDateChanged);
651 // hashset
652 file->WriteHash(m_abyFileHash);
654 uint16 parts = m_hashlist.size();
655 file->WriteUInt16(parts);
657 for (int i = 0; i < parts; ++i)
658 file->WriteHash(m_hashlist[i]);
660 //tags
661 const int iFixedTags = 8;
662 uint32 tagcount = iFixedTags;
663 if (HasProperAICHHashSet()) {
664 tagcount++;
666 // Float meta tags are currently not written. All older eMule versions < 0.28a have
667 // a bug in the meta tag reading+writing code. To achive maximum backward
668 // compatibility for met files with older eMule versions we just don't write float
669 // tags. This is OK, because we (eMule) do not use float tags. The only float tags
670 // we may have to handle is the '# Sent' tag from the Hybrid, which is pretty
671 // useless but may be received from us via the servers.
673 // The code for writing the float tags SHOULD BE ENABLED in SOME MONTHS (after most
674 // people are using the newer eMule versions which do not write broken float tags).
675 for (size_t j = 0; j < m_taglist.size(); ++j){
676 if (m_taglist[j].IsInt() || m_taglist[j].IsStr()) {
677 ++tagcount;
681 if (m_lastPublishTimeKadSrc) {
682 ++tagcount;
685 if (m_lastPublishTimeKadNotes){
686 ++tagcount;
689 // standard tags
691 file->WriteUInt32(tagcount);
693 // We still save the unicoded filename, for backwards
694 // compatibility with pre-2.2 and other clients.
695 CTagString nametag_unicode(FT_FILENAME, GetFileName().GetRaw());
696 // We write it with BOM to kep eMule compatibility
697 nametag_unicode.WriteTagToFile(file,utf8strOptBOM);
699 // The non-unicoded filename is written in an 'universial'
700 // format, which allows us to identify files, even if the
701 // system locale changes.
702 CTagString nametag(FT_FILENAME, CPath::ToUniv(GetFileName()));
703 nametag.WriteTagToFile(file);
705 CTagIntSized sizetag(FT_FILESIZE, GetFileSize(), IsLargeFile() ? 64 : 32);
706 sizetag.WriteTagToFile(file);
708 // statistic
709 uint32 tran;
710 tran=statistic.alltimetransferred & 0xFFFFFFFF;
711 CTagInt32 attag1(FT_ATTRANSFERRED, tran);
712 attag1.WriteTagToFile(file);
714 tran=statistic.alltimetransferred>>32;
715 CTagInt32 attag4(FT_ATTRANSFERREDHI, tran);
716 attag4.WriteTagToFile(file);
718 CTagInt32 attag2(FT_ATREQUESTED, statistic.GetAllTimeRequests());
719 attag2.WriteTagToFile(file);
721 CTagInt32 attag3(FT_ATACCEPTED, statistic.GetAllTimeAccepts());
722 attag3.WriteTagToFile(file);
724 // priority N permission
725 CTagInt32 priotag(FT_ULPRIORITY, IsAutoUpPriority() ? PR_AUTO : m_iUpPriority);
726 priotag.WriteTagToFile(file);
728 //AICH Filehash
729 if (HasProperAICHHashSet()) {
730 CTagString aichtag(FT_AICH_HASH, m_pAICHHashSet->GetMasterHash().GetString());
731 aichtag.WriteTagToFile(file);
734 // Kad sources
735 if (m_lastPublishTimeKadSrc){
736 CTagInt32 kadLastPubSrc(FT_KADLASTPUBLISHSRC, m_lastPublishTimeKadSrc);
737 kadLastPubSrc.WriteTagToFile(file);
740 // Kad notes
741 if (m_lastPublishTimeKadNotes){
742 CTagInt32 kadLastPubNotes(FT_KADLASTPUBLISHNOTES, m_lastPublishTimeKadNotes);
743 kadLastPubNotes.WriteTagToFile(file);
746 //other tags
747 for (size_t j = 0; j < m_taglist.size(); ++j){
748 if (m_taglist[j].IsInt() || m_taglist[j].IsStr()) {
749 m_taglist[j].WriteTagToFile(file);
752 return true;
756 void CKnownFile::CreateHashFromHashlist(const ArrayOfCMD4Hash& hashes, CMD4Hash* Output)
758 wxCHECK_RET(hashes.size(), wxT("No input to hash from in CreateHashFromHashlist"));
760 std::vector<byte> buffer(hashes.size() * MD4HASH_LENGTH);
761 std::vector<byte>::iterator it = buffer.begin();
763 for (size_t i = 0; i < hashes.size(); ++i) {
764 it = STLCopy_n(hashes[i].GetHash(), MD4HASH_LENGTH, it);
767 CreateHashFromInput(&buffer[0], buffer.size(), Output, NULL);
771 void CKnownFile::CreateHashFromFile(CFileDataIO* file, uint32 Length, CMD4Hash* Output, CAICHHashTree* pShaHashOut)
773 wxCHECK_RET(file && Length, wxT("No input to hash from in CreateHashFromFile"));
775 std::vector<byte> buffer(Length);
776 file->Read(&buffer[0], Length);
778 CreateHashFromInput(&buffer[0], Length, Output, pShaHashOut);
782 void CKnownFile::CreateHashFromInput(const byte* input, uint32 Length, CMD4Hash* Output, CAICHHashTree* pShaHashOut )
784 wxASSERT_MSG(Output || pShaHashOut, wxT("Nothing to do in CreateHashFromInput"));
785 wxCHECK_RET(input, wxT("No input to hash from in CreateHashFromInput"));
786 wxASSERT(Length <= PARTSIZE); // We never hash more than one PARTSIZE
788 CMemFile data(input, Length);
790 uint32 Required = Length;
791 byte X[64*128];
793 uint32 posCurrentEMBlock = 0;
794 uint32 nIACHPos = 0;
795 CScopedPtr<CAICHHashAlgo> pHashAlg(CAICHHashSet::GetNewHashAlgo());
797 // This is all AICH.
798 while (Required >= 64) {
799 uint32 len = Required / 64;
800 if (len > sizeof(X)/(64 * sizeof(X[0]))) {
801 len = sizeof(X)/(64 * sizeof(X[0]));
804 data.Read(&X, len * 64);
806 // SHA hash needs 180KB blocks
807 if (pShaHashOut) {
808 if (nIACHPos + len*64 >= EMBLOCKSIZE) {
809 uint32 nToComplete = EMBLOCKSIZE - nIACHPos;
810 pHashAlg->Add(X, nToComplete);
811 wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE );
812 pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg.get());
813 posCurrentEMBlock += EMBLOCKSIZE;
814 pHashAlg->Reset();
815 pHashAlg->Add(X+nToComplete,(len*64) - nToComplete);
816 nIACHPos = (len*64) - nToComplete;
818 else{
819 pHashAlg->Add(X, len*64);
820 nIACHPos += len*64;
824 Required -= len*64;
826 // bytes to read
827 Required = Length % 64;
828 if (Required != 0){
829 data.Read(&X,Required);
831 if (pShaHashOut != NULL){
832 if (nIACHPos + Required >= EMBLOCKSIZE){
833 uint32 nToComplete = EMBLOCKSIZE - nIACHPos;
834 pHashAlg->Add(X, nToComplete);
835 wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE );
836 pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg.get());
837 posCurrentEMBlock += EMBLOCKSIZE;
838 pHashAlg->Reset();
839 pHashAlg->Add(X+nToComplete, Required - nToComplete);
840 nIACHPos = Required - nToComplete;
842 else{
843 pHashAlg->Add(X, Required);
844 nIACHPos += Required;
848 if (pShaHashOut != NULL){
849 if(nIACHPos > 0){
850 pShaHashOut->SetBlockHash(nIACHPos, posCurrentEMBlock, pHashAlg.get());
851 posCurrentEMBlock += nIACHPos;
853 wxASSERT( posCurrentEMBlock == Length );
854 wxCHECK2( pShaHashOut->ReCalculateHash(pHashAlg.get(), false), );
857 if (Output != NULL){
858 #ifdef __WEAK_CRYPTO__
859 CryptoPP::Weak::MD4 md4_hasher;
860 #else
861 CryptoPP::MD4 md4_hasher;
862 #endif
863 md4_hasher.CalculateDigest(Output->GetHash(), input, Length);
868 const CMD4Hash& CKnownFile::GetPartHash(uint16 part) const {
869 wxASSERT( part < m_hashlist.size() );
871 return m_hashlist[part];
874 CPacket* CKnownFile::CreateSrcInfoPacket(const CUpDownClient* forClient, uint8 byRequestedVersion, uint16 nRequestedOptions)
876 // Kad reviewed
878 if (m_ClientUploadList.empty()) {
879 return NULL;
882 if ((((CKnownFile*)forClient->GetRequestFile() != this)
883 && ((CKnownFile*)forClient->GetUploadFile() != this)) || forClient->GetUploadFileID() != GetFileHash()) {
884 wxString file1 = _("Unknown");
885 if (forClient->GetRequestFile() && forClient->GetRequestFile()->GetFileName().IsOk()) {
886 file1 = forClient->GetRequestFile()->GetFileName().GetPrintable();
887 } else if (forClient->GetUploadFile() && forClient->GetUploadFile()->GetFileName().IsOk()) {
888 file1 = forClient->GetUploadFile()->GetFileName().GetPrintable();
890 wxString file2 = _("Unknown");
891 if (GetFileName().IsOk()) {
892 file2 = GetFileName().GetPrintable();
894 AddDebugLogLineM(false, logKnownFiles, wxT("File missmatch on source packet (K) Sending: ") + file1 + wxT(" From: ") + file2);
895 return NULL;
898 const BitVector& rcvstatus = forClient->GetUpPartStatus();
899 bool SupportsUploadChunksState = !rcvstatus.empty();
900 //wxASSERT(rcvstatus.size() == GetPartCount()); // Obviously!
901 if (rcvstatus.size() != GetPartCount()) {
902 // Yuck. Same file but different part count? Seriously fucked up.
903 AddDebugLogLineM(false, logKnownFiles, wxString::Format(wxT("Impossible situation: different partcounts for the same known file: %i (client) and %i (file)"),rcvstatus.size(),GetPartCount()));
904 return NULL;
907 CMemFile data(1024);
909 uint8 byUsedVersion;
910 bool bIsSX2Packet;
911 if (forClient->SupportsSourceExchange2() && byRequestedVersion > 0){
912 // the client uses SourceExchange2 and requested the highest version he knows
913 // and we send the highest version we know, but of course not higher than his request
914 byUsedVersion = std::min(byRequestedVersion, (uint8)SOURCEEXCHANGE2_VERSION);
915 bIsSX2Packet = true;
916 data.WriteUInt8(byUsedVersion);
918 // we don't support any special SX2 options yet, reserved for later use
919 if (nRequestedOptions != 0) {
920 AddDebugLogLineM(false, logKnownFiles, CFormat(wxT("Client requested unknown options for SourceExchange2: %u")) % nRequestedOptions);
922 } else {
923 byUsedVersion = forClient->GetSourceExchange1Version();
924 bIsSX2Packet = false;
925 if (forClient->SupportsSourceExchange2()) {
926 AddDebugLogLineM(false, logKnownFiles, wxT("Client which announced to support SX2 sent SX1 packet instead"));
930 uint16 nCount = 0;
932 data.WriteHash(forClient->GetUploadFileID());
933 data.WriteUInt16(nCount);
934 uint32 cDbgNoSrc = 0;
936 SourceSet::iterator it = m_ClientUploadList.begin();
937 for ( ; it != m_ClientUploadList.end(); it++ ) {
938 const CUpDownClient *cur_src = *it;
940 if ( cur_src->HasLowID() ||
941 cur_src == forClient ||
942 !( cur_src->GetUploadState() == US_UPLOADING ||
943 cur_src->GetUploadState() == US_ONUPLOADQUEUE)) {
944 continue;
947 bool bNeeded = false;
949 if ( SupportsUploadChunksState ) {
950 const BitVector& srcstatus = cur_src->GetUpPartStatus();
951 if ( !srcstatus.empty() ) {
952 //wxASSERT(srcstatus.size() == GetPartCount()); // Obviously!
953 if (srcstatus.size() != GetPartCount()) {
954 continue;
956 if ( cur_src->GetUpPartCount() == forClient->GetUpPartCount() ) {
957 for (int x = 0; x < GetPartCount(); x++ ) {
958 if ( srcstatus.at(x) && !rcvstatus.at(x) ) {
959 // We know the receiving client needs
960 // a chunk from this client.
961 bNeeded = true;
962 break;
966 } else {
967 cDbgNoSrc++;
968 // This client doesn't support upload chunk status.
969 // So just send it and hope for the best.
970 bNeeded = true;
972 } else {
973 // remote client does not support upload chunk status,
974 // search sources which have at least one complete part
975 // we could even sort the list of sources by available
976 // chunks to return as much sources as possible which
977 // have the most available chunks. but this could be
978 // a noticeable performance problem.
979 const BitVector& srcstatus = cur_src->GetUpPartStatus();
980 if ( !srcstatus.empty() ) {
981 //wxASSERT(srcstatus.size() == GetPartCount());
982 if (srcstatus.size() != GetPartCount()) {
983 continue;
985 for (int x = 0; x < GetPartCount(); x++ ) {
986 if ( srcstatus.at(x) ) {
987 // this client has at least one chunk
988 bNeeded = true;
989 break;
992 } else {
993 // This client doesn't support upload chunk status.
994 // So just send it and hope for the best.
995 bNeeded = true;
999 if ( bNeeded ) {
1000 nCount++;
1001 uint32 dwID;
1002 if(byUsedVersion >= 3) {
1003 dwID = cur_src->GetUserIDHybrid();
1004 } else {
1005 dwID = cur_src->GetIP();
1007 data.WriteUInt32(dwID);
1008 data.WriteUInt16(cur_src->GetUserPort());
1009 data.WriteUInt32(cur_src->GetServerIP());
1010 data.WriteUInt16(cur_src->GetServerPort());
1012 if (byUsedVersion >= 2) {
1013 data.WriteHash(cur_src->GetUserHash());
1016 if (byUsedVersion >= 4){
1017 // CryptSettings - SourceExchange V4
1018 // 5 Reserved (!)
1019 // 1 CryptLayer Required
1020 // 1 CryptLayer Requested
1021 // 1 CryptLayer Supported
1022 const uint8 uSupportsCryptLayer = cur_src->SupportsCryptLayer() ? 1 : 0;
1023 const uint8 uRequestsCryptLayer = cur_src->RequestsCryptLayer() ? 1 : 0;
1024 const uint8 uRequiresCryptLayer = cur_src->RequiresCryptLayer() ? 1 : 0;
1025 const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0);
1026 data.WriteUInt8(byCryptOptions);
1029 if (nCount > 500) {
1030 break;
1035 if (!nCount) {
1036 return 0;
1039 data.Seek(bIsSX2Packet ? 17 : 16, wxFromStart);
1040 data.WriteUInt16(nCount);
1042 CPacket* result = new CPacket(data, OP_EMULEPROT, bIsSX2Packet ? OP_ANSWERSOURCES2 : OP_ANSWERSOURCES);
1044 if ( result->GetPacketSize() > 354 ) {
1045 result->PackPacket();
1048 return result;
1052 // Updates priority of file if autopriority is activated
1053 void CKnownFile::UpdateAutoUpPriority()
1055 if (IsAutoUpPriority()) {
1056 uint32 queued = GetQueuedCount();
1057 uint8 priority = PR_NORMAL;
1059 if (queued > 20) {
1060 priority = PR_LOW;
1061 } else if (queued > 1) {
1062 priority = PR_NORMAL;
1063 } else {
1064 priority = PR_HIGH;
1067 if (GetUpPriority() != priority) {
1068 SetUpPriority(priority, false);
1069 Notify_SharedFilesUpdateItem(this);
1074 void CKnownFile::SetFileComment(const wxString& strNewComment)
1076 if (m_strComment != strNewComment) {
1077 SetLastPublishTimeKadNotes(0);
1078 wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1080 wxConfigBase* cfg = wxConfigBase::Get();
1081 cfg->Write( strCfgPath + wxT("Comment"), strNewComment);
1083 m_strComment = strNewComment;
1085 SourceSet::iterator it = m_ClientUploadList.begin();
1086 for ( ; it != m_ClientUploadList.end(); it++ ) {
1087 (*it)->SetCommentDirty();
1093 // For File rate
1094 void CKnownFile::SetFileRating(int8 iNewRating)
1096 if (m_iRating != iNewRating) {
1097 SetLastPublishTimeKadNotes(0);
1098 wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1099 wxConfigBase* cfg = wxConfigBase::Get();
1100 cfg->Write( strCfgPath + wxT("Rate"), (int)iNewRating);
1101 m_iRating = iNewRating;
1103 SourceSet::iterator it = m_ClientUploadList.begin();
1104 for ( ; it != m_ClientUploadList.end(); it++ ) {
1105 (*it)->SetCommentDirty();
1111 void CKnownFile::SetUpPriority(uint8 iNewUpPriority, bool m_bsave){
1112 m_iUpPriority = iNewUpPriority;
1113 if( IsPartFile() && m_bsave ) {
1114 ((CPartFile*)this)->SavePartFile();
1118 void CKnownFile::SetPublishedED2K(bool val){
1119 m_PublishedED2K = val;
1120 Notify_SharedFilesUpdateItem(this);
1123 bool CKnownFile::PublishNotes()
1125 if(m_lastPublishTimeKadNotes > (uint32)time(NULL)) {
1126 return false;
1129 if(!GetFileComment().IsEmpty()) {
1130 m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1131 return true;
1134 if(GetFileRating() != 0) {
1135 m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1136 return true;
1139 return false;
1142 bool CKnownFile::PublishSrc()
1144 uint32 lastBuddyIP = 0;
1146 if( theApp->IsFirewalled() ) {
1147 CUpDownClient* buddy = theApp->clientlist->GetBuddy();
1148 if( buddy ) {
1149 lastBuddyIP = theApp->clientlist->GetBuddy()->GetIP();
1150 if( lastBuddyIP != m_lastBuddyIP ) {
1151 SetLastPublishTimeKadSrc( (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES, lastBuddyIP );
1152 return true;
1154 } else {
1155 return false;
1159 if(m_lastPublishTimeKadSrc > (uint32)time(NULL)) {
1160 return false;
1163 SetLastPublishTimeKadSrc((uint32)time(NULL)+KADEMLIAREPUBLISHTIMES,lastBuddyIP);
1164 return true;
1168 void CKnownFile::UpdatePartsInfo()
1170 // Cache part count
1171 uint16 partcount = GetPartCount();
1172 bool flag = (time(NULL) - m_nCompleteSourcesTime > 0);
1174 // Ensure the frequency-list is ready
1175 if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1176 m_AvailPartFrequency.clear();
1177 m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1180 if (flag) {
1181 ArrayOfUInts16 count;
1182 count.reserve(m_ClientUploadList.size());
1184 SourceSet::iterator it = m_ClientUploadList.begin();
1185 for ( ; it != m_ClientUploadList.end(); it++ ) {
1186 if ( !(*it)->GetUpPartStatus().empty() && (*it)->GetUpPartCount() == partcount ) {
1187 count.push_back((*it)->GetUpCompleteSourcesCount());
1191 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo = m_nCompleteSourcesCountHi = 0;
1193 if( partcount > 0) {
1194 m_nCompleteSourcesCount = m_AvailPartFrequency[0];
1196 for (uint16 i = 1; i < partcount; ++i) {
1197 if( m_nCompleteSourcesCount > m_AvailPartFrequency[i]) {
1198 m_nCompleteSourcesCount = m_AvailPartFrequency[i];
1201 count.push_back(m_nCompleteSourcesCount);
1203 int32 n = count.size();
1204 if (n > 0) {
1205 std::sort(count.begin(), count.end(), std::less<uint16>());
1207 // calculate range
1208 int i = n >> 1; // (n / 2)
1209 int j = (n * 3) >> 2; // (n * 3) / 4
1210 int k = (n * 7) >> 3; // (n * 7) / 8
1212 // For complete files, trust the people your uploading to more...
1214 // For low guess and normal guess count
1215 // - If we see more sources then the guessed low and
1216 // normal, use what we see.
1217 // - If we see less sources then the guessed low,
1218 // adjust network accounts for 100%, we account for
1219 // 0% with what we see and make sure we are still
1220 // above the normal.
1221 // For high guess
1222 // Adjust 100% network and 0% what we see.
1223 if (n < 20) {
1224 if ( count[i] < m_nCompleteSourcesCount ) {
1225 m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1226 } else {
1227 m_nCompleteSourcesCountLo = count[i];
1229 m_nCompleteSourcesCount= m_nCompleteSourcesCountLo;
1230 m_nCompleteSourcesCountHi = count[j];
1231 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1232 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1234 } else {
1235 // Many sources..
1236 // For low guess
1237 // Use what we see.
1238 // For normal guess
1239 // Adjust network accounts for 100%, we account for
1240 // 0% with what we see and make sure we are still above the low.
1241 // For high guess
1242 // Adjust network accounts for 100%, we account for 0%
1243 // with what we see and make sure we are still above the normal.
1245 m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1246 m_nCompleteSourcesCount = count[j];
1247 if( m_nCompleteSourcesCount < m_nCompleteSourcesCountLo ) {
1248 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo;
1250 m_nCompleteSourcesCountHi= count[k];
1251 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1252 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1256 m_nCompleteSourcesTime = time(NULL) + (60);
1259 Notify_SharedFilesUpdateItem(this);
1263 void CKnownFile::UpdateUpPartsFrequency( CUpDownClient* client, bool increment )
1265 if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1266 m_AvailPartFrequency.clear();
1267 m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1268 if ( !increment ) {
1269 return;
1273 const BitVector& freq = client->GetUpPartStatus();
1274 unsigned int size = freq.size();
1275 if ( size != m_AvailPartFrequency.size() ) {
1276 return;
1279 if ( increment ) {
1280 for ( unsigned int i = 0; i < size; ++i ) {
1281 if ( freq[i] ) {
1282 m_AvailPartFrequency[i]++;
1285 } else {
1286 for ( unsigned int i = 0; i < size; ++i ) {
1287 if ( freq[i] ) {
1288 m_AvailPartFrequency[i]--;
1294 void CKnownFile::ClearPriority() {
1295 if ( !m_bAutoUpPriority ) return;
1296 m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
1297 UpdateAutoUpPriority();
1300 void CKnownFile::SetFileName(const CPath& filename)
1302 CAbstractFile::SetFileName(filename);
1303 #ifndef CLIENT_GUI
1304 wordlist.clear();
1305 Kademlia::CSearchManager::GetWords(GetFileName().GetPrintable(), &wordlist);
1306 #endif
1309 #endif // CLIENT_GUI
1311 //For File Comment //
1312 void CKnownFile::LoadComment()
1314 #ifndef CLIENT_GUI
1315 wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1317 wxConfigBase* cfg = wxConfigBase::Get();
1319 m_strComment = cfg->Read( strCfgPath + wxT("Comment"), wxEmptyString);
1320 m_iRating = cfg->Read( strCfgPath + wxT("Rate"), 0l);
1321 m_bCommentLoaded = true;
1323 #else
1324 m_strComment = wxT("Comments are not allowed on remote gui yet");
1325 m_bCommentLoaded = true;
1326 m_iRating =0;
1327 #endif
1332 wxString CKnownFile::GetAICHMasterHash() const
1334 #ifdef CLIENT_GUI
1335 return m_AICHMasterHash;
1336 #else
1337 if (HasProperAICHHashSet()) {
1338 return m_pAICHHashSet->GetMasterHash().GetString();
1341 return wxEmptyString;
1342 #endif
1346 bool CKnownFile::HasProperAICHHashSet() const
1348 #ifdef CLIENT_GUI
1349 return m_AICHMasterHash.Length();
1350 #else
1351 return m_pAICHHashSet->HasValidMasterHash() &&
1352 (m_pAICHHashSet->GetStatus() == AICH_HASHSETCOMPLETE ||
1353 m_pAICHHashSet->GetStatus() == AICH_VERIFIED);
1354 #endif
1357 wxString CKnownFile::GetFeedback() const
1359 return wxString(_("File name")) + wxT(": ") + GetFileName().GetPrintable() + wxT("\n")
1360 + _("File size") + wxT(": ") + CastItoXBytes(GetFileSize()) + wxT("\n")
1361 + _("Share ratio") + wxString::Format(wxT(": %.2f%%\n"), (((double)statistic.GetAllTimeTransferred() / (double)GetFileSize()) * 100.0))
1362 + _("Uploaded") + wxT(": ") + CastItoXBytes(statistic.GetTransferred()) + wxT(" (") + CastItoXBytes(statistic.GetAllTimeTransferred()) + wxT(")\n")
1363 + _("Requested") + CFormat(wxT(": %u (%u)\n")) % statistic.GetRequests() % statistic.GetAllTimeRequests()
1364 + _("Accepted") + CFormat(wxT(": %u (%u)\n")) % statistic.GetAccepts() % statistic.GetAllTimeAccepts()
1365 + _("Complete sources") + CFormat(wxT(": %u\n")) % m_nCompleteSourcesCount;
1367 // File_checked_for_headers