Upstream tarball 10153
[amule.git] / src / KnownFile.cpp
blob2ade44cb91ab28af2d1c4308407306cb25635054
1 //
2 // This file is part of the aMule Project.
3 //
4 // Parts of this file are based on work from pan One (http://home-3.tiscali.nl/~meost/pms/)
5 //
6 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
7 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
8 //
9 // Any parts of this program derived from the xMule, lMule or eMule project,
10 // or contributed by third-party developers are copyrighted by their
11 // respective authors.
13 // This program is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation; either version 2 of the License, or
16 // (at your option) any later version.
18 // This program is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "KnownFile.h" // Do_not_auto_remove
32 #include <protocol/kad/Constants.h>
33 #include <protocol/ed2k/Client2Client/TCP.h>
34 #include <protocol/Protocols.h>
35 #include <tags/FileTags.h>
38 #include <wx/config.h>
41 #include "MemFile.h" // Needed for CMemFile
42 #include "updownclient.h" // Needed for CUpDownClient
43 #include "Packet.h" // Needed for CPacket
44 #include "Preferences.h" // Needed for CPreferences
45 #include "KnownFileList.h" // Needed for CKnownFileList
46 #include "amule.h" // Needed for theApp
47 #include "PartFile.h" // Needed for SavePartFile
48 #include "ClientList.h" // Needed for clientlist (buddy support)
49 #include "Logger.h"
50 #include "ScopedPtr.h" // Needed for CScopedArray and CScopedPtr
51 #include "GuiEvents.h" // Needed for Notify_*
52 #include "SearchFile.h" // Needed for CSearchFile
53 #include "FileArea.h" // Needed for CFileArea
54 #include "FileAutoClose.h" // Needed for CFileAutoClose
56 #include "CryptoPP_Inc.h" // Needed for MD4
58 #include <common/Format.h>
60 CFileStatistic::CFileStatistic() :
61 requested(0),
62 transferred(0),
63 accepted(0),
64 alltimerequested(0),
65 alltimetransferred(0),
66 alltimeaccepted(0)
70 #ifndef CLIENT_GUI
72 void CFileStatistic::AddRequest(){
73 requested++;
74 alltimerequested++;
75 theApp->knownfiles->requested++;
76 theApp->sharedfiles->UpdateItem(fileParent);
79 void CFileStatistic::AddAccepted(){
80 accepted++;
81 alltimeaccepted++;
82 theApp->knownfiles->accepted++;
83 theApp->sharedfiles->UpdateItem(fileParent);
86 void CFileStatistic::AddTransferred(uint64 bytes){
87 transferred += bytes;
88 alltimetransferred += bytes;
89 theApp->knownfiles->transferred += bytes;
90 theApp->sharedfiles->UpdateItem(fileParent);
93 #endif // CLIENT_GUI
96 /* Abstract File (base class)*/
98 CAbstractFile::CAbstractFile()
100 m_iRating(0),
101 m_hasComment(false),
102 m_iUserRating(0),
103 m_nFileSize(0)
108 CAbstractFile::CAbstractFile(const CAbstractFile& other)
110 m_abyFileHash(other.m_abyFileHash),
111 m_strComment(other.m_strComment),
112 m_iRating(other.m_iRating),
113 m_hasComment(other.m_hasComment),
114 m_iUserRating(other.m_iUserRating),
115 m_taglist(other.m_taglist),
116 m_nFileSize(other.m_nFileSize),
117 m_fileName(other.m_fileName)
119 /* // TODO: Currently it's not safe to duplicate the entries, but isn't needed either.
120 CKadEntryPtrList::const_iterator it = other.m_kadNotes.begin();
121 for (; it != other.m_kadNotes.end(); ++it) {
122 m_kadNotes.push_back(new Kademlia::CEntry(**it));
128 void CAbstractFile::SetFileName(const CPath& fileName)
130 m_fileName = fileName;
133 uint32 CAbstractFile::GetIntTagValue(uint8 tagname) const
135 ArrayOfCTag::const_iterator it = m_taglist.begin();
136 for (; it != m_taglist.end(); ++it){
137 if (((*it).GetNameID() == tagname) && (*it).IsInt()) {
138 return (*it).GetInt();
141 return 0;
144 bool CAbstractFile::GetIntTagValue(uint8 tagname, uint32& ruValue) const
146 ArrayOfCTag::const_iterator it = m_taglist.begin();
147 for (; it != m_taglist.end(); ++it){
148 if (((*it).GetNameID() == tagname) && (*it).IsInt()){
149 ruValue = (*it).GetInt();
150 return true;
153 return false;
156 uint32 CAbstractFile::GetIntTagValue(const wxString& tagname) const
158 ArrayOfCTag::const_iterator it = m_taglist.begin();
159 for (; it != m_taglist.end(); ++it){
160 if ((*it).IsInt() && ((*it).GetName() == tagname)) {
161 return (*it).GetInt();
164 return 0;
167 const wxString& CAbstractFile::GetStrTagValue(uint8 tagname) const
169 ArrayOfCTag::const_iterator it = m_taglist.begin();
170 for (; it != m_taglist.end(); ++it){
171 if ((*it).GetNameID() == tagname && (*it).IsStr()) {
172 return (*it).GetStr();
175 return EmptyString;
178 const wxString& CAbstractFile::GetStrTagValue(const wxString& tagname) const
180 ArrayOfCTag::const_iterator it = m_taglist.begin();
181 for (; it != m_taglist.end(); ++it){
182 if ((*it).IsStr() && ((*it).GetName() == tagname)) {
183 return (*it).GetStr();
186 return EmptyString;
189 const CTag *CAbstractFile::GetTag(uint8 tagname, uint8 tagtype) const
191 ArrayOfCTag::const_iterator it = m_taglist.begin();
192 for (; it != m_taglist.end(); ++it){
193 if ((*it).GetNameID() == tagname && (*it).GetType() == tagtype) {
194 return &(*it);
197 return NULL;
200 const CTag *CAbstractFile::GetTag(const wxString& tagname, uint8 tagtype) const
202 ArrayOfCTag::const_iterator it = m_taglist.begin();
203 for (; it != m_taglist.end(); ++it){
204 if ((*it).GetType() == tagtype && (*it).GetName() == tagname) {
205 return &(*it);
208 return NULL;
211 const CTag *CAbstractFile::GetTag(uint8 tagname) const
213 ArrayOfCTag::const_iterator it = m_taglist.begin();
214 for (; it != m_taglist.end(); ++it){
215 if ((*it).GetNameID() == tagname) {
216 return &(*it);
219 return NULL;
222 const CTag *CAbstractFile::GetTag(const wxString& tagname) const
224 ArrayOfCTag::const_iterator it = m_taglist.begin();
225 for (; it != m_taglist.end(); ++it){
226 if ((*it).GetName() == tagname) {
227 return &(*it);
230 return NULL;
233 void CAbstractFile::AddTagUnique(const CTag &rTag)
235 ArrayOfCTag::iterator it = m_taglist.begin();
236 for (; it != m_taglist.end(); ++it) {
237 if ( ( ((*it).GetNameID() != 0 &&
238 (*it).GetNameID() == rTag.GetNameID()) ||
239 (!(*it).GetName().IsEmpty() &&
240 !rTag.GetName().IsEmpty() &&
241 (*it).GetName() == rTag.GetName()) ) &&
242 (*it).GetType() == rTag.GetType()){
243 m_taglist.erase(it);
244 m_taglist.insert(it, rTag);
245 return;
248 m_taglist.push_back(rTag);
251 #ifndef CLIENT_GUI
252 void CAbstractFile::AddNote(Kademlia::CEntry *pEntry)
254 CKadEntryPtrList::iterator it = m_kadNotes.begin();
255 for (; it != m_kadNotes.end(); ++it) {
256 Kademlia::CEntry* entry = *it;
257 if(entry->m_uIP == pEntry->m_uIP || entry->m_uSourceID == pEntry->m_uSourceID) {
258 delete pEntry;
259 return;
262 m_kadNotes.push_front(pEntry);
264 #else
265 void CAbstractFile::AddNote(Kademlia::CEntry *)
268 #endif
271 /* Known File */
273 CKnownFile::CKnownFile()
275 Init();
278 CKnownFile::CKnownFile(uint32 ecid) : CECID(ecid)
280 Init();
284 //#warning Experimental: Construct a CKnownFile from a CSearchFile
285 CKnownFile::CKnownFile(const CSearchFile &searchFile)
287 // This will copy the file hash
288 CAbstractFile(static_cast<const CAbstractFile &>(searchFile))
290 Init();
292 // Use CKnownFile::SetFileName()
293 SetFileName(searchFile.GetFileName());
295 // Use CKnownFile::SetFileSize()
296 SetFileSize(searchFile.GetFileSize());
300 void CKnownFile::Init()
302 m_showSources = false;
303 m_nCompleteSourcesTime = time(NULL);
304 m_nCompleteSourcesCount = 0;
305 m_nCompleteSourcesCountLo = 0;
306 m_nCompleteSourcesCountHi = 0;
307 m_bCommentLoaded = false;
308 m_iPartCount = 0;
309 m_iED2KPartCount = 0;
310 m_iED2KPartHashCount = 0;
311 m_PublishedED2K = false;
312 kadFileSearchID = 0;
313 m_lastPublishTimeKadSrc = 0;
314 m_lastPublishTimeKadNotes = 0;
315 m_lastBuddyIP = 0;
316 m_lastDateChanged = 0;
317 m_bAutoUpPriority = thePrefs::GetNewAutoUp();
318 m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
320 statistic.fileParent = this;
322 #ifndef CLIENT_GUI
323 m_pAICHHashSet = new CAICHHashSet(this);
324 #endif
328 void CKnownFile::SetFileSize(uint64 nFileSize)
330 CAbstractFile::SetFileSize(nFileSize);
331 #ifndef CLIENT_GUI
332 m_pAICHHashSet->SetFileSize(nFileSize);
333 #endif
335 // Examples of parthashs, hashsets and filehashs for different filesizes
336 // according the ed2k protocol
337 //----------------------------------------------------------------------
339 //File size: 3 bytes
340 //File hash: 2D55E87D0E21F49B9AD25F98531F3724
341 //Nr. hashs: 0
344 //File size: 1*PARTSIZE
345 //File hash: A72CA8DF7F07154E217C236C89C17619
346 //Nr. hashs: 2
347 //Hash[ 0]: 4891ED2E5C9C49F442145A3A5F608299
348 //Hash[ 1]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
351 //File size: 1*PARTSIZE + 1 byte
352 //File hash: 2F620AE9D462CBB6A59FE8401D2B3D23
353 //Nr. hashs: 2
354 //Hash[ 0]: 121795F0BEDE02DDC7C5426D0995F53F
355 //Hash[ 1]: C329E527945B8FE75B3C5E8826755747
358 //File size: 2*PARTSIZE
359 //File hash: A54C5E562D5E03CA7D77961EB9A745A4
360 //Nr. hashs: 3
361 //Hash[ 0]: B3F5CE2A06BF403BFB9BFFF68BDDC4D9
362 //Hash[ 1]: 509AA30C9EA8FC136B1159DF2F35B8A9
363 //Hash[ 2]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
366 //File size: 3*PARTSIZE
367 //File hash: 5E249B96F9A46A18FC2489B005BF2667
368 //Nr. hashs: 4
369 //Hash[ 0]: 5319896A2ECAD43BF17E2E3575278E72
370 //Hash[ 1]: D86EF157D5E49C5ED502EDC15BB5F82B
371 //Hash[ 2]: 10F2D5B1FCB95C0840519C58D708480F
372 //Hash[ 3]: 31D6CFE0D16AE931B73C59D7E0C089C0 *special part hash*
375 //File size: 3*PARTSIZE + 1 byte
376 //File hash: 797ED552F34380CAFF8C958207E40355
377 //Nr. hashs: 4
378 //Hash[ 0]: FC7FD02CCD6987DCF1421F4C0AF94FB8
379 //Hash[ 1]: 2FE466AF8A7C06DA3365317B75A5ACFE
380 //Hash[ 2]: 873D3BF52629F7C1527C6E8E473C1C30
381 //Hash[ 3]: BCE50BEE7877BB07BB6FDA56BFE142FB
384 // File size Data parts ED2K parts ED2K part hashs
385 // ---------------------------------------------------------------
386 // 1..PARTSIZE-1 1 1 0(!)
387 // PARTSIZE 1 2(!) 2(!)
388 // PARTSIZE+1 2 2 2
389 // PARTSIZE*2 2 3(!) 3(!)
390 // PARTSIZE*2+1 3 3 3
392 if (nFileSize == 0){
393 //wxFAIL; // Kry - Why commented out by lemonfan? it can never be 0
394 m_iPartCount = 0;
395 m_iED2KPartCount = 0;
396 m_iED2KPartHashCount = 0;
397 m_sizeLastPart = 0;
398 return;
401 // nr. of data parts
402 m_iPartCount = nFileSize / PARTSIZE + 1;
403 // size of last part
404 m_sizeLastPart = nFileSize % PARTSIZE;
405 // file with size of n * PARTSIZE
406 if (m_sizeLastPart == 0) {
407 m_sizeLastPart = PARTSIZE;
408 m_iPartCount--;
411 // nr. of parts to be used with OP_FILESTATUS
412 m_iED2KPartCount = nFileSize / PARTSIZE + 1;
414 // nr. of parts to be used with OP_HASHSETANSWER
415 m_iED2KPartHashCount = nFileSize / PARTSIZE;
416 if (m_iED2KPartHashCount != 0) {
417 m_iED2KPartHashCount += 1;
422 #ifdef CLIENT_GUI
424 CKnownFile::CKnownFile(CEC_SharedFile_Tag *tag) : CECID(tag->ID())
426 Init();
428 SetFileName(CPath(tag->FileName()));
429 m_abyFileHash = tag->FileHash();
430 SetFileSize(tag->SizeFull());
431 m_AvailPartFrequency.insert(m_AvailPartFrequency.end(), m_iPartCount, 0);
432 m_iUpPriorityEC = tag->UpPrio();
433 m_AICHMasterHash = tag->GetAICHHash();
434 m_filePath = CPath(tag->FilePath());
435 m_queuedCount = 0;
438 CKnownFile::~CKnownFile()
442 #else // ! CLIENT_GUI
444 CKnownFile::~CKnownFile()
446 SourceSet::iterator it = m_ClientUploadList.begin();
447 for ( ; it != m_ClientUploadList.end(); ++it ) {
448 (*it)->ClearUploadFileID();
451 delete m_pAICHHashSet;
454 void CKnownFile::AddUploadingClient(CUpDownClient* client)
456 m_ClientUploadList.insert(client);
458 SourceItemType type = UNAVAILABLE_SOURCE;
459 switch (client->GetUploadState()) {
460 case US_UPLOADING:
461 case US_ONUPLOADQUEUE:
462 type = AVAILABLE_SOURCE;
463 break;
464 default: {
465 // Any other state is UNAVAILABLE_SOURCE by default.
469 Notify_SharedCtrlAddClient(this, client, type);
471 UpdateAutoUpPriority();
475 void CKnownFile::RemoveUploadingClient(CUpDownClient* client)
477 if (m_ClientUploadList.erase(client)) {
478 Notify_SharedCtrlRemoveClient(this, client);
479 UpdateAutoUpPriority();
484 void CKnownFile::SetFilePath(const CPath& filePath)
486 m_filePath = filePath;
490 // needed for memfiles. its probably better to switch everything to CFile...
491 bool CKnownFile::LoadHashsetFromFile(const CFileDataIO* file, bool checkhash)
493 CMD4Hash checkid = file->ReadHash();
495 uint16 parts = file->ReadUInt16();
496 for (uint16 i = 0; i < parts; ++i){
497 CMD4Hash cur_hash = file->ReadHash();
498 m_hashlist.push_back(cur_hash);
501 // SLUGFILLER: SafeHash - always check for valid m_hashlist
502 if (!checkhash){
503 m_abyFileHash = checkid;
504 if (parts <= 1) { // nothing to check
505 return true;
507 } else {
508 if ( m_abyFileHash != checkid ) {
509 return false; // wrong file?
510 } else {
511 if (parts != GetED2KPartHashCount()) {
512 return false;
516 // SLUGFILLER: SafeHash
518 // trust noone ;-)
519 // lol, useless comment but made me lmao
520 // wtf you guys are weird.
522 if (!m_hashlist.empty()) {
523 CreateHashFromHashlist(m_hashlist, &checkid);
526 if ( m_abyFileHash == checkid ) {
527 return true;
528 } else {
529 m_hashlist.clear();
530 return false;
535 bool CKnownFile::LoadTagsFromFile(const CFileDataIO* file)
537 uint32 tagcount = file->ReadUInt32();
538 for (uint32 j = 0; j != tagcount; ++j) {
539 CTag newtag(*file, true);
540 switch(newtag.GetNameID()){
541 case FT_FILENAME:
542 if (GetFileName().IsOk()) {
543 // Unlike eMule, we actually prefer the second
544 // filename tag, since we use it to specify the
545 // 'universial' filename (see CPath::ToUniv).
546 CPath path = CPath::FromUniv(newtag.GetStr());
548 // May be invalid, if from older versions where
549 // unicoded filenames be saved as empty-strings.
550 if (path.IsOk()) {
551 SetFileName(path);
553 } else {
554 SetFileName(CPath(newtag.GetStr()));
556 break;
558 case FT_FILESIZE:
559 SetFileSize(newtag.GetInt());
560 m_AvailPartFrequency.clear();
561 m_AvailPartFrequency.insert(
562 m_AvailPartFrequency.begin(),
563 GetPartCount(), 0);
564 break;
566 case FT_ATTRANSFERRED:
567 statistic.alltimetransferred += newtag.GetInt();
568 break;
570 case FT_ATTRANSFERREDHI:
571 statistic.alltimetransferred =
572 (((uint64)newtag.GetInt()) << 32) +
573 ((uint64)statistic.alltimetransferred);
574 break;
576 case FT_ATREQUESTED:
577 statistic.alltimerequested = newtag.GetInt();
578 break;
580 case FT_ATACCEPTED:
581 statistic.alltimeaccepted = newtag.GetInt();
582 break;
584 case FT_ULPRIORITY:
585 m_iUpPriority = newtag.GetInt();
586 if( m_iUpPriority == PR_AUTO ){
587 m_iUpPriority = PR_HIGH;
588 m_bAutoUpPriority = true;
589 } else {
590 if ( m_iUpPriority != PR_VERYLOW &&
591 m_iUpPriority != PR_LOW &&
592 m_iUpPriority != PR_NORMAL &&
593 m_iUpPriority != PR_HIGH &&
594 m_iUpPriority != PR_VERYHIGH &&
595 m_iUpPriority != PR_POWERSHARE) {
596 m_iUpPriority = PR_NORMAL;
599 m_bAutoUpPriority = false;
601 break;
603 case FT_PERMISSIONS:
604 // Ignore it, it's not used anymore.
605 break;
607 case FT_AICH_HASH: {
608 CAICHHash hash;
609 bool hashSizeOk =
610 hash.DecodeBase32(newtag.GetStr()) == CAICHHash::GetHashSize();
611 wxASSERT(hashSizeOk);
612 if (hashSizeOk) {
613 m_pAICHHashSet->SetMasterHash(hash, AICH_HASHSETCOMPLETE);
615 break;
618 case FT_KADLASTPUBLISHSRC:
619 SetLastPublishTimeKadSrc( newtag.GetInt(), 0 );
621 if(GetLastPublishTimeKadSrc() > (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES) {
622 //There may be a posibility of an older client that saved a random number here.. This will check for that..
623 SetLastPublishTimeKadSrc(0, 0);
625 break;
627 case FT_KADLASTPUBLISHNOTES:
628 SetLastPublishTimeKadNotes( newtag.GetInt() );
629 break;
631 case FT_KADLASTPUBLISHKEY:
632 // Just purge it
633 wxASSERT( newtag.IsInt() );
634 break;
636 default:
637 // Store them here and write them back on saving.
638 m_taglist.push_back(newtag);
642 return true;
646 bool CKnownFile::LoadDateFromFile(const CFileDataIO* file)
648 m_lastDateChanged = file->ReadUInt32();
650 return true;
654 bool CKnownFile::LoadFromFile(const CFileDataIO* file)
656 // SLUGFILLER: SafeHash - load first, verify later
657 bool ret1 = LoadDateFromFile(file);
658 bool ret2 = LoadHashsetFromFile(file,false);
659 bool ret3 = LoadTagsFromFile(file);
660 UpdatePartsInfo();
661 // Final hash-count verification, needs to be done after the tags are loaded.
662 return ret1 && ret2 && ret3 && GetED2KPartHashCount()==GetHashCount();
663 // SLUGFILLER: SafeHash
667 bool CKnownFile::WriteToFile(CFileDataIO* file)
669 wxCHECK(!IsPartFile(), false);
671 // date
672 file->WriteUInt32(m_lastDateChanged);
673 // hashset
674 file->WriteHash(m_abyFileHash);
676 uint16 parts = m_hashlist.size();
677 file->WriteUInt16(parts);
679 for (int i = 0; i < parts; ++i)
680 file->WriteHash(m_hashlist[i]);
682 //tags
683 const int iFixedTags = 8;
684 uint32 tagcount = iFixedTags;
685 if (HasProperAICHHashSet()) {
686 tagcount++;
688 // Float meta tags are currently not written. All older eMule versions < 0.28a have
689 // a bug in the meta tag reading+writing code. To achive maximum backward
690 // compatibility for met files with older eMule versions we just don't write float
691 // tags. This is OK, because we (eMule) do not use float tags. The only float tags
692 // we may have to handle is the '# Sent' tag from the Hybrid, which is pretty
693 // useless but may be received from us via the servers.
695 // The code for writing the float tags SHOULD BE ENABLED in SOME MONTHS (after most
696 // people are using the newer eMule versions which do not write broken float tags).
697 for (size_t j = 0; j < m_taglist.size(); ++j){
698 if (m_taglist[j].IsInt() || m_taglist[j].IsStr()) {
699 ++tagcount;
703 if (m_lastPublishTimeKadSrc) {
704 ++tagcount;
707 if (m_lastPublishTimeKadNotes){
708 ++tagcount;
711 // standard tags
713 file->WriteUInt32(tagcount);
715 // We still save the unicoded filename, for backwards
716 // compatibility with pre-2.2 and other clients.
717 CTagString nametag_unicode(FT_FILENAME, GetFileName().GetRaw());
718 // We write it with BOM to kep eMule compatibility
719 nametag_unicode.WriteTagToFile(file,utf8strOptBOM);
721 // The non-unicoded filename is written in an 'universial'
722 // format, which allows us to identify files, even if the
723 // system locale changes.
724 CTagString nametag(FT_FILENAME, CPath::ToUniv(GetFileName()));
725 nametag.WriteTagToFile(file);
727 CTagIntSized sizetag(FT_FILESIZE, GetFileSize(), IsLargeFile() ? 64 : 32);
728 sizetag.WriteTagToFile(file);
730 // statistic
731 uint32 tran;
732 tran=statistic.alltimetransferred & 0xFFFFFFFF;
733 CTagInt32 attag1(FT_ATTRANSFERRED, tran);
734 attag1.WriteTagToFile(file);
736 tran=statistic.alltimetransferred>>32;
737 CTagInt32 attag4(FT_ATTRANSFERREDHI, tran);
738 attag4.WriteTagToFile(file);
740 CTagInt32 attag2(FT_ATREQUESTED, statistic.GetAllTimeRequests());
741 attag2.WriteTagToFile(file);
743 CTagInt32 attag3(FT_ATACCEPTED, statistic.GetAllTimeAccepts());
744 attag3.WriteTagToFile(file);
746 // priority N permission
747 CTagInt32 priotag(FT_ULPRIORITY, IsAutoUpPriority() ? PR_AUTO : m_iUpPriority);
748 priotag.WriteTagToFile(file);
750 //AICH Filehash
751 if (HasProperAICHHashSet()) {
752 CTagString aichtag(FT_AICH_HASH, m_pAICHHashSet->GetMasterHash().GetString());
753 aichtag.WriteTagToFile(file);
756 // Kad sources
757 if (m_lastPublishTimeKadSrc){
758 CTagInt32 kadLastPubSrc(FT_KADLASTPUBLISHSRC, m_lastPublishTimeKadSrc);
759 kadLastPubSrc.WriteTagToFile(file);
762 // Kad notes
763 if (m_lastPublishTimeKadNotes){
764 CTagInt32 kadLastPubNotes(FT_KADLASTPUBLISHNOTES, m_lastPublishTimeKadNotes);
765 kadLastPubNotes.WriteTagToFile(file);
768 //other tags
769 for (size_t j = 0; j < m_taglist.size(); ++j){
770 if (m_taglist[j].IsInt() || m_taglist[j].IsStr()) {
771 m_taglist[j].WriteTagToFile(file);
774 return true;
778 void CKnownFile::CreateHashFromHashlist(const ArrayOfCMD4Hash& hashes, CMD4Hash* Output)
780 wxCHECK_RET(hashes.size(), wxT("No input to hash from in CreateHashFromHashlist"));
782 std::vector<byte> buffer(hashes.size() * MD4HASH_LENGTH);
783 std::vector<byte>::iterator it = buffer.begin();
785 for (size_t i = 0; i < hashes.size(); ++i) {
786 it = STLCopy_n(hashes[i].GetHash(), MD4HASH_LENGTH, it);
789 CreateHashFromInput(&buffer[0], buffer.size(), Output, NULL);
793 void CKnownFile::CreateHashFromFile(CFileAutoClose& file, uint64 offset, uint32 Length, CMD4Hash* Output, CAICHHashTree* pShaHashOut)
795 wxCHECK_RET(Length, wxT("No input to hash from in CreateHashFromFile"));
797 CFileArea area;
798 area.ReadAt(file, offset, Length);
800 CreateHashFromInput(area.GetBuffer(), Length, Output, pShaHashOut);
801 area.CheckError();
805 void CKnownFile::CreateHashFromInput(const byte* input, uint32 Length, CMD4Hash* Output, CAICHHashTree* pShaHashOut )
807 wxASSERT_MSG(Output || pShaHashOut, wxT("Nothing to do in CreateHashFromInput"));
808 wxCHECK_RET(input, wxT("No input to hash from in CreateHashFromInput"));
809 wxASSERT(Length <= PARTSIZE); // We never hash more than one PARTSIZE
811 CMemFile data(input, Length);
813 uint32 Required = Length;
814 byte X[64*128];
816 uint32 posCurrentEMBlock = 0;
817 uint32 nIACHPos = 0;
818 CScopedPtr<CAICHHashAlgo> pHashAlg(CAICHHashSet::GetNewHashAlgo());
820 // This is all AICH.
821 while (Required >= 64) {
822 uint32 len = Required / 64;
823 if (len > sizeof(X)/(64 * sizeof(X[0]))) {
824 len = sizeof(X)/(64 * sizeof(X[0]));
827 data.Read(&X, len * 64);
829 // SHA hash needs 180KB blocks
830 if (pShaHashOut) {
831 if (nIACHPos + len*64 >= EMBLOCKSIZE) {
832 uint32 nToComplete = EMBLOCKSIZE - nIACHPos;
833 pHashAlg->Add(X, nToComplete);
834 wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE );
835 pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg.get());
836 posCurrentEMBlock += EMBLOCKSIZE;
837 pHashAlg->Reset();
838 pHashAlg->Add(X+nToComplete,(len*64) - nToComplete);
839 nIACHPos = (len*64) - nToComplete;
841 else{
842 pHashAlg->Add(X, len*64);
843 nIACHPos += len*64;
847 Required -= len*64;
849 // bytes to read
850 Required = Length % 64;
851 if (Required != 0){
852 data.Read(&X,Required);
854 if (pShaHashOut != NULL){
855 if (nIACHPos + Required >= EMBLOCKSIZE){
856 uint32 nToComplete = EMBLOCKSIZE - nIACHPos;
857 pHashAlg->Add(X, nToComplete);
858 wxASSERT( nIACHPos + nToComplete == EMBLOCKSIZE );
859 pShaHashOut->SetBlockHash(EMBLOCKSIZE, posCurrentEMBlock, pHashAlg.get());
860 posCurrentEMBlock += EMBLOCKSIZE;
861 pHashAlg->Reset();
862 pHashAlg->Add(X+nToComplete, Required - nToComplete);
863 nIACHPos = Required - nToComplete;
865 else{
866 pHashAlg->Add(X, Required);
867 nIACHPos += Required;
871 if (pShaHashOut != NULL){
872 if(nIACHPos > 0){
873 pShaHashOut->SetBlockHash(nIACHPos, posCurrentEMBlock, pHashAlg.get());
874 posCurrentEMBlock += nIACHPos;
876 wxASSERT( posCurrentEMBlock == Length );
877 wxCHECK2( pShaHashOut->ReCalculateHash(pHashAlg.get(), false), );
880 if (Output != NULL){
881 #ifdef __WEAK_CRYPTO__
882 CryptoPP::Weak::MD4 md4_hasher;
883 #else
884 CryptoPP::MD4 md4_hasher;
885 #endif
886 md4_hasher.CalculateDigest(Output->GetHash(), input, Length);
891 const CMD4Hash& CKnownFile::GetPartHash(uint16 part) const {
892 wxASSERT( part < m_hashlist.size() );
894 return m_hashlist[part];
897 CPacket* CKnownFile::CreateSrcInfoPacket(const CUpDownClient* forClient, uint8 byRequestedVersion, uint16 nRequestedOptions)
899 // Kad reviewed
901 if (m_ClientUploadList.empty()) {
902 return NULL;
905 if ((((CKnownFile*)forClient->GetRequestFile() != this)
906 && ((CKnownFile*)forClient->GetUploadFile() != this)) || forClient->GetUploadFileID() != GetFileHash()) {
907 wxString file1 = _("Unknown");
908 if (forClient->GetRequestFile() && forClient->GetRequestFile()->GetFileName().IsOk()) {
909 file1 = forClient->GetRequestFile()->GetFileName().GetPrintable();
910 } else if (forClient->GetUploadFile() && forClient->GetUploadFile()->GetFileName().IsOk()) {
911 file1 = forClient->GetUploadFile()->GetFileName().GetPrintable();
913 wxString file2 = _("Unknown");
914 if (GetFileName().IsOk()) {
915 file2 = GetFileName().GetPrintable();
917 AddDebugLogLineM(false, logKnownFiles, wxT("File missmatch on source packet (K) Sending: ") + file1 + wxT(" From: ") + file2);
918 return NULL;
921 const BitVector& rcvstatus = forClient->GetUpPartStatus();
922 bool SupportsUploadChunksState = !rcvstatus.empty();
923 //wxASSERT(rcvstatus.size() == GetPartCount()); // Obviously!
924 if (rcvstatus.size() != GetPartCount()) {
925 // Yuck. Same file but different part count? Seriously fucked up.
926 AddDebugLogLineM(false, logKnownFiles, CFormat(wxT("Impossible situation: different partcounts for the same known file: %i (client) and %i (file)")) % rcvstatus.size() % GetPartCount());
927 return NULL;
930 CMemFile data(1024);
932 uint8 byUsedVersion;
933 bool bIsSX2Packet;
934 if (forClient->SupportsSourceExchange2() && byRequestedVersion > 0){
935 // the client uses SourceExchange2 and requested the highest version he knows
936 // and we send the highest version we know, but of course not higher than his request
937 byUsedVersion = std::min(byRequestedVersion, (uint8)SOURCEEXCHANGE2_VERSION);
938 bIsSX2Packet = true;
939 data.WriteUInt8(byUsedVersion);
941 // we don't support any special SX2 options yet, reserved for later use
942 if (nRequestedOptions != 0) {
943 AddDebugLogLineM(false, logKnownFiles, CFormat(wxT("Client requested unknown options for SourceExchange2: %u")) % nRequestedOptions);
945 } else {
946 byUsedVersion = forClient->GetSourceExchange1Version();
947 bIsSX2Packet = false;
948 if (forClient->SupportsSourceExchange2()) {
949 AddDebugLogLineM(false, logKnownFiles, wxT("Client which announced to support SX2 sent SX1 packet instead"));
953 uint16 nCount = 0;
955 data.WriteHash(forClient->GetUploadFileID());
956 data.WriteUInt16(nCount);
957 uint32 cDbgNoSrc = 0;
959 SourceSet::iterator it = m_ClientUploadList.begin();
960 for ( ; it != m_ClientUploadList.end(); it++ ) {
961 const CUpDownClient *cur_src = *it;
963 if ( cur_src->HasLowID() ||
964 cur_src == forClient ||
965 !( cur_src->GetUploadState() == US_UPLOADING ||
966 cur_src->GetUploadState() == US_ONUPLOADQUEUE)) {
967 continue;
970 bool bNeeded = false;
972 if ( SupportsUploadChunksState ) {
973 const BitVector& srcstatus = cur_src->GetUpPartStatus();
974 if ( !srcstatus.empty() ) {
975 //wxASSERT(srcstatus.size() == GetPartCount()); // Obviously!
976 if (srcstatus.size() != GetPartCount()) {
977 continue;
979 if ( cur_src->GetUpPartCount() == forClient->GetUpPartCount() ) {
980 for (int x = 0; x < GetPartCount(); x++ ) {
981 if ( srcstatus.at(x) && !rcvstatus.at(x) ) {
982 // We know the receiving client needs
983 // a chunk from this client.
984 bNeeded = true;
985 break;
989 } else {
990 cDbgNoSrc++;
991 // This client doesn't support upload chunk status.
992 // So just send it and hope for the best.
993 bNeeded = true;
995 } else {
996 // remote client does not support upload chunk status,
997 // search sources which have at least one complete part
998 // we could even sort the list of sources by available
999 // chunks to return as much sources as possible which
1000 // have the most available chunks. but this could be
1001 // a noticeable performance problem.
1002 const BitVector& srcstatus = cur_src->GetUpPartStatus();
1003 if ( !srcstatus.empty() ) {
1004 //wxASSERT(srcstatus.size() == GetPartCount());
1005 if (srcstatus.size() != GetPartCount()) {
1006 continue;
1008 for (int x = 0; x < GetPartCount(); x++ ) {
1009 if ( srcstatus.at(x) ) {
1010 // this client has at least one chunk
1011 bNeeded = true;
1012 break;
1015 } else {
1016 // This client doesn't support upload chunk status.
1017 // So just send it and hope for the best.
1018 bNeeded = true;
1022 if ( bNeeded ) {
1023 nCount++;
1024 uint32 dwID;
1025 if(byUsedVersion >= 3) {
1026 dwID = cur_src->GetUserIDHybrid();
1027 } else {
1028 dwID = cur_src->GetIP();
1030 data.WriteUInt32(dwID);
1031 data.WriteUInt16(cur_src->GetUserPort());
1032 data.WriteUInt32(cur_src->GetServerIP());
1033 data.WriteUInt16(cur_src->GetServerPort());
1035 if (byUsedVersion >= 2) {
1036 data.WriteHash(cur_src->GetUserHash());
1039 if (byUsedVersion >= 4){
1040 // CryptSettings - SourceExchange V4
1041 // 5 Reserved (!)
1042 // 1 CryptLayer Required
1043 // 1 CryptLayer Requested
1044 // 1 CryptLayer Supported
1045 const uint8 uSupportsCryptLayer = cur_src->SupportsCryptLayer() ? 1 : 0;
1046 const uint8 uRequestsCryptLayer = cur_src->RequestsCryptLayer() ? 1 : 0;
1047 const uint8 uRequiresCryptLayer = cur_src->RequiresCryptLayer() ? 1 : 0;
1048 const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0);
1049 data.WriteUInt8(byCryptOptions);
1052 if (nCount > 500) {
1053 break;
1058 if (!nCount) {
1059 return 0;
1062 data.Seek(bIsSX2Packet ? 17 : 16, wxFromStart);
1063 data.WriteUInt16(nCount);
1065 CPacket* result = new CPacket(data, OP_EMULEPROT, bIsSX2Packet ? OP_ANSWERSOURCES2 : OP_ANSWERSOURCES);
1067 if ( result->GetPacketSize() > 354 ) {
1068 result->PackPacket();
1071 return result;
1075 // Updates priority of file if autopriority is activated
1076 void CKnownFile::UpdateAutoUpPriority()
1078 if (IsAutoUpPriority()) {
1079 uint32 queued = GetQueuedCount();
1080 uint8 priority = PR_NORMAL;
1082 if (queued > 20) {
1083 priority = PR_LOW;
1084 } else if (queued > 1) {
1085 priority = PR_NORMAL;
1086 } else {
1087 priority = PR_HIGH;
1090 if (GetUpPriority() != priority) {
1091 SetUpPriority(priority, false);
1092 Notify_SharedFilesUpdateItem(this);
1097 void CKnownFile::SetFileComment(const wxString& strNewComment)
1099 if (m_strComment != strNewComment) {
1100 SetLastPublishTimeKadNotes(0);
1101 wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1103 wxConfigBase* cfg = wxConfigBase::Get();
1104 cfg->Write( strCfgPath + wxT("Comment"), strNewComment);
1106 m_strComment = strNewComment;
1108 SourceSet::iterator it = m_ClientUploadList.begin();
1109 for ( ; it != m_ClientUploadList.end(); it++ ) {
1110 (*it)->SetCommentDirty();
1116 // For File rate
1117 void CKnownFile::SetFileRating(int8 iNewRating)
1119 if (m_iRating != iNewRating) {
1120 SetLastPublishTimeKadNotes(0);
1121 wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1122 wxConfigBase* cfg = wxConfigBase::Get();
1123 cfg->Write( strCfgPath + wxT("Rate"), (int)iNewRating);
1124 m_iRating = iNewRating;
1126 SourceSet::iterator it = m_ClientUploadList.begin();
1127 for ( ; it != m_ClientUploadList.end(); it++ ) {
1128 (*it)->SetCommentDirty();
1134 void CKnownFile::SetUpPriority(uint8 iNewUpPriority, bool m_bsave){
1135 m_iUpPriority = iNewUpPriority;
1136 if( IsPartFile() && m_bsave ) {
1137 ((CPartFile*)this)->SavePartFile();
1141 void CKnownFile::SetPublishedED2K(bool val){
1142 m_PublishedED2K = val;
1143 Notify_SharedFilesUpdateItem(this);
1146 bool CKnownFile::PublishNotes()
1148 if(m_lastPublishTimeKadNotes > (uint32)time(NULL)) {
1149 return false;
1152 if(!GetFileComment().IsEmpty()) {
1153 m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1154 return true;
1157 if(GetFileRating() != 0) {
1158 m_lastPublishTimeKadNotes = (uint32)time(NULL)+KADEMLIAREPUBLISHTIMEN;
1159 return true;
1162 return false;
1165 bool CKnownFile::PublishSrc()
1167 uint32 lastBuddyIP = 0;
1169 if( theApp->IsFirewalled() ) {
1170 CUpDownClient* buddy = theApp->clientlist->GetBuddy();
1171 if( buddy ) {
1172 lastBuddyIP = theApp->clientlist->GetBuddy()->GetIP();
1173 if( lastBuddyIP != m_lastBuddyIP ) {
1174 SetLastPublishTimeKadSrc( (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES, lastBuddyIP );
1175 return true;
1177 } else {
1178 return false;
1182 if(m_lastPublishTimeKadSrc > (uint32)time(NULL)) {
1183 return false;
1186 SetLastPublishTimeKadSrc((uint32)time(NULL)+KADEMLIAREPUBLISHTIMES,lastBuddyIP);
1187 return true;
1191 void CKnownFile::UpdatePartsInfo()
1193 // Cache part count
1194 uint16 partcount = GetPartCount();
1195 bool flag = (time(NULL) - m_nCompleteSourcesTime > 0);
1197 // Ensure the frequency-list is ready
1198 if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1199 m_AvailPartFrequency.clear();
1200 m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1203 if (flag) {
1204 ArrayOfUInts16 count;
1205 count.reserve(m_ClientUploadList.size());
1207 SourceSet::iterator it = m_ClientUploadList.begin();
1208 for ( ; it != m_ClientUploadList.end(); it++ ) {
1209 if ( !(*it)->GetUpPartStatus().empty() && (*it)->GetUpPartCount() == partcount ) {
1210 count.push_back((*it)->GetUpCompleteSourcesCount());
1214 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo = m_nCompleteSourcesCountHi = 0;
1216 if( partcount > 0) {
1217 m_nCompleteSourcesCount = m_AvailPartFrequency[0];
1219 for (uint16 i = 1; i < partcount; ++i) {
1220 if( m_nCompleteSourcesCount > m_AvailPartFrequency[i]) {
1221 m_nCompleteSourcesCount = m_AvailPartFrequency[i];
1224 count.push_back(m_nCompleteSourcesCount);
1226 int32 n = count.size();
1227 if (n > 0) {
1228 std::sort(count.begin(), count.end(), std::less<uint16>());
1230 // calculate range
1231 int i = n >> 1; // (n / 2)
1232 int j = (n * 3) >> 2; // (n * 3) / 4
1233 int k = (n * 7) >> 3; // (n * 7) / 8
1235 // For complete files, trust the people your uploading to more...
1237 // For low guess and normal guess count
1238 // - If we see more sources then the guessed low and
1239 // normal, use what we see.
1240 // - If we see less sources then the guessed low,
1241 // adjust network accounts for 100%, we account for
1242 // 0% with what we see and make sure we are still
1243 // above the normal.
1244 // For high guess
1245 // Adjust 100% network and 0% what we see.
1246 if (n < 20) {
1247 if ( count[i] < m_nCompleteSourcesCount ) {
1248 m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1249 } else {
1250 m_nCompleteSourcesCountLo = count[i];
1252 m_nCompleteSourcesCount= m_nCompleteSourcesCountLo;
1253 m_nCompleteSourcesCountHi = count[j];
1254 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1255 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1257 } else {
1258 // Many sources..
1259 // For low guess
1260 // Use what we see.
1261 // For normal guess
1262 // Adjust network accounts for 100%, we account for
1263 // 0% with what we see and make sure we are still above the low.
1264 // For high guess
1265 // Adjust network accounts for 100%, we account for 0%
1266 // with what we see and make sure we are still above the normal.
1268 m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1269 m_nCompleteSourcesCount = count[j];
1270 if( m_nCompleteSourcesCount < m_nCompleteSourcesCountLo ) {
1271 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo;
1273 m_nCompleteSourcesCountHi= count[k];
1274 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1275 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1279 m_nCompleteSourcesTime = time(NULL) + (60);
1282 Notify_SharedFilesUpdateItem(this);
1286 void CKnownFile::UpdateUpPartsFrequency( CUpDownClient* client, bool increment )
1288 if ( m_AvailPartFrequency.size() != GetPartCount() ) {
1289 m_AvailPartFrequency.clear();
1290 m_AvailPartFrequency.insert(m_AvailPartFrequency.begin(), GetPartCount(), 0);
1291 if ( !increment ) {
1292 return;
1296 const BitVector& freq = client->GetUpPartStatus();
1297 unsigned int size = freq.size();
1298 if ( size != m_AvailPartFrequency.size() ) {
1299 return;
1302 if ( increment ) {
1303 for ( unsigned int i = 0; i < size; ++i ) {
1304 if ( freq[i] ) {
1305 m_AvailPartFrequency[i]++;
1308 } else {
1309 for ( unsigned int i = 0; i < size; ++i ) {
1310 if ( freq[i] ) {
1311 m_AvailPartFrequency[i]--;
1317 void CKnownFile::ClearPriority() {
1318 if ( !m_bAutoUpPriority ) return;
1319 m_iUpPriority = ( m_bAutoUpPriority ) ? PR_HIGH : PR_NORMAL;
1320 UpdateAutoUpPriority();
1323 void GuessAndRemoveExt(CPath& name)
1325 wxString ext = name.GetExt();
1327 // Remove common two-part extensions, such as "tar.gz"
1328 if (ext == wxT("gz") || ext == wxT("bz2")) {
1329 name = name.RemoveExt();
1330 if (name.GetExt() == wxT("tar")) {
1331 name = name.RemoveExt();
1333 // might be an extension if length == 3
1334 // and also remove some common non-three-character extensions
1335 } else if (ext.Length() == 3 ||
1336 ext == wxT("7z") ||
1337 ext == wxT("rm") ||
1338 ext == wxT("jpeg") ||
1339 ext == wxT("mpeg")
1341 name = name.RemoveExt();
1345 void CKnownFile::SetFileName(const CPath& filename)
1347 CAbstractFile::SetFileName(filename);
1348 wordlist.clear();
1349 // Don't publish extension. That'd kill the node indexing e.g. "avi".
1350 CPath tmpName = GetFileName();
1351 GuessAndRemoveExt(tmpName);
1352 Kademlia::CSearchManager::GetWords(tmpName.GetPrintable(), &wordlist);
1355 #endif // CLIENT_GUI
1357 //For File Comment //
1358 void CKnownFile::LoadComment()
1360 #ifndef CLIENT_GUI
1361 wxString strCfgPath = wxT("/") + m_abyFileHash.Encode() + wxT("/");
1363 wxConfigBase* cfg = wxConfigBase::Get();
1365 m_strComment = cfg->Read( strCfgPath + wxT("Comment"), wxEmptyString);
1366 m_iRating = cfg->Read( strCfgPath + wxT("Rate"), 0l);
1367 m_bCommentLoaded = true;
1369 #else
1370 m_strComment = wxEmptyString;
1371 m_bCommentLoaded = true;
1372 m_iRating =0;
1373 #endif
1378 wxString CKnownFile::GetAICHMasterHash() const
1380 #ifdef CLIENT_GUI
1381 return m_AICHMasterHash;
1382 #else
1383 if (HasProperAICHHashSet()) {
1384 return m_pAICHHashSet->GetMasterHash().GetString();
1387 return wxEmptyString;
1388 #endif
1392 bool CKnownFile::HasProperAICHHashSet() const
1394 #ifdef CLIENT_GUI
1395 return m_AICHMasterHash.Length() != 0;
1396 #else
1397 return m_pAICHHashSet->HasValidMasterHash() &&
1398 (m_pAICHHashSet->GetStatus() == AICH_HASHSETCOMPLETE ||
1399 m_pAICHHashSet->GetStatus() == AICH_VERIFIED);
1400 #endif
1403 wxString CKnownFile::GetFeedback() const
1405 return wxString(_("File name")) + wxT(": ") + GetFileName().GetPrintable() + wxT("\n")
1406 + _("File size") + wxT(": ") + CastItoXBytes(GetFileSize()) + wxT("\n")
1407 + _("Share ratio") + wxString::Format(wxT(": %.2f%%\n"), (((double)statistic.GetAllTimeTransferred() / (double)GetFileSize()) * 100.0))
1408 + _("Uploaded") + wxT(": ") + CastItoXBytes(statistic.GetTransferred()) + wxT(" (") + CastItoXBytes(statistic.GetAllTimeTransferred()) + wxT(")\n")
1409 + _("Requested") + CFormat(wxT(": %u (%u)\n")) % statistic.GetRequests() % statistic.GetAllTimeRequests()
1410 + _("Accepted") + CFormat(wxT(": %u (%u)\n")) % statistic.GetAccepts() % statistic.GetAllTimeAccepts()
1411 + _("On Queue") + CFormat(wxT(": %u\n")) % GetQueuedCount()
1412 + _("Complete sources") + CFormat(wxT(": %u\n")) % m_nCompleteSourcesCount;
1414 // File_checked_for_headers