2 // This file is part of the aMule Project.
4 // Parts of this file are based on work from pan One (http://home-3.tiscali.nl/~meost/pms/)
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 )
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.
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)
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() :
64 alltimetransferred(0),
71 void CFileStatistic::AddRequest(){
74 theApp
->knownfiles
->requested
++;
75 theApp
->sharedfiles
->UpdateItem(fileParent
);
78 void CFileStatistic::AddAccepted(){
81 theApp
->knownfiles
->accepted
++;
82 theApp
->sharedfiles
->UpdateItem(fileParent
);
85 void CFileStatistic::AddTransferred(uint64 bytes
){
87 alltimetransferred
+= bytes
;
88 theApp
->knownfiles
->transferred
+= bytes
;
89 theApp
->sharedfiles
->UpdateItem(fileParent
);
95 /* Abstract File (base class)*/
97 CAbstractFile::CAbstractFile()
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();
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();
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();
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();
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();
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
) {
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
) {
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
) {
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
) {
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()){
243 m_taglist
.insert(it
, rTag
);
247 m_taglist
.push_back(rTag
);
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
) {
261 m_kadNotes
.push_front(pEntry
);
264 void CAbstractFile::AddNote(Kademlia::CEntry
*)
272 CKnownFile::CKnownFile()
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
))
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;
308 m_iED2KPartCount
= 0;
309 m_iED2KPartHashCount
= 0;
310 m_PublishedED2K
= false;
312 m_lastPublishTimeKadSrc
= 0;
313 m_lastPublishTimeKadNotes
= 0;
315 m_lastDateChanged
= 0;
317 statistic
.fileParent
= this;
320 m_pAICHHashSet
= new CAICHHashSet(this);
325 void CKnownFile::SetFileSize(uint64 nFileSize
)
327 CAbstractFile::SetFileSize(nFileSize
);
329 m_pAICHHashSet
->SetFileSize(nFileSize
);
332 // Examples of parthashs, hashsets and filehashs for different filesizes
333 // according the ed2k protocol
334 //----------------------------------------------------------------------
337 //File hash: 2D55E87D0E21F49B9AD25F98531F3724
341 //File size: 1*PARTSIZE
342 //File hash: A72CA8DF7F07154E217C236C89C17619
344 //Hash[ 0]: 4891ED2E5C9C49F442145A3A5F608299
345 //Hash[ 1]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
348 //File size: 1*PARTSIZE + 1 byte
349 //File hash: 2F620AE9D462CBB6A59FE8401D2B3D23
351 //Hash[ 0]: 121795F0BEDE02DDC7C5426D0995F53F
352 //Hash[ 1]: C329E527945B8FE75B3C5E8826755747
355 //File size: 2*PARTSIZE
356 //File hash: A54C5E562D5E03CA7D77961EB9A745A4
358 //Hash[ 0]: B3F5CE2A06BF403BFB9BFFF68BDDC4D9
359 //Hash[ 1]: 509AA30C9EA8FC136B1159DF2F35B8A9
360 //Hash[ 2]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
363 //File size: 3*PARTSIZE
364 //File hash: 5E249B96F9A46A18FC2489B005BF2667
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
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(!)
386 // PARTSIZE*2 2 3(!) 3(!)
387 // PARTSIZE*2+1 3 3 3
390 //wxFAIL; // Kry - Why commented out by lemonfan? it can never be 0
392 m_iED2KPartCount
= 0;
393 m_iED2KPartHashCount
= 0;
399 m_iPartCount
= nFileSize
/ PARTSIZE
+ 1;
401 m_sizeLastPart
= nFileSize
% PARTSIZE
;
402 // file with size of n * PARTSIZE
403 if (m_sizeLastPart
== 0) {
404 m_sizeLastPart
= PARTSIZE
;
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;
421 CKnownFile::CKnownFile(CEC_SharedFile_Tag
*tag
)
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 ) {
432 m_bAutoUpPriority
= true;
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
491 m_abyFileHash
= checkid
;
492 if (parts
<= 1) { // nothing to check
496 if ( m_abyFileHash
!= checkid
) {
497 return false; // wrong file?
499 if (parts
!= GetED2KPartHashCount()) {
504 // SLUGFILLER: SafeHash
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
) {
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()){
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.
542 SetFileName(CPath(newtag
.GetStr()));
547 SetFileSize(newtag
.GetInt());
548 m_AvailPartFrequency
.clear();
549 m_AvailPartFrequency
.insert(
550 m_AvailPartFrequency
.begin(),
554 case FT_ATTRANSFERRED
:
555 statistic
.alltimetransferred
+= newtag
.GetInt();
558 case FT_ATTRANSFERREDHI
:
559 statistic
.alltimetransferred
=
560 (((uint64
)newtag
.GetInt()) << 32) +
561 ((uint64
)statistic
.alltimetransferred
);
565 statistic
.alltimerequested
= newtag
.GetInt();
569 statistic
.alltimeaccepted
= newtag
.GetInt();
573 m_iUpPriority
= newtag
.GetInt();
574 if( m_iUpPriority
== PR_AUTO
){
575 m_iUpPriority
= PR_HIGH
;
576 m_bAutoUpPriority
= true;
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;
592 // Ignore it, it's not used anymore.
598 hash
.DecodeBase32(newtag
.GetStr()) == CAICHHash::GetHashSize();
599 wxASSERT(hashSizeOk
);
601 m_pAICHHashSet
->SetMasterHash(hash
, AICH_HASHSETCOMPLETE
);
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);
615 case FT_KADLASTPUBLISHNOTES
:
616 SetLastPublishTimeKadNotes( newtag
.GetInt() );
619 case FT_KADLASTPUBLISHKEY
:
621 wxASSERT( newtag
.IsInt() );
625 // Store them here and write them back on saving.
626 m_taglist
.push_back(newtag
);
634 bool CKnownFile::LoadDateFromFile(const CFileDataIO
* file
)
636 m_lastDateChanged
= file
->ReadUInt32();
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
);
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);
660 file
->WriteUInt32(m_lastDateChanged
);
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
]);
671 const int iFixedTags
= 8;
672 uint32 tagcount
= iFixedTags
;
673 if (HasProperAICHHashSet()) {
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()) {
691 if (m_lastPublishTimeKadSrc
) {
695 if (m_lastPublishTimeKadNotes
){
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
);
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
);
739 if (HasProperAICHHashSet()) {
740 CTagString
aichtag(FT_AICH_HASH
, m_pAICHHashSet
->GetMasterHash().GetString());
741 aichtag
.WriteTagToFile(file
);
745 if (m_lastPublishTimeKadSrc
){
746 CTagInt32
kadLastPubSrc(FT_KADLASTPUBLISHSRC
, m_lastPublishTimeKadSrc
);
747 kadLastPubSrc
.WriteTagToFile(file
);
751 if (m_lastPublishTimeKadNotes
){
752 CTagInt32
kadLastPubNotes(FT_KADLASTPUBLISHNOTES
, m_lastPublishTimeKadNotes
);
753 kadLastPubNotes
.WriteTagToFile(file
);
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
);
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"));
786 area
.Read(file
, Length
);
788 CreateHashFromInput(area
.GetBuffer(), Length
, Output
, pShaHashOut
);
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
;
804 uint32 posCurrentEMBlock
= 0;
806 CScopedPtr
<CAICHHashAlgo
> pHashAlg(CAICHHashSet::GetNewHashAlgo());
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
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
;
826 pHashAlg
->Add(X
+nToComplete
,(len
*64) - nToComplete
);
827 nIACHPos
= (len
*64) - nToComplete
;
830 pHashAlg
->Add(X
, len
*64);
838 Required
= Length
% 64;
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
;
850 pHashAlg
->Add(X
+nToComplete
, Required
- nToComplete
);
851 nIACHPos
= Required
- nToComplete
;
854 pHashAlg
->Add(X
, Required
);
855 nIACHPos
+= Required
;
859 if (pShaHashOut
!= NULL
){
861 pShaHashOut
->SetBlockHash(nIACHPos
, posCurrentEMBlock
, pHashAlg
.get());
862 posCurrentEMBlock
+= nIACHPos
;
864 wxASSERT( posCurrentEMBlock
== Length
);
865 wxCHECK2( pShaHashOut
->ReCalculateHash(pHashAlg
.get(), false), );
869 #ifdef __WEAK_CRYPTO__
870 CryptoPP::Weak::MD4 md4_hasher
;
872 CryptoPP::MD4 md4_hasher
;
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
)
889 if (m_ClientUploadList
.empty()) {
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
);
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()));
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
);
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
);
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"));
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
)) {
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()) {
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.
979 // This client doesn't support upload chunk status.
980 // So just send it and hope for the best.
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()) {
996 for (int x
= 0; x
< GetPartCount(); x
++ ) {
997 if ( srcstatus
.at(x
) ) {
998 // this client has at least one chunk
1004 // This client doesn't support upload chunk status.
1005 // So just send it and hope for the best.
1013 if(byUsedVersion
>= 3) {
1014 dwID
= cur_src
->GetUserIDHybrid();
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
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
);
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();
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
;
1072 } else if (queued
> 1) {
1073 priority
= PR_NORMAL
;
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();
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
)) {
1140 if(!GetFileComment().IsEmpty()) {
1141 m_lastPublishTimeKadNotes
= (uint32
)time(NULL
)+KADEMLIAREPUBLISHTIMEN
;
1145 if(GetFileRating() != 0) {
1146 m_lastPublishTimeKadNotes
= (uint32
)time(NULL
)+KADEMLIAREPUBLISHTIMEN
;
1153 bool CKnownFile::PublishSrc()
1155 uint32 lastBuddyIP
= 0;
1157 if( theApp
->IsFirewalled() ) {
1158 CUpDownClient
* buddy
= theApp
->clientlist
->GetBuddy();
1160 lastBuddyIP
= theApp
->clientlist
->GetBuddy()->GetIP();
1161 if( lastBuddyIP
!= m_lastBuddyIP
) {
1162 SetLastPublishTimeKadSrc( (uint32
)time(NULL
)+KADEMLIAREPUBLISHTIMES
, lastBuddyIP
);
1170 if(m_lastPublishTimeKadSrc
> (uint32
)time(NULL
)) {
1174 SetLastPublishTimeKadSrc((uint32
)time(NULL
)+KADEMLIAREPUBLISHTIMES
,lastBuddyIP
);
1179 void CKnownFile::UpdatePartsInfo()
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);
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();
1216 std::sort(count
.begin(), count
.end(), std::less
<uint16
>());
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.
1233 // Adjust 100% network and 0% what we see.
1235 if ( count
[i
] < m_nCompleteSourcesCount
) {
1236 m_nCompleteSourcesCountLo
= m_nCompleteSourcesCount
;
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
;
1250 // Adjust network accounts for 100%, we account for
1251 // 0% with what we see and make sure we are still above the low.
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);
1284 const BitVector
& freq
= client
->GetUpPartStatus();
1285 unsigned int size
= freq
.size();
1286 if ( size
!= m_AvailPartFrequency
.size() ) {
1291 for ( unsigned int i
= 0; i
< size
; ++i
) {
1293 m_AvailPartFrequency
[i
]++;
1297 for ( unsigned int i
= 0; i
< size
; ++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
);
1316 Kademlia::CSearchManager::GetWords(GetFileName().GetPrintable(), &wordlist
);
1320 #endif // CLIENT_GUI
1322 //For File Comment //
1323 void CKnownFile::LoadComment()
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;
1335 m_strComment
= wxT("Comments are not allowed on remote gui yet");
1336 m_bCommentLoaded
= true;
1343 wxString
CKnownFile::GetAICHMasterHash() const
1346 return m_AICHMasterHash
;
1348 if (HasProperAICHHashSet()) {
1349 return m_pAICHHashSet
->GetMasterHash().GetString();
1352 return wxEmptyString
;
1357 bool CKnownFile::HasProperAICHHashSet() const
1360 return m_AICHMasterHash
.Length() != 0;
1362 return m_pAICHHashSet
->HasValidMasterHash() &&
1363 (m_pAICHHashSet
->GetStatus() == AICH_HASHSETCOMPLETE
||
1364 m_pAICHHashSet
->GetStatus() == AICH_VERIFIED
);
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