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-2011 aMule Team ( admin@amule.org / http://www.amule.org )
7 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
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
31 #include <protocol/kad/Constants.h>
32 #include <protocol/ed2k/Client2Client/TCP.h>
33 #include <protocol/ed2k/ClientSoftware.h>
34 #include <protocol/Protocols.h>
35 #include <tags/FileTags.h>
37 #include <wx/config.h>
40 #include "UpDownClientEC.h" // Needed for CUpDownClient
42 #include "updownclient.h" // Needed for CUpDownClient
45 #include "MemFile.h" // Needed for CMemFile
46 #include "Packet.h" // Needed for CPacket
47 #include "Preferences.h" // Needed for CPreferences
48 #include "KnownFileList.h" // Needed for CKnownFileList
49 #include "amule.h" // Needed for theApp
50 #include "PartFile.h" // Needed for SavePartFile
51 #include "ClientList.h" // Needed for clientlist (buddy support)
53 #include "ScopedPtr.h" // Needed for CScopedArray and CScopedPtr
54 #include "GuiEvents.h" // Needed for Notify_*
55 #include "SearchFile.h" // Needed for CSearchFile
56 #include "FileArea.h" // Needed for CFileArea
57 #include "FileAutoClose.h" // Needed for CFileAutoClose
58 #include "Server.h" // Needed for CServer
60 #include "CryptoPP_Inc.h" // Needed for MD4
62 #include <common/Format.h>
64 CFileStatistic::CFileStatistic(CKnownFile
*parent
)
70 alltimetransferred(0),
76 void CFileStatistic::AddRequest()
80 theApp
->knownfiles
->requested
++;
81 theApp
->sharedfiles
->UpdateItem(fileParent
);
84 void CFileStatistic::AddAccepted()
88 theApp
->knownfiles
->accepted
++;
89 theApp
->sharedfiles
->UpdateItem(fileParent
);
92 void CFileStatistic::AddTransferred(uint64 bytes
)
95 alltimetransferred
+= bytes
;
96 theApp
->knownfiles
->transferred
+= bytes
;
97 theApp
->sharedfiles
->UpdateItem(fileParent
);
103 /* Abstract File (base class)*/
105 CAbstractFile::CAbstractFile()
113 CAbstractFile::CAbstractFile(const CAbstractFile
& other
)
114 : m_abyFileHash(other
.m_abyFileHash
),
115 m_strComment(other
.m_strComment
),
116 m_iRating(other
.m_iRating
),
117 m_hasComment(other
.m_hasComment
),
118 m_iUserRating(other
.m_iUserRating
),
119 m_taglist(other
.m_taglist
),
121 m_nFileSize(other
.m_nFileSize
),
122 m_fileName(other
.m_fileName
)
124 /* // TODO: Currently it's not safe to duplicate the entries, but isn't needed either.
125 CKadEntryPtrList::const_iterator it = other.m_kadNotes.begin();
126 for (; it != other.m_kadNotes.end(); ++it) {
127 m_kadNotes.push_back(new Kademlia::CEntry(**it));
133 void CAbstractFile::SetFileName(const CPath
& fileName
)
135 m_fileName
= fileName
;
138 uint32
CAbstractFile::GetIntTagValue(uint8 tagname
) const
140 ArrayOfCTag::const_iterator it
= m_taglist
.begin();
141 for (; it
!= m_taglist
.end(); ++it
){
142 if (((*it
).GetNameID() == tagname
) && (*it
).IsInt()) {
143 return (*it
).GetInt();
149 bool CAbstractFile::GetIntTagValue(uint8 tagname
, uint32
& ruValue
) const
151 ArrayOfCTag::const_iterator it
= m_taglist
.begin();
152 for (; it
!= m_taglist
.end(); ++it
){
153 if (((*it
).GetNameID() == tagname
) && (*it
).IsInt()){
154 ruValue
= (*it
).GetInt();
161 uint32
CAbstractFile::GetIntTagValue(const wxString
& tagname
) const
163 ArrayOfCTag::const_iterator it
= m_taglist
.begin();
164 for (; it
!= m_taglist
.end(); ++it
){
165 if ((*it
).IsInt() && ((*it
).GetName() == tagname
)) {
166 return (*it
).GetInt();
172 const wxString
& CAbstractFile::GetStrTagValue(uint8 tagname
) const
174 ArrayOfCTag::const_iterator it
= m_taglist
.begin();
175 for (; it
!= m_taglist
.end(); ++it
){
176 if ((*it
).GetNameID() == tagname
&& (*it
).IsStr()) {
177 return (*it
).GetStr();
183 const wxString
& CAbstractFile::GetStrTagValue(const wxString
& tagname
) const
185 ArrayOfCTag::const_iterator it
= m_taglist
.begin();
186 for (; it
!= m_taglist
.end(); ++it
){
187 if ((*it
).IsStr() && ((*it
).GetName() == tagname
)) {
188 return (*it
).GetStr();
194 const CTag
*CAbstractFile::GetTag(uint8 tagname
, uint8 tagtype
) const
196 ArrayOfCTag::const_iterator it
= m_taglist
.begin();
197 for (; it
!= m_taglist
.end(); ++it
){
198 if ((*it
).GetNameID() == tagname
&& (*it
).GetType() == tagtype
) {
205 const CTag
*CAbstractFile::GetTag(const wxString
& tagname
, uint8 tagtype
) const
207 ArrayOfCTag::const_iterator it
= m_taglist
.begin();
208 for (; it
!= m_taglist
.end(); ++it
){
209 if ((*it
).GetType() == tagtype
&& (*it
).GetName() == tagname
) {
216 const CTag
*CAbstractFile::GetTag(uint8 tagname
) const
218 ArrayOfCTag::const_iterator it
= m_taglist
.begin();
219 for (; it
!= m_taglist
.end(); ++it
){
220 if ((*it
).GetNameID() == tagname
) {
227 const CTag
*CAbstractFile::GetTag(const wxString
& tagname
) const
229 ArrayOfCTag::const_iterator it
= m_taglist
.begin();
230 for (; it
!= m_taglist
.end(); ++it
){
231 if ((*it
).GetName() == tagname
) {
238 void CAbstractFile::AddTagUnique(const CTag
&rTag
)
240 ArrayOfCTag::iterator it
= m_taglist
.begin();
241 for (; it
!= m_taglist
.end(); ++it
) {
242 if ( ( ((*it
).GetNameID() != 0 &&
243 (*it
).GetNameID() == rTag
.GetNameID()) ||
244 (!(*it
).GetName().IsEmpty() &&
245 !rTag
.GetName().IsEmpty() &&
246 (*it
).GetName() == rTag
.GetName()) ) &&
247 (*it
).GetType() == rTag
.GetType())
249 it
= m_taglist
.erase(it
);
250 m_taglist
.insert(it
, rTag
);
254 m_taglist
.push_back(rTag
);
258 void CAbstractFile::AddNote(Kademlia::CEntry
*pEntry
)
260 CKadEntryPtrList::iterator it
= m_kadNotes
.begin();
261 for (; it
!= m_kadNotes
.end(); ++it
) {
262 Kademlia::CEntry
* entry
= *it
;
263 if(entry
->m_uIP
== pEntry
->m_uIP
|| entry
->m_uSourceID
== pEntry
->m_uSourceID
) {
268 m_kadNotes
.push_front(pEntry
);
271 void CAbstractFile::AddNote(Kademlia::CEntry
*)
279 CKnownFile::CKnownFile()
285 CKnownFile::CKnownFile(uint32 ecid
)
293 //#warning Experimental: Construct a CKnownFile from a CSearchFile
294 CKnownFile::CKnownFile(const CSearchFile
&searchFile
)
296 // This will copy the file hash
297 CAbstractFile(static_cast<const CAbstractFile
&>(searchFile
)),
302 // Use CKnownFile::SetFileName()
303 SetFileName(searchFile
.GetFileName());
305 // Use CKnownFile::SetFileSize()
306 SetFileSize(searchFile
.GetFileSize());
310 void CKnownFile::Init()
312 m_showSources
= false;
314 m_nCompleteSourcesTime
= time(NULL
);
315 m_nCompleteSourcesCount
= 0;
316 m_nCompleteSourcesCountLo
= 0;
317 m_nCompleteSourcesCountHi
= 0;
318 m_bCommentLoaded
= false;
320 m_iED2KPartCount
= 0;
321 m_iED2KPartHashCount
= 0;
322 m_PublishedED2K
= false;
324 m_lastPublishTimeKadSrc
= 0;
325 m_lastPublishTimeKadNotes
= 0;
327 m_lastDateChanged
= 0;
328 m_bAutoUpPriority
= thePrefs::GetNewAutoUp();
329 m_iUpPriority
= ( m_bAutoUpPriority
) ? PR_HIGH
: PR_NORMAL
;
330 m_hashingProgress
= 0;
333 m_pAICHHashSet
= new CAICHHashSet(this);
338 void CKnownFile::SetFileSize(uint64 nFileSize
)
340 CAbstractFile::SetFileSize(nFileSize
);
342 m_pAICHHashSet
->SetFileSize(nFileSize
);
345 // Examples of parthashs, hashsets and filehashs for different filesizes
346 // according the ed2k protocol
347 //----------------------------------------------------------------------
350 //File hash: 2D55E87D0E21F49B9AD25F98531F3724
354 //File size: 1*PARTSIZE
355 //File hash: A72CA8DF7F07154E217C236C89C17619
357 //Hash[ 0]: 4891ED2E5C9C49F442145A3A5F608299
358 //Hash[ 1]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
361 //File size: 1*PARTSIZE + 1 byte
362 //File hash: 2F620AE9D462CBB6A59FE8401D2B3D23
364 //Hash[ 0]: 121795F0BEDE02DDC7C5426D0995F53F
365 //Hash[ 1]: C329E527945B8FE75B3C5E8826755747
368 //File size: 2*PARTSIZE
369 //File hash: A54C5E562D5E03CA7D77961EB9A745A4
371 //Hash[ 0]: B3F5CE2A06BF403BFB9BFFF68BDDC4D9
372 //Hash[ 1]: 509AA30C9EA8FC136B1159DF2F35B8A9
373 //Hash[ 2]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
376 //File size: 3*PARTSIZE
377 //File hash: 5E249B96F9A46A18FC2489B005BF2667
379 //Hash[ 0]: 5319896A2ECAD43BF17E2E3575278E72
380 //Hash[ 1]: D86EF157D5E49C5ED502EDC15BB5F82B
381 //Hash[ 2]: 10F2D5B1FCB95C0840519C58D708480F
382 //Hash[ 3]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
385 //File size: 3*PARTSIZE + 1 byte
386 //File hash: 797ED552F34380CAFF8C958207E40355
388 //Hash[ 0]: FC7FD02CCD6987DCF1421F4C0AF94FB8
389 //Hash[ 1]: 2FE466AF8A7C06DA3365317B75A5ACFE
390 //Hash[ 2]: 873D3BF52629F7C1527C6E8E473C1C30
391 //Hash[ 3]: BCE50BEE7877BB07BB6FDA56BFE142FB
394 // File size Data parts ED2K parts ED2K part hashs
395 // ---------------------------------------------------------------
396 // 1..PARTSIZE-1 1 1 0(!)
397 // PARTSIZE 1 2(!) 2(!)
399 // PARTSIZE*2 2 3(!) 3(!)
400 // PARTSIZE*2+1 3 3 3
403 //wxFAIL; // Kry - Why commented out by lemonfan? it can never be 0
405 m_iED2KPartCount
= 0;
406 m_iED2KPartHashCount
= 0;
412 m_iPartCount
= nFileSize
/ PARTSIZE
+ 1;
414 m_sizeLastPart
= nFileSize
% PARTSIZE
;
415 // file with size of n * PARTSIZE
416 if (m_sizeLastPart
== 0) {
417 m_sizeLastPart
= PARTSIZE
;
421 // nr. of parts to be used with OP_FILESTATUS
422 m_iED2KPartCount
= nFileSize
/ PARTSIZE
+ 1;
424 // nr. of parts to be used with OP_HASHSETANSWER
425 m_iED2KPartHashCount
= nFileSize
/ PARTSIZE
;
426 if (m_iED2KPartHashCount
!= 0) {
427 m_iED2KPartHashCount
+= 1;
432 void CKnownFile::AddUploadingClient(CUpDownClient
* client
)
434 m_ClientUploadList
.insert(CCLIENTREF(client
, wxT("CKnownFile::AddUploadingClient m_ClientUploadList")));
436 SourceItemType type
= UNAVAILABLE_SOURCE
;
437 switch (client
->GetUploadState()) {
439 case US_ONUPLOADQUEUE
:
440 type
= AVAILABLE_SOURCE
;
443 // Any other state is UNAVAILABLE_SOURCE by default.
447 Notify_SharedCtrlAddClient(this, CCLIENTREF(client
, wxT("CKnownFile::AddUploadingClient Notify_SharedCtrlAddClient")), type
);
449 UpdateAutoUpPriority();
453 void CKnownFile::RemoveUploadingClient(CUpDownClient
* client
)
455 if (m_ClientUploadList
.erase(CCLIENTREF(client
, wxEmptyString
))) {
456 Notify_SharedCtrlRemoveClient(client
->ECID(), this);
457 UpdateAutoUpPriority();
464 CKnownFile::CKnownFile(const CEC_SharedFile_Tag
*tag
)
470 m_abyFileHash
= tag
->FileHash();
471 SetFileSize(tag
->SizeFull());
472 m_AvailPartFrequency
.insert(m_AvailPartFrequency
.end(), m_iPartCount
, 0);
476 CKnownFile::~CKnownFile()
479 void CKnownFile::UpdateAutoUpPriority()
483 #else // ! CLIENT_GUI
485 CKnownFile::~CKnownFile()
487 SourceSet::iterator it
= m_ClientUploadList
.begin();
488 for ( ; it
!= m_ClientUploadList
.end(); ++it
) {
489 it
->ClearUploadFileID();
492 delete m_pAICHHashSet
;
496 void CKnownFile::SetFilePath(const CPath
& filePath
)
498 m_filePath
= filePath
;
502 // needed for memfiles. its probably better to switch everything to CFile...
503 bool CKnownFile::LoadHashsetFromFile(const CFileDataIO
* file
, bool checkhash
)
505 CMD4Hash checkid
= file
->ReadHash();
507 uint16 parts
= file
->ReadUInt16();
509 for (uint16 i
= 0; i
< parts
; ++i
){
510 CMD4Hash cur_hash
= file
->ReadHash();
511 m_hashlist
.push_back(cur_hash
);
514 // SLUGFILLER: SafeHash - always check for valid m_hashlist
516 m_abyFileHash
= checkid
;
517 if (parts
<= 1) { // nothing to check
521 if ( m_abyFileHash
!= checkid
) {
522 return false; // wrong file?
524 if (parts
!= GetED2KPartHashCount()) {
529 // SLUGFILLER: SafeHash
532 // lol, useless comment but made me lmao
533 // wtf you guys are weird.
535 if (!m_hashlist
.empty()) {
536 CreateHashFromHashlist(m_hashlist
, &checkid
);
539 if ( m_abyFileHash
== checkid
) {
548 bool CKnownFile::LoadTagsFromFile(const CFileDataIO
* file
)
550 uint32 tagcount
= file
->ReadUInt32();
552 for (uint32 j
= 0; j
!= tagcount
; ++j
) {
553 CTag
newtag(*file
, true);
554 switch(newtag
.GetNameID()){
556 if (GetFileName().IsOk()) {
557 // Unlike eMule, we actually prefer the second
558 // filename tag, since we use it to specify the
559 // 'universial' filename (see CPath::ToUniv).
560 CPath path
= CPath::FromUniv(newtag
.GetStr());
562 // May be invalid, if from older versions where
563 // unicoded filenames be saved as empty-strings.
568 SetFileName(CPath(newtag
.GetStr()));
573 SetFileSize(newtag
.GetInt());
574 m_AvailPartFrequency
.clear();
575 m_AvailPartFrequency
.insert(
576 m_AvailPartFrequency
.begin(),
580 case FT_ATTRANSFERRED
:
581 statistic
.SetAllTimeTransferred(statistic
.GetAllTimeTransferred() + newtag
.GetInt());
584 case FT_ATTRANSFERREDHI
:
585 statistic
.SetAllTimeTransferred(statistic
.GetAllTimeTransferred() + (((uint64
)newtag
.GetInt()) << 32));
589 statistic
.SetAllTimeRequests(newtag
.GetInt());
593 statistic
.SetAllTimeAccepts(newtag
.GetInt());
597 m_iUpPriority
= newtag
.GetInt();
598 if( m_iUpPriority
== PR_AUTO
){
599 m_iUpPriority
= PR_HIGH
;
600 m_bAutoUpPriority
= true;
602 if ( m_iUpPriority
!= PR_VERYLOW
&&
603 m_iUpPriority
!= PR_LOW
&&
604 m_iUpPriority
!= PR_NORMAL
&&
605 m_iUpPriority
!= PR_HIGH
&&
606 m_iUpPriority
!= PR_VERYHIGH
&&
607 m_iUpPriority
!= PR_POWERSHARE
) {
608 m_iUpPriority
= PR_NORMAL
;
611 m_bAutoUpPriority
= false;
616 case FT_KADLASTPUBLISHKEY
:
617 case FT_PARTFILENAME
:
618 // Old tags, not used anymore. Just purge them.
624 hash
.DecodeBase32(newtag
.GetStr()) == CAICHHash::GetHashSize();
625 wxASSERT(hashSizeOk
);
627 m_pAICHHashSet
->SetMasterHash(hash
, AICH_HASHSETCOMPLETE
);
632 case FT_KADLASTPUBLISHSRC
:
633 SetLastPublishTimeKadSrc( newtag
.GetInt(), 0 );
635 if(GetLastPublishTimeKadSrc() > (uint32
)time(NULL
)+KADEMLIAREPUBLISHTIMES
) {
636 //There may be a posibility of an older client that saved a random number here.. This will check for that..
637 SetLastPublishTimeKadSrc(0, 0);
641 case FT_KADLASTPUBLISHNOTES
:
642 SetLastPublishTimeKadNotes( newtag
.GetInt() );
646 // Store them here and write them back on saving.
647 m_taglist
.push_back(newtag
);
655 bool CKnownFile::LoadDateFromFile(const CFileDataIO
* file
)
657 m_lastDateChanged
= file
->ReadUInt32();
663 bool CKnownFile::LoadFromFile(const CFileDataIO
* file
)
665 // SLUGFILLER: SafeHash - load first, verify later
666 bool ret1
= LoadDateFromFile(file
);
667 bool ret2
= LoadHashsetFromFile(file
,false);
668 bool ret3
= LoadTagsFromFile(file
);
670 // Final hash-count verification, needs to be done after the tags are loaded.
671 return ret1
&& ret2
&& ret3
&& GetED2KPartHashCount()==GetHashCount();
672 // SLUGFILLER: SafeHash
676 bool CKnownFile::WriteToFile(CFileDataIO
* file
)
678 wxCHECK(!IsPartFile(), false);
681 file
->WriteUInt32(m_lastDateChanged
);
683 file
->WriteHash(m_abyFileHash
);
685 uint16 parts
= m_hashlist
.size();
686 file
->WriteUInt16(parts
);
688 for (int i
= 0; i
< parts
; ++i
)
689 file
->WriteHash(m_hashlist
[i
]);
692 const int iFixedTags
= 8;
693 uint32 tagcount
= iFixedTags
;
694 if (HasProperAICHHashSet()) {
697 // Float meta tags are currently not written. All older eMule versions < 0.28a have
698 // a bug in the meta tag reading+writing code. To achive maximum backward
699 // compatibility for met files with older eMule versions we just don't write float
700 // tags. This is OK, because we (eMule) do not use float tags. The only float tags
701 // we may have to handle is the '# Sent' tag from the Hybrid, which is pretty
702 // useless but may be received from us via the servers.
704 // The code for writing the float tags SHOULD BE ENABLED in SOME MONTHS (after most
705 // people are using the newer eMule versions which do not write broken float tags).
706 for (size_t j
= 0; j
< m_taglist
.size(); ++j
){
707 if (m_taglist
[j
].IsInt() || m_taglist
[j
].IsStr()) {
712 if (m_lastPublishTimeKadSrc
) {
716 if (m_lastPublishTimeKadNotes
){
722 file
->WriteUInt32(tagcount
);
724 // We still save the unicoded filename, for backwards
725 // compatibility with pre-2.2 and other clients.
726 CTagString
nametag_unicode(FT_FILENAME
, GetFileName().GetRaw());
727 // We write it with BOM to keep eMule compatibility
728 nametag_unicode
.WriteTagToFile(file
,utf8strOptBOM
);
730 // The non-unicoded filename is written in an 'universial'
731 // format, which allows us to identify files, even if the
732 // system locale changes.
733 CTagString
nametag(FT_FILENAME
, CPath::ToUniv(GetFileName()));
734 nametag
.WriteTagToFile(file
);
736 CTagIntSized
sizetag(FT_FILESIZE
, GetFileSize(), IsLargeFile() ? 64 : 32);
737 sizetag
.WriteTagToFile(file
);
741 tran
= statistic
.GetAllTimeTransferred() & 0xFFFFFFFF;
742 CTagInt32
attag1(FT_ATTRANSFERRED
, tran
);
743 attag1
.WriteTagToFile(file
);
745 tran
= statistic
.GetAllTimeTransferred() >> 32;
746 CTagInt32
attag4(FT_ATTRANSFERREDHI
, tran
);
747 attag4
.WriteTagToFile(file
);
749 CTagInt32
attag2(FT_ATREQUESTED
, statistic
.GetAllTimeRequests());
750 attag2
.WriteTagToFile(file
);
752 CTagInt32
attag3(FT_ATACCEPTED
, statistic
.GetAllTimeAccepts());
753 attag3
.WriteTagToFile(file
);
755 // priority N permission
756 CTagInt32
priotag(FT_ULPRIORITY
, IsAutoUpPriority() ? PR_AUTO
: m_iUpPriority
);
757 priotag
.WriteTagToFile(file
);
760 if (HasProperAICHHashSet()) {
761 CTagString
aichtag(FT_AICH_HASH
, m_pAICHHashSet
->GetMasterHash().GetString());
762 aichtag
.WriteTagToFile(file
);
766 if (m_lastPublishTimeKadSrc
){
767 CTagInt32
kadLastPubSrc(FT_KADLASTPUBLISHSRC
, m_lastPublishTimeKadSrc
);
768 kadLastPubSrc
.WriteTagToFile(file
);
772 if (m_lastPublishTimeKadNotes
){
773 CTagInt32
kadLastPubNotes(FT_KADLASTPUBLISHNOTES
, m_lastPublishTimeKadNotes
);
774 kadLastPubNotes
.WriteTagToFile(file
);
778 for (size_t j
= 0; j
< m_taglist
.size(); ++j
){
779 if (m_taglist
[j
].IsInt() || m_taglist
[j
].IsStr()) {
780 m_taglist
[j
].WriteTagToFile(file
);
787 void CKnownFile::CreateHashFromHashlist(const ArrayOfCMD4Hash
& hashes
, CMD4Hash
* Output
)
789 wxCHECK_RET(hashes
.size(), wxT("No input to hash from in CreateHashFromHashlist"));
791 std::vector
<byte
> buffer(hashes
.size() * MD4HASH_LENGTH
);
792 std::vector
<byte
>::iterator it
= buffer
.begin();
794 for (size_t i
= 0; i
< hashes
.size(); ++i
) {
795 it
= STLCopy_n(hashes
[i
].GetHash(), MD4HASH_LENGTH
, it
);
798 CreateHashFromInput(&buffer
[0], buffer
.size(), Output
, NULL
);
802 void CKnownFile::CreateHashFromFile(CFileAutoClose
& file
, uint64 offset
, uint32 Length
, CMD4Hash
* Output
, CAICHHashTree
* pShaHashOut
)
804 wxCHECK_RET(Length
, wxT("No input to hash from in CreateHashFromFile"));
807 area
.ReadAt(file
, offset
, Length
);
809 CreateHashFromInput(area
.GetBuffer(), Length
, Output
, pShaHashOut
);
814 void CKnownFile::CreateHashFromInput(const byte
* input
, uint32 Length
, CMD4Hash
* Output
, CAICHHashTree
* pShaHashOut
)
816 wxASSERT_MSG(Output
|| pShaHashOut
, wxT("Nothing to do in CreateHashFromInput"));
817 { wxCHECK_RET(input
, wxT("No input to hash from in CreateHashFromInput")); }
818 wxASSERT(Length
<= PARTSIZE
); // We never hash more than one PARTSIZE
820 CMemFile
data(input
, Length
);
822 uint32 Required
= Length
;
825 uint32 posCurrentEMBlock
= 0;
827 CScopedPtr
<CAICHHashAlgo
> pHashAlg(CAICHHashSet::GetNewHashAlgo());
830 while (Required
>= 64) {
831 uint32 len
= Required
/ 64;
832 if (len
> sizeof(X
)/(64 * sizeof(X
[0]))) {
833 len
= sizeof(X
)/(64 * sizeof(X
[0]));
836 data
.Read(&X
, len
* 64);
838 // SHA hash needs 180KB blocks
840 if (nIACHPos
+ len
*64 >= EMBLOCKSIZE
) {
841 uint32 nToComplete
= EMBLOCKSIZE
- nIACHPos
;
842 pHashAlg
->Add(X
, nToComplete
);
843 wxASSERT( nIACHPos
+ nToComplete
== EMBLOCKSIZE
);
844 pShaHashOut
->SetBlockHash(EMBLOCKSIZE
, posCurrentEMBlock
, pHashAlg
.get());
845 posCurrentEMBlock
+= EMBLOCKSIZE
;
847 pHashAlg
->Add(X
+nToComplete
,(len
*64) - nToComplete
);
848 nIACHPos
= (len
*64) - nToComplete
;
851 pHashAlg
->Add(X
, len
*64);
859 Required
= Length
% 64;
861 data
.Read(&X
,Required
);
863 if (pShaHashOut
!= NULL
){
864 if (nIACHPos
+ Required
>= EMBLOCKSIZE
){
865 uint32 nToComplete
= EMBLOCKSIZE
- nIACHPos
;
866 pHashAlg
->Add(X
, nToComplete
);
867 wxASSERT( nIACHPos
+ nToComplete
== EMBLOCKSIZE
);
868 pShaHashOut
->SetBlockHash(EMBLOCKSIZE
, posCurrentEMBlock
, pHashAlg
.get());
869 posCurrentEMBlock
+= EMBLOCKSIZE
;
871 pHashAlg
->Add(X
+nToComplete
, Required
- nToComplete
);
872 nIACHPos
= Required
- nToComplete
;
875 pHashAlg
->Add(X
, Required
);
876 nIACHPos
+= Required
;
880 if (pShaHashOut
!= NULL
){
882 pShaHashOut
->SetBlockHash(nIACHPos
, posCurrentEMBlock
, pHashAlg
.get());
883 posCurrentEMBlock
+= nIACHPos
;
885 wxASSERT( posCurrentEMBlock
== Length
);
886 wxCHECK2( pShaHashOut
->ReCalculateHash(pHashAlg
.get(), false), );
890 #ifdef __WEAK_CRYPTO__
891 CryptoPP::Weak::MD4 md4_hasher
;
893 CryptoPP::MD4 md4_hasher
;
895 md4_hasher
.CalculateDigest(Output
->GetHash(), input
, Length
);
900 const CMD4Hash
& CKnownFile::GetPartHash(uint16 part
) const {
901 wxASSERT( part
< m_hashlist
.size() );
903 return m_hashlist
[part
];
906 CPacket
* CKnownFile::CreateSrcInfoPacket(const CUpDownClient
* forClient
, uint8 byRequestedVersion
, uint16 nRequestedOptions
)
910 if (m_ClientUploadList
.empty()) {
914 if (((static_cast<CKnownFile
*>(forClient
->GetRequestFile()) != this)
915 && (forClient
->GetUploadFile() != this)) || forClient
->GetUploadFileID() != GetFileHash()) {
916 wxString file1
= _("Unknown");
917 if (forClient
->GetRequestFile() && forClient
->GetRequestFile()->GetFileName().IsOk()) {
918 file1
= forClient
->GetRequestFile()->GetFileName().GetPrintable();
919 } else if (forClient
->GetUploadFile() && forClient
->GetUploadFile()->GetFileName().IsOk()) {
920 file1
= forClient
->GetUploadFile()->GetFileName().GetPrintable();
922 wxString file2
= _("Unknown");
923 if (GetFileName().IsOk()) {
924 file2
= GetFileName().GetPrintable();
926 AddDebugLogLineN(logKnownFiles
, wxT("File mismatch on source packet (K) Sending: ") + file1
+ wxT(" From: ") + file2
);
930 const BitVector
& rcvstatus
= forClient
->GetUpPartStatus();
931 bool SupportsUploadChunksState
= !rcvstatus
.empty();
932 //wxASSERT(rcvstatus.size() == GetPartCount()); // Obviously!
933 if (rcvstatus
.size() != GetPartCount()) {
934 // Yuck. Same file but different part count? Seriously fucked up.
935 AddDebugLogLineN(logKnownFiles
, CFormat(wxT("Impossible situation: different partcounts for the same known file: %i (client) and %i (file)")) % rcvstatus
.size() % GetPartCount());
943 if (forClient
->SupportsSourceExchange2() && byRequestedVersion
> 0){
944 // the client uses SourceExchange2 and requested the highest version he knows
945 // and we send the highest version we know, but of course not higher than his request
946 byUsedVersion
= std::min(byRequestedVersion
, (uint8
)SOURCEEXCHANGE2_VERSION
);
948 data
.WriteUInt8(byUsedVersion
);
950 // we don't support any special SX2 options yet, reserved for later use
951 if (nRequestedOptions
!= 0) {
952 AddDebugLogLineN(logKnownFiles
, CFormat(wxT("Client requested unknown options for SourceExchange2: %u")) % nRequestedOptions
);
955 byUsedVersion
= forClient
->GetSourceExchange1Version();
956 bIsSX2Packet
= false;
957 if (forClient
->SupportsSourceExchange2()) {
958 AddDebugLogLineN(logKnownFiles
, wxT("Client which announced to support SX2 sent SX1 packet instead"));
964 data
.WriteHash(forClient
->GetUploadFileID());
965 data
.WriteUInt16(nCount
);
966 uint32 cDbgNoSrc
= 0;
968 SourceSet::iterator it
= m_ClientUploadList
.begin();
969 for ( ; it
!= m_ClientUploadList
.end(); ++it
) {
970 const CUpDownClient
*cur_src
= it
->GetClient();
972 if ( cur_src
->HasLowID() ||
973 cur_src
== forClient
||
974 !( cur_src
->GetUploadState() == US_UPLOADING
||
975 cur_src
->GetUploadState() == US_ONUPLOADQUEUE
)) {
979 bool bNeeded
= false;
981 if ( SupportsUploadChunksState
) {
982 const BitVector
& srcstatus
= cur_src
->GetUpPartStatus();
983 if ( !srcstatus
.empty() ) {
984 //wxASSERT(srcstatus.size() == GetPartCount()); // Obviously!
985 if (srcstatus
.size() != GetPartCount()) {
988 if ( cur_src
->GetUpPartCount() == forClient
->GetUpPartCount() ) {
989 for (int x
= 0; x
< GetPartCount(); x
++ ) {
990 if ( srcstatus
.get(x
) && !rcvstatus
.get(x
) ) {
991 // We know the receiving client needs
992 // a chunk from this client.
1000 // This client doesn't support upload chunk status.
1001 // So just send it and hope for the best.
1005 // remote client does not support upload chunk status,
1006 // search sources which have at least one complete part
1007 // we could even sort the list of sources by available
1008 // chunks to return as much sources as possible which
1009 // have the most available chunks. but this could be
1010 // a noticeable performance problem.
1011 const BitVector
& srcstatus
= cur_src
->GetUpPartStatus();
1012 if ( !srcstatus
.empty() ) {
1013 //wxASSERT(srcstatus.size() == GetPartCount());
1014 if (srcstatus
.size() != GetPartCount()) {
1017 for (int x
= 0; x
< GetPartCount(); x
++ ) {
1018 if ( srcstatus
.get(x
) ) {
1019 // this client has at least one chunk
1025 // This client doesn't support upload chunk status.
1026 // So just send it and hope for the best.
1034 if(byUsedVersion
>= 3) {
1035 dwID
= cur_src
->GetUserIDHybrid();
1037 dwID
= cur_src
->GetIP();
1039 data
.WriteUInt32(dwID
);
1040 data
.WriteUInt16(cur_src
->GetUserPort());
1041 data
.WriteUInt32(cur_src
->GetServerIP());
1042 data
.WriteUInt16(cur_src
->GetServerPort());
1044 if (byUsedVersion
>= 2) {
1045 data
.WriteHash(cur_src
->GetUserHash());
1048 if (byUsedVersion
>= 4){
1049 // CryptSettings - SourceExchange V4
1051 // 1 CryptLayer Required
1052 // 1 CryptLayer Requested
1053 // 1 CryptLayer Supported
1054 const uint8 uSupportsCryptLayer
= cur_src
->SupportsCryptLayer() ? 1 : 0;
1055 const uint8 uRequestsCryptLayer
= cur_src
->RequestsCryptLayer() ? 1 : 0;
1056 const uint8 uRequiresCryptLayer
= cur_src
->RequiresCryptLayer() ? 1 : 0;
1057 const uint8 byCryptOptions
= (uRequiresCryptLayer
<< 2) | (uRequestsCryptLayer
<< 1) | (uSupportsCryptLayer
<< 0);
1058 data
.WriteUInt8(byCryptOptions
);
1071 data
.Seek(bIsSX2Packet
? 17 : 16, wxFromStart
);
1072 data
.WriteUInt16(nCount
);
1074 CPacket
* result
= new CPacket(data
, OP_EMULEPROT
, bIsSX2Packet
? OP_ANSWERSOURCES2
: OP_ANSWERSOURCES
);
1076 if ( result
->GetPacketSize() > 354 ) {
1077 result
->PackPacket();
1084 void CKnownFile::CreateOfferedFilePacket(
1087 CUpDownClient
*pClient
) {
1089 // This function is used for offering files to the local server and for sending
1090 // shared files to some other client. In each case we send our IP+Port only, if
1091 // we have a HighID.
1093 wxASSERT(!(pClient
&& pServer
));
1095 SetPublishedED2K(true);
1096 files
->WriteHash(GetFileHash());
1098 uint32 nClientID
= 0;
1099 uint16 nClientPort
= 0;
1102 if (pServer
->GetTCPFlags() & SRV_TCPFLG_COMPRESSION
) {
1103 #define FILE_COMPLETE_ID 0xfbfbfbfb
1104 #define FILE_COMPLETE_PORT 0xfbfb
1105 #define FILE_INCOMPLETE_ID 0xfcfcfcfc
1106 #define FILE_INCOMPLETE_PORT 0xfcfc
1107 // complete file: ip 251.251.251 (0xfbfbfbfb) port 0xfbfb
1108 // incomplete file: op 252.252.252 (0xfcfcfcfc) port 0xfcfc
1109 if (GetStatus() == PS_COMPLETE
) {
1110 nClientID
= FILE_COMPLETE_ID
;
1111 nClientPort
= FILE_COMPLETE_PORT
;
1113 nClientID
= FILE_INCOMPLETE_ID
;
1114 nClientPort
= FILE_INCOMPLETE_PORT
;
1117 if (theApp
->IsConnectedED2K() && !::IsLowID(theApp
->GetED2KID())){
1118 nClientID
= theApp
->GetID();
1119 nClientPort
= thePrefs::GetPort();
1123 // Do not merge this with the above case - this one
1124 // also checks Kad status.
1125 if (theApp
->IsConnected() && !theApp
->IsFirewalled()) {
1126 nClientID
= theApp
->GetID();
1127 nClientPort
= thePrefs::GetPort();
1131 files
->WriteUInt32(nClientID
);
1132 files
->WriteUInt16(nClientPort
);
1136 // The printable filename is used because it's destined for another user.
1137 tags
.push_back(new CTagString(FT_FILENAME
, GetFileName().GetPrintable()));
1139 if (pClient
&& pClient
->GetVBTTags()) {
1140 tags
.push_back(new CTagVarInt(FT_FILESIZE
, GetFileSize()));
1142 if (!IsLargeFile()){
1143 tags
.push_back(new CTagInt32(FT_FILESIZE
, GetFileSize()));
1146 // we send 2*32 bit tags to servers, but a real 64 bit tag to other clients.
1148 if (!pServer
->SupportsLargeFilesTCP()){
1150 tags
.push_back(new CTagInt32(FT_FILESIZE
, 0));
1152 tags
.push_back(new CTagInt32(FT_FILESIZE
, (uint32
)GetFileSize()));
1153 tags
.push_back(new CTagInt32(FT_FILESIZE_HI
, (uint32
)(GetFileSize() >> 32)));
1156 if (!pClient
->SupportsLargeFiles()) {
1158 tags
.push_back(new CTagInt32(FT_FILESIZE
, 0));
1160 tags
.push_back(new CTagInt64(FT_FILESIZE
, GetFileSize()));
1166 if (GetFileRating()) {
1167 tags
.push_back(new CTagVarInt(FT_FILERATING
, GetFileRating(), (pClient
&& pClient
->GetVBTTags()) ? 0 : 32));
1170 // NOTE: Archives and CD-Images are published+searched with file type "Pro"
1171 bool bAddedFileType
= false;
1172 if (pServer
&& (pServer
->GetTCPFlags() & SRV_TCPFLG_TYPETAGINTEGER
)) {
1173 // Send integer file type tags to newer servers
1174 EED2KFileType eFileType
= GetED2KFileTypeSearchID(GetED2KFileTypeID(GetFileName()));
1175 if (eFileType
>= ED2KFT_AUDIO
&& eFileType
<= ED2KFT_CDIMAGE
) {
1176 tags
.push_back(new CTagInt32(FT_FILETYPE
, eFileType
));
1177 bAddedFileType
= true;
1180 if (!bAddedFileType
) {
1181 // Send string file type tags to:
1182 // - newer servers, in case there is no integer type available for the file type (e.g. emulecollection)
1185 wxString
strED2KFileType(GetED2KFileTypeSearchTerm(GetED2KFileTypeID(GetFileName())));
1186 if (!strED2KFileType
.IsEmpty()) {
1187 tags
.push_back(new CTagString(FT_FILETYPE
, strED2KFileType
));
1191 // There, we could add MetaData info, if we ever get to have that.
1193 EUtf8Str eStrEncode
;
1195 bool unicode_support
=
1196 // eservers that support UNICODE.
1197 (pServer
&& (pServer
->GetUnicodeSupport()))
1199 // clients that support unicode
1200 (pClient
&& pClient
->GetUnicodeSupport());
1201 eStrEncode
= unicode_support
? utf8strRaw
: utf8strNone
;
1203 files
->WriteUInt32(tags
.size());
1205 // Sadly, eMule doesn't use a MISCOPTIONS flag on hello packet for this, so we
1206 // have to identify the support for new tags by version.
1208 // eMule client > 0.42f
1209 (pClient
&& pClient
->IsEmuleClient() && pClient
->GetVersion() >= MAKE_CLIENT_VERSION(0,42,7))
1211 // aMule >= 2.0.0rc8. Sadly, there's no way to check the rcN number, so I checked
1212 // the rc8 changelog. On rc8 OSInfo was introduced, so...
1213 (pClient
&& pClient
->GetClientSoft() == SO_AMULE
&& !pClient
->GetClientOSInfo().IsEmpty())
1215 // eservers use a flag for this, at least.
1216 (pServer
&& (pServer
->GetTCPFlags() & SRV_TCPFLG_NEWTAGS
));
1218 for (TagPtrList::iterator it
= tags
.begin(); it
!= tags
.end(); ++it
) {
1221 pTag
->WriteNewEd2kTag(files
, eStrEncode
);
1223 pTag
->WriteTagToFile(files
, eStrEncode
);
1230 // Updates priority of file if autopriority is activated
1231 void CKnownFile::UpdateAutoUpPriority()
1233 if (IsAutoUpPriority()) {
1234 uint32 queued
= GetQueuedCount();
1235 uint8 priority
= PR_NORMAL
;
1239 } else if (queued
> 1) {
1240 priority
= PR_NORMAL
;
1245 if (GetUpPriority() != priority
) {
1246 SetUpPriority(priority
, false);
1247 Notify_SharedFilesUpdateItem(this);
1252 void CKnownFile::SetFileCommentRating(const wxString
& strNewComment
, int8 iNewRating
)
1254 if (m_strComment
!= strNewComment
|| m_iRating
!= iNewRating
) {
1255 SetLastPublishTimeKadNotes(0);
1256 wxString strCfgPath
= wxT("/") + m_abyFileHash
.Encode() + wxT("/");
1258 wxConfigBase
* cfg
= wxConfigBase::Get();
1259 if (strNewComment
.IsEmpty() && iNewRating
== 0) {
1260 cfg
->DeleteGroup(strCfgPath
);
1262 cfg
->Write( strCfgPath
+ wxT("Comment"), strNewComment
);
1263 cfg
->Write( strCfgPath
+ wxT("Rate"), (int)iNewRating
);
1266 m_strComment
= strNewComment
;
1267 m_iRating
= iNewRating
;
1269 SourceSet::iterator it
= m_ClientUploadList
.begin();
1270 for ( ; it
!= m_ClientUploadList
.end(); ++it
) {
1271 it
->SetCommentDirty();
1277 void CKnownFile::SetUpPriority(uint8 iNewUpPriority
, bool m_bsave
){
1278 m_iUpPriority
= iNewUpPriority
;
1279 if( IsPartFile() && m_bsave
) {
1280 static_cast<CPartFile
*>(this)->SavePartFile();
1284 void CKnownFile::SetPublishedED2K(bool val
){
1285 m_PublishedED2K
= val
;
1286 Notify_SharedFilesUpdateItem(this);
1289 bool CKnownFile::PublishNotes()
1291 if(m_lastPublishTimeKadNotes
> (uint32
)time(NULL
)) {
1295 if(!GetFileComment().IsEmpty()) {
1296 m_lastPublishTimeKadNotes
= (uint32
)time(NULL
)+KADEMLIAREPUBLISHTIMEN
;
1300 if(GetFileRating() != 0) {
1301 m_lastPublishTimeKadNotes
= (uint32
)time(NULL
)+KADEMLIAREPUBLISHTIMEN
;
1308 bool CKnownFile::PublishSrc()
1310 uint32 lastBuddyIP
= 0;
1312 if( theApp
->IsFirewalled() ) {
1313 CUpDownClient
* buddy
= theApp
->clientlist
->GetBuddy();
1315 lastBuddyIP
= theApp
->clientlist
->GetBuddy()->GetIP();
1316 if( lastBuddyIP
!= m_lastBuddyIP
) {
1317 SetLastPublishTimeKadSrc( (uint32
)time(NULL
)+KADEMLIAREPUBLISHTIMES
, lastBuddyIP
);
1325 if(m_lastPublishTimeKadSrc
> (uint32
)time(NULL
)) {
1329 SetLastPublishTimeKadSrc((uint32
)time(NULL
)+KADEMLIAREPUBLISHTIMES
,lastBuddyIP
);
1334 void CKnownFile::UpdatePartsInfo()
1337 uint16 partcount
= GetPartCount();
1338 bool flag
= (time(NULL
) - m_nCompleteSourcesTime
> 0);
1340 // Ensure the frequency-list is ready
1341 if ( m_AvailPartFrequency
.size() != GetPartCount() ) {
1342 m_AvailPartFrequency
.clear();
1343 m_AvailPartFrequency
.insert(m_AvailPartFrequency
.begin(), GetPartCount(), 0);
1347 ArrayOfUInts16 count
;
1348 count
.reserve(m_ClientUploadList
.size());
1350 SourceSet::iterator it
= m_ClientUploadList
.begin();
1351 for ( ; it
!= m_ClientUploadList
.end(); ++it
) {
1352 CUpDownClient
* client
= it
->GetClient();
1353 if ( !client
->GetUpPartStatus().empty() && client
->GetUpPartCount() == partcount
) {
1354 count
.push_back(client
->GetUpCompleteSourcesCount());
1358 m_nCompleteSourcesCount
= m_nCompleteSourcesCountLo
= m_nCompleteSourcesCountHi
= 0;
1360 if( partcount
> 0) {
1361 m_nCompleteSourcesCount
= m_AvailPartFrequency
[0];
1363 for (uint16 i
= 1; i
< partcount
; ++i
) {
1364 if( m_nCompleteSourcesCount
> m_AvailPartFrequency
[i
]) {
1365 m_nCompleteSourcesCount
= m_AvailPartFrequency
[i
];
1368 count
.push_back(m_nCompleteSourcesCount
);
1370 int32 n
= count
.size();
1372 std::sort(count
.begin(), count
.end(), std::less
<uint16
>());
1375 int i
= n
>> 1; // (n / 2)
1376 int j
= (n
* 3) >> 2; // (n * 3) / 4
1377 int k
= (n
* 7) >> 3; // (n * 7) / 8
1379 // For complete files, trust the people your uploading to more...
1381 // For low guess and normal guess count
1382 // - If we see more sources then the guessed low and
1383 // normal, use what we see.
1384 // - If we see less sources then the guessed low,
1385 // adjust network accounts for 100%, we account for
1386 // 0% with what we see and make sure we are still
1387 // above the normal.
1389 // Adjust 100% network and 0% what we see.
1391 if ( count
[i
] < m_nCompleteSourcesCount
) {
1392 m_nCompleteSourcesCountLo
= m_nCompleteSourcesCount
;
1394 m_nCompleteSourcesCountLo
= count
[i
];
1396 m_nCompleteSourcesCount
= m_nCompleteSourcesCountLo
;
1397 m_nCompleteSourcesCountHi
= count
[j
];
1398 if( m_nCompleteSourcesCountHi
< m_nCompleteSourcesCount
) {
1399 m_nCompleteSourcesCountHi
= m_nCompleteSourcesCount
;
1406 // Adjust network accounts for 100%, we account for
1407 // 0% with what we see and make sure we are still above the low.
1409 // Adjust network accounts for 100%, we account for 0%
1410 // with what we see and make sure we are still above the normal.
1412 m_nCompleteSourcesCountLo
= m_nCompleteSourcesCount
;
1413 m_nCompleteSourcesCount
= count
[j
];
1414 if( m_nCompleteSourcesCount
< m_nCompleteSourcesCountLo
) {
1415 m_nCompleteSourcesCount
= m_nCompleteSourcesCountLo
;
1417 m_nCompleteSourcesCountHi
= count
[k
];
1418 if( m_nCompleteSourcesCountHi
< m_nCompleteSourcesCount
) {
1419 m_nCompleteSourcesCountHi
= m_nCompleteSourcesCount
;
1423 m_nCompleteSourcesTime
= time(NULL
) + (60);
1426 Notify_SharedFilesUpdateItem(this);
1430 void CKnownFile::UpdateUpPartsFrequency( CUpDownClient
* client
, bool increment
)
1432 if ( m_AvailPartFrequency
.size() != GetPartCount() ) {
1433 m_AvailPartFrequency
.clear();
1434 m_AvailPartFrequency
.insert(m_AvailPartFrequency
.begin(), GetPartCount(), 0);
1440 const BitVector
& freq
= client
->GetUpPartStatus();
1441 unsigned int size
= freq
.size();
1442 if ( size
!= m_AvailPartFrequency
.size() ) {
1447 for ( unsigned int i
= 0; i
< size
; ++i
) {
1448 if ( freq
.get(i
) ) {
1449 m_AvailPartFrequency
[i
]++;
1453 for ( unsigned int i
= 0; i
< size
; ++i
) {
1454 if ( freq
.get(i
) ) {
1455 m_AvailPartFrequency
[i
]--;
1461 void CKnownFile::ClearPriority() {
1462 if ( !m_bAutoUpPriority
) return;
1463 m_iUpPriority
= ( m_bAutoUpPriority
) ? PR_HIGH
: PR_NORMAL
;
1464 UpdateAutoUpPriority();
1467 static void GuessAndRemoveExt(CPath
& name
)
1469 wxString ext
= name
.GetExt();
1471 // Remove common two-part extensions, such as "tar.gz"
1472 if (ext
== wxT("gz") || ext
== wxT("bz2")) {
1473 name
= name
.RemoveExt();
1474 if (name
.GetExt() == wxT("tar")) {
1475 name
= name
.RemoveExt();
1477 // might be an extension if length == 3
1478 // and also remove some common non-three-character extensions
1479 } else if (ext
.Length() == 3 ||
1482 ext
== wxT("jpeg") ||
1485 name
= name
.RemoveExt();
1489 void CKnownFile::SetFileName(const CPath
& filename
)
1491 CAbstractFile::SetFileName(filename
);
1493 // Don't publish extension. That'd kill the node indexing e.g. "avi".
1494 CPath tmpName
= GetFileName();
1495 GuessAndRemoveExt(tmpName
);
1496 Kademlia::CSearchManager::GetWords(tmpName
.GetPrintable(), &wordlist
);
1499 #endif // CLIENT_GUI
1501 //For File Comment //
1502 void CKnownFile::LoadComment() const
1505 wxString strCfgPath
= wxT("/") + m_abyFileHash
.Encode() + wxT("/");
1507 wxConfigBase
* cfg
= wxConfigBase::Get();
1509 m_strComment
= cfg
->Read( strCfgPath
+ wxT("Comment"), wxEmptyString
);
1510 m_iRating
= cfg
->Read( strCfgPath
+ wxT("Rate"), 0l);
1513 m_bCommentLoaded
= true;
1517 wxString
CKnownFile::GetAICHMasterHash() const
1520 return m_AICHMasterHash
;
1522 if (HasProperAICHHashSet()) {
1523 return m_pAICHHashSet
->GetMasterHash().GetString();
1526 return wxEmptyString
;
1531 bool CKnownFile::HasProperAICHHashSet() const
1534 return m_AICHMasterHash
.Length() != 0;
1536 return m_pAICHHashSet
->HasValidMasterHash() &&
1537 (m_pAICHHashSet
->GetStatus() == AICH_HASHSETCOMPLETE
||
1538 m_pAICHHashSet
->GetStatus() == AICH_VERIFIED
);
1542 wxString
CKnownFile::GetFeedback() const
1544 return wxString(_("File name")) + wxT(": ") + GetFileName().GetPrintable() + wxT("\n")
1545 + _("File size") + wxT(": ") + CastItoXBytes(GetFileSize()) + wxT("\n")
1546 + _("Share ratio") + CFormat(wxT(": %.2f%%\n")) % (((double)statistic
.GetAllTimeTransferred() / (double)GetFileSize()) * 100.0)
1547 + _("Uploaded") + wxT(": ") + CastItoXBytes(statistic
.GetTransferred()) + wxT(" (") + CastItoXBytes(statistic
.GetAllTimeTransferred()) + wxT(")\n")
1548 + _("Requested") + CFormat(wxT(": %u (%u)\n")) % statistic
.GetRequests() % statistic
.GetAllTimeRequests()
1549 + _("Accepted") + CFormat(wxT(": %u (%u)\n")) % statistic
.GetAccepts() % statistic
.GetAllTimeAccepts()
1550 + _("On Queue") + CFormat(wxT(": %u\n")) % GetQueuedCount()
1551 + _("Complete sources") + CFormat(wxT(": %u\n")) % m_nCompleteSourcesCount
;
1554 // File_checked_for_headers