Upstream tarball 20080522
[amule.git] / src / PartFile.cpp
bloba4f79d50245d78465257dca2dd0ec266379740a5
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <wx/wx.h>
28 #include "PartFile.h" // Interface declarations.
30 #ifdef HAVE_CONFIG_H
31 #include "config.h" // Needed for VERSION
32 #endif
34 #include <protocol/kad/Constants.h>
35 #include <protocol/ed2k/Client2Client/TCP.h>
36 #include <protocol/Protocols.h>
37 #include <common/DataFileVersion.h>
38 #include <common/Constants.h>
39 #include <tags/FileTags.h>
41 #include <wx/utils.h>
42 #include <wx/tokenzr.h> // Needed for wxStringTokenizer
44 #include "KnownFileList.h" // Needed for CKnownFileList
45 #include "UploadQueue.h" // Needed for CFileHash
46 #include "IPFilter.h" // Needed for CIPFilter
47 #include "Server.h" // Needed for CServer
48 #include "ServerConnect.h" // Needed for CServerConnect
49 #include "updownclient.h" // Needed for CUpDownClient
50 #include "MemFile.h" // Needed for CMemFile
51 #include "Preferences.h" // Needed for CPreferences
52 #include "DownloadQueue.h" // Needed for CDownloadQueue
53 #include "amule.h" // Needed for theApp
54 #include "ED2KLink.h" // Needed for CED2KLink
55 #include "Packet.h" // Needed for CTag
56 #include "SearchList.h" // Needed for CSearchFile
57 #include "ClientList.h" // Needed for clientlist
58 #include "Statistics.h" // Needed for theStats
59 #include "Logger.h"
60 #include <common/Format.h> // Needed for CFormat
61 #include <common/FileFunctions.h> // Needed for GetLastModificationTime
62 #include "ThreadTasks.h" // Needed for CHashingTask/CCompletionTask
63 #include "GuiEvents.h" // Needed for Notify_*
64 #include "DataToText.h" // Needed for OriginToText()
65 #include "PlatformSpecific.h" // Needed for CreateSparseFile()
67 #include "kademlia/kademlia/Kademlia.h"
68 #include "kademlia/kademlia/Search.h"
71 SFileRating::SFileRating(const wxString &u, const wxString &f, sint16 r, const wxString &c)
73 UserName(u),
74 FileName(f),
75 Rating(r),
76 Comment(c)
81 SFileRating::SFileRating(const SFileRating &fr)
83 UserName(fr.UserName),
84 FileName(fr.FileName),
85 Rating(fr.Rating),
86 Comment(fr.Comment)
91 SFileRating::SFileRating(const CUpDownClient &client)
93 UserName(client.GetUserName()),
94 FileName(client.GetClientFilename()),
95 Rating(client.GetFileRating()),
96 Comment(client.GetFileComment())
101 SFileRating::~SFileRating()
106 typedef std::list<Chunk> ChunkList;
109 #ifndef CLIENT_GUI
111 CPartFile::CPartFile()
113 Init();
116 CPartFile::CPartFile(CSearchFile* searchresult)
118 Init();
120 m_abyFileHash = searchresult->GetFileHash();
121 SetFileName(searchresult->GetFileName());
122 SetFileSize(searchresult->GetFileSize());
124 for (unsigned int i = 0; i < searchresult->m_taglist.size(); ++i){
125 const CTag& pTag = searchresult->m_taglist[i];
127 bool bTagAdded = false;
128 if (pTag.GetNameID() == 0 && !pTag.GetName().IsEmpty() && (pTag.IsStr() || pTag.IsInt())) {
129 static const struct {
130 wxString pszName;
131 uint8 nType;
132 } _aMetaTags[] =
134 { wxT(FT_ED2K_MEDIA_ARTIST), 2 },
135 { wxT(FT_ED2K_MEDIA_ALBUM), 2 },
136 { wxT(FT_ED2K_MEDIA_TITLE), 2 },
137 { wxT(FT_ED2K_MEDIA_LENGTH), 2 },
138 { wxT(FT_ED2K_MEDIA_BITRATE), 3 },
139 { wxT(FT_ED2K_MEDIA_CODEC), 2 }
142 for (unsigned int t = 0; t < itemsof(_aMetaTags); ++t) {
143 if ( pTag.GetType() == _aMetaTags[t].nType &&
144 (pTag.GetName() == _aMetaTags[t].pszName)) {
145 // skip string tags with empty string values
146 if (pTag.IsStr() && pTag.GetStr().IsEmpty()) {
147 break;
150 // skip "length" tags with "0: 0" values
151 if (pTag.GetName() == wxT(FT_ED2K_MEDIA_LENGTH)) {
152 if (pTag.GetStr().IsSameAs(wxT("0: 0")) ||
153 pTag.GetStr().IsSameAs(wxT("0:0"))) {
154 break;
158 // skip "bitrate" tags with '0' values
159 if ((pTag.GetName() == wxT(FT_ED2K_MEDIA_BITRATE)) && !pTag.GetInt()) {
160 break;
163 AddDebugLogLineM( false, logPartFile,
164 wxT("CPartFile::CPartFile(CSearchFile*): added tag ") +
165 pTag.GetFullInfo() );
166 m_taglist.push_back(pTag);
167 bTagAdded = true;
168 break;
171 } else if (pTag.GetNameID() != 0 && pTag.GetName().IsEmpty() && (pTag.IsStr() || pTag.IsInt())) {
172 static const struct {
173 uint8 nID;
174 uint8 nType;
175 } _aMetaTags[] =
177 { FT_FILETYPE, 2 },
178 { FT_FILEFORMAT, 2 }
180 for (unsigned int t = 0; t < itemsof(_aMetaTags); ++t) {
181 if (pTag.GetType() == _aMetaTags[t].nType && pTag.GetNameID() == _aMetaTags[t].nID) {
182 // skip string tags with empty string values
183 if (pTag.IsStr() && pTag.GetStr().IsEmpty()) {
184 break;
187 AddDebugLogLineM( false, logPartFile,
188 wxT("CPartFile::CPartFile(CSearchFile*): added tag ") +
189 pTag.GetFullInfo() );
190 m_taglist.push_back(pTag);
191 bTagAdded = true;
192 break;
197 if (!bTagAdded) {
198 AddDebugLogLineM( false, logPartFile,
199 wxT("CPartFile::CPartFile(CSearchFile*): ignored tag ") +
200 pTag.GetFullInfo() );
204 CreatePartFile();
208 CPartFile::CPartFile(const CED2KFileLink* fileLink)
210 Init();
212 SetFileName(CPath(fileLink->GetName()));
213 SetFileSize(fileLink->GetSize());
214 m_abyFileHash = fileLink->GetHashKey();
216 CreatePartFile();
218 if (fileLink->m_hashset) {
219 if (!LoadHashsetFromFile(fileLink->m_hashset, true)) {
220 AddDebugLogLineM(true, logPartFile, wxT("eD2K link contained invalid hashset: ") + fileLink->GetLink());
226 CPartFile::~CPartFile()
229 // Barry - Ensure all buffered data is written
231 // Kry - WTF?
232 // eMule had same problem with lseek error ... and override with a simple
233 // check for INVALID_HANDLE_VALUE (that, btw, does not exist on linux)
234 // So we just guess is < 0 on error and > 2 if ok (0 stdin, 1 stdout, 2 stderr)
235 // But, where does this wrong handle comes from?
237 if (m_hpartfile.IsOpened() && (m_hpartfile.fd() > 2)) {
238 FlushBuffer(true);
241 if (m_hpartfile.IsOpened() && (m_hpartfile.fd() > 2)) {
242 m_hpartfile.Close();
243 // Update met file (with current directory entry)
244 SavePartFile();
247 DeleteContents(m_gaplist);
249 std::list<PartFileBufferedData*>::iterator it = m_BufferedData_list.begin();
250 for (; it != m_BufferedData_list.end(); ++it) {
251 PartFileBufferedData* item = *it;
253 delete[] item->data;
254 delete item;
257 wxASSERT(m_SrcList.empty());
258 wxASSERT(m_A4AFsrclist.empty());
261 void CPartFile::CreatePartFile()
263 // use lowest free partfilenumber for free file (InterCeptor)
264 int i = 0;
265 do {
266 ++i;
267 m_partmetfilename = CPath(wxString::Format(wxT("%03i.part.met"), i));
268 m_fullname = thePrefs::GetTempDir().JoinPaths(m_partmetfilename);
269 } while (m_fullname.FileExists());
271 wxString strPartName = m_partmetfilename.RemoveExt().GetRaw();
272 m_taglist.push_back(CTagString(FT_PARTFILENAME, strPartName ));
274 Gap_Struct* gap = new Gap_Struct;
275 gap->start = 0;
276 gap->end = GetFileSize() - 1;
278 m_gaplist.push_back(gap);
280 CPath partPath = m_fullname.RemoveExt();
281 if (PlatformSpecific::CreateSparseFile(partPath, GetFileSize())) {
283 if(!m_hpartfile.Open(partPath, CFile::read_write)) {
284 AddLogLineM(false,_("ERROR: Failed to open partfile)"));
285 SetPartFileStatus(PS_ERROR);
287 } else {
288 AddLogLineM(false,_("ERROR: Failed to create partfile)"));
289 SetPartFileStatus(PS_ERROR);
292 SetFilePath(thePrefs::GetTempDir());
294 if (thePrefs::GetAllocFullPart()) {
295 //#warning Code for full file alloc - should be done on thread.
299 m_hashsetneeded = (GetED2KPartHashCount() > 0);
301 SavePartFile(true);
302 SetActive(theApp->IsConnected());
306 uint8 CPartFile::LoadPartFile(const CPath& in_directory, const CPath& filename, bool from_backup, bool getsizeonly)
308 bool isnewstyle = false;
309 uint8 version,partmettype=PMT_UNKNOWN;
311 std::map<uint16, Gap_Struct*> gap_map; // Slugfiller
312 transferred = 0;
314 m_partmetfilename = filename;
315 m_filePath = in_directory;
316 m_fullname = m_filePath.JoinPaths(m_partmetfilename);
318 // readfile data form part.met file
319 CPath curMetFilename = m_fullname;
320 if (from_backup) {
321 curMetFilename = curMetFilename.AppendExt(PARTMET_BAK_EXT);
322 AddLogLineM(false, CFormat( _("Trying to load backup of met-file from %s") )
323 % curMetFilename );
326 try {
327 CFile metFile(curMetFilename, CFile::read);
328 if (!metFile.IsOpened()) {
329 AddLogLineM(false, CFormat( _("Error: Failed to open part.met file: %s ==> %s") )
330 % curMetFilename
331 % GetFileName() );
333 return false;
334 } else if (metFile.GetLength() == 0) {
335 AddLogLineM(false, CFormat( _("Error: part.met file is 0 size: %s ==> %s") )
336 % m_partmetfilename
337 % GetFileName() );
339 return false;
342 version = metFile.ReadUInt8();
343 if (version != PARTFILE_VERSION && version != PARTFILE_SPLITTEDVERSION && version != PARTFILE_VERSION_LARGEFILE){
344 metFile.Close();
345 //if (version == 83) return ImportShareazaTempFile(...)
346 AddLogLineM(false, CFormat( _("Error: Invalid part.met fileversion: %s ==> %s") )
347 % m_partmetfilename
348 % GetFileName() );
349 return false;
352 isnewstyle = (version == PARTFILE_SPLITTEDVERSION);
353 partmettype = isnewstyle ? PMT_SPLITTED : PMT_DEFAULTOLD;
355 if (!isnewstyle) {
356 uint8 test[4];
357 metFile.Seek(24, wxFromStart);
358 metFile.Read(test,4);
360 metFile.Seek(1, wxFromStart);
361 if (test[0]==0 && test[1]==0 && test[2]==2 && test[3]==1) {
362 isnewstyle=true; // edonkeys so called "old part style"
363 partmettype=PMT_NEWOLD;
367 if (isnewstyle) {
368 uint32 temp = metFile.ReadUInt32();
370 if (temp==0) { // 0.48 partmets - different again
371 LoadHashsetFromFile(&metFile, false);
372 } else {
373 metFile.Seek(2, wxFromStart);
374 LoadDateFromFile(&metFile);
375 m_abyFileHash = metFile.ReadHash();
378 } else {
379 LoadDateFromFile(&metFile);
380 LoadHashsetFromFile(&metFile, false);
383 uint32 tagcount = metFile.ReadUInt32();
385 for (uint32 j = 0; j < tagcount; ++j) {
386 CTag newtag(metFile,true);
387 if ( !getsizeonly ||
388 (getsizeonly &&
389 (newtag.GetNameID() == FT_FILESIZE ||
390 newtag.GetNameID() == FT_FILENAME))) {
391 switch(newtag.GetNameID()) {
392 case FT_FILENAME: {
393 if (!GetFileName().IsOk()) {
394 // If it's not empty, we already loaded the unicoded one
395 SetFileName(CPath(newtag.GetStr()));
397 break;
399 case FT_LASTSEENCOMPLETE: {
400 lastseencomplete = newtag.GetInt();
401 break;
403 case FT_FILESIZE: {
404 SetFileSize(newtag.GetInt());
405 break;
407 case FT_TRANSFERRED: {
408 transferred = newtag.GetInt();
409 break;
411 case FT_FILETYPE:{
412 //#warning needs setfiletype string
413 //SetFileType(newtag.GetStr());
414 break;
416 case FT_CATEGORY: {
417 m_category = newtag.GetInt();
418 if (m_category > theApp->glob_prefs->GetCatCount() - 1 ) {
419 m_category = 0;
421 break;
423 case FT_OLDDLPRIORITY:
424 case FT_DLPRIORITY: {
425 if (!isnewstyle){
426 m_iDownPriority = newtag.GetInt();
427 if( m_iDownPriority == PR_AUTO ){
428 m_iDownPriority = PR_HIGH;
429 SetAutoDownPriority(true);
431 else{
432 if ( m_iDownPriority != PR_LOW &&
433 m_iDownPriority != PR_NORMAL &&
434 m_iDownPriority != PR_HIGH)
435 m_iDownPriority = PR_NORMAL;
436 SetAutoDownPriority(false);
439 break;
441 case FT_STATUS: {
442 m_paused = (newtag.GetInt() == 1);
443 m_stopped = m_paused;
444 break;
446 case FT_OLDULPRIORITY:
447 case FT_ULPRIORITY: {
448 if (!isnewstyle){
449 SetUpPriority(newtag.GetInt(), false);
450 if( GetUpPriority() == PR_AUTO ){
451 SetUpPriority(PR_HIGH, false);
452 SetAutoUpPriority(true);
453 } else {
454 SetAutoUpPriority(false);
457 break;
459 case FT_KADLASTPUBLISHSRC:{
460 SetLastPublishTimeKadSrc(newtag.GetInt(), 0);
461 if(GetLastPublishTimeKadSrc() > (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES) {
462 //There may be a posibility of an older client that saved a random number here.. This will check for that..
463 SetLastPublishTimeKadSrc(0,0);
465 break;
467 case FT_KADLASTPUBLISHNOTES:{
468 SetLastPublishTimeKadNotes(newtag.GetInt());
469 break;
471 // old tags: as long as they are not needed, take the chance to purge them
472 case FT_PERMISSIONS:
473 case FT_KADLASTPUBLISHKEY:
474 break;
475 case FT_DL_ACTIVE_TIME:
476 if (newtag.IsInt()) {
477 m_nDlActiveTime = newtag.GetInt();
479 break;
480 case FT_CORRUPTEDPARTS: {
481 wxASSERT(m_corrupted_list.empty());
482 wxString strCorruptedParts(newtag.GetStr());
483 wxStringTokenizer tokenizer(strCorruptedParts, wxT(","));
484 while ( tokenizer.HasMoreTokens() ) {
485 wxString token = tokenizer.GetNextToken();
486 unsigned long uPart;
487 if (token.ToULong(&uPart)) {
488 if (uPart < GetPartCount() && !IsCorruptedPart(uPart)) {
489 m_corrupted_list.push_back(uPart);
493 break;
495 case FT_AICH_HASH:{
496 CAICHHash hash;
497 bool hashSizeOk =
498 hash.DecodeBase32(newtag.GetStr()) == CAICHHash::GetHashSize();
499 wxASSERT(hashSizeOk);
500 if (hashSizeOk) {
501 m_pAICHHashSet->SetMasterHash(hash, AICH_VERIFIED);
503 break;
505 case FT_ATTRANSFERRED:{
506 statistic.SetAllTimeTransferred(statistic.GetAllTimeTransferred() + (uint64)newtag.GetInt());
507 break;
509 case FT_ATTRANSFERREDHI:{
510 statistic.SetAllTimeTransferred(statistic.GetAllTimeTransferred() + (((uint64)newtag.GetInt()) << 32));
511 break;
513 case FT_ATREQUESTED:{
514 statistic.SetAllTimeRequests(newtag.GetInt());
515 break;
517 case FT_ATACCEPTED:{
518 statistic.SetAllTimeAccepts(newtag.GetInt());
519 break;
521 default: {
522 // Start Changes by Slugfiller for better exception handling
524 wxCharBuffer tag_ansi_name = newtag.GetName().ToAscii();
525 char gap_mark = tag_ansi_name ? tag_ansi_name[0u] : 0;
526 if ( newtag.IsInt() && (newtag.GetName().Length() > 1) &&
527 ((gap_mark == FT_GAPSTART) ||
528 (gap_mark == FT_GAPEND))) {
529 Gap_Struct *gap = NULL;
530 unsigned long int gapkey;
531 if (newtag.GetName().Mid(1).ToULong(&gapkey)) {
532 if ( gap_map.find( gapkey ) == gap_map.end() ) {
533 gap = new Gap_Struct;
534 gap_map[gapkey] = gap;
535 gap->start = (uint64)-1;
536 gap->end = (uint64)-1;
537 } else {
538 gap = gap_map[ gapkey ];
540 if (gap_mark == FT_GAPSTART) {
541 gap->start = newtag.GetInt();
543 if (gap_mark == FT_GAPEND) {
544 gap->end = newtag.GetInt()-1;
546 } else {
547 printf("Wrong gap map key while reading met file!\n");
548 wxASSERT(0);
550 // End Changes by Slugfiller for better exception handling
551 } else {
552 m_taglist.push_back(newtag);
556 } else {
557 // Nothing. Else, nothing.
561 // load the hashsets from the hybridstylepartmet
562 if (isnewstyle && !getsizeonly && (metFile.GetPosition()<metFile.GetLength()) ) {
563 metFile.Seek(1, wxFromCurrent);
565 uint16 parts=GetPartCount(); // assuming we will get all hashsets
567 for (uint16 i = 0; i < parts && (metFile.GetPosition()+16<metFile.GetLength()); ++i){
568 CMD4Hash cur_hash = metFile.ReadHash();
569 m_hashlist.push_back(cur_hash);
572 CMD4Hash checkhash;
573 if (!m_hashlist.empty()) {
574 CreateHashFromHashlist(m_hashlist, &checkhash);
576 bool flag=false;
577 if (m_abyFileHash == checkhash) {
578 flag=true;
579 } else {
580 m_hashlist.clear();
581 flag=false;
584 } catch (const CInvalidPacket& e) {
585 AddLogLineM(true, CFormat(wxT("Error: %s (%s) is corrupt (bad tags: %s), unable to load file."))
586 % m_partmetfilename
587 % GetFileName()
588 % e.what());
589 return false;
590 } catch (const CIOFailureException& e) {
591 AddDebugLogLineM(true, logPartFile, CFormat( wxT("IO failure while loading '%s': %s") )
592 % m_partmetfilename
593 % e.what() );
594 return false;
595 } catch (const CEOFException& WXUNUSED(e)) {
596 AddLogLineM(true, CFormat( _("Error: %s (%s) is corrupt (wrong tagcount), unable to load file.") )
597 % m_partmetfilename
598 % GetFileName() );
599 AddLogLineM(true, _("Trying to recover file info..."));
601 // Safe file is that who have
602 // - FileSize
603 if (GetFileSize()) {
604 // We have filesize, try other needed info
606 // Do we need to check gaps? I think not,
607 // because they are checked below. Worst
608 // scenario will only mark file as 0 bytes downloaded.
610 // -Filename
611 if (!GetFileName().IsOk()) {
612 // Not critical, let's put a random filename.
613 AddLogLineM(true, _(
614 "Recovering no-named file - will try to recover it as RecoveredFile.dat"));
615 SetFileName(CPath(wxT("RecoveredFile.dat")));
618 AddLogLineM(true,
619 _("Recovered all available file info :D - Trying to use it..."));
620 } else {
621 AddLogLineM(true, _("Unable to recover file info :("));
622 return false;
626 if (getsizeonly) {
627 return partmettype;
629 // Now to flush the map into the list (Slugfiller)
630 std::map<uint16, Gap_Struct*>::iterator it = gap_map.begin();
631 for ( ; it != gap_map.end(); ++it ) {
632 Gap_Struct* gap = it->second;
633 // SLUGFILLER: SafeHash - revised code, and extra safety
634 if ( (gap->start != (uint64)-1) &&
635 (gap->end != (uint64)-1) &&
636 gap->start <= gap->end &&
637 gap->start < GetFileSize()) {
638 if (gap->end >= GetFileSize()) {
639 gap->end = GetFileSize()-1; // Clipping
641 AddGap(gap->start, gap->end); // All tags accounted for, use safe adding
643 delete gap;
644 // SLUGFILLER: SafeHash
647 //check if this is a backup
648 if ( m_fullname.GetExt().MakeLower() == wxT("backup" )) {
649 m_fullname = m_fullname.RemoveExt();
652 // open permanent handle
653 CPath partFilePath = m_fullname.RemoveExt();
654 if ( !m_hpartfile.Open(partFilePath, CFile::read_write)) {
655 AddLogLineM(false, CFormat( _("Failed to open %s (%s)") )
656 % m_fullname
657 % GetFileName() );
658 return false;
661 SetPartFileStatus(PS_EMPTY);
663 try {
664 // SLUGFILLER: SafeHash - final safety, make sure any missing part of the file is gap
665 if (m_hpartfile.GetLength() < GetFileSize())
666 AddGap(m_hpartfile.GetLength(), GetFileSize()-1);
667 // Goes both ways - Partfile should never be too large
668 if (m_hpartfile.GetLength() > GetFileSize()) {
669 AddDebugLogLineM( true, logPartFile, CFormat( wxT("Partfile \"%s\" is too large! Truncating %llu bytes.") ) % GetFileName() % (m_hpartfile.GetLength() - GetFileSize()));
670 m_hpartfile.SetLength(GetFileSize());
672 // SLUGFILLER: SafeHash
673 } catch (const CIOFailureException& e) {
674 AddDebugLogLineM( true, logPartFile, CFormat( wxT("Error while accessing partfile \"%s\": %s") ) % GetFileName() % e.what());
675 SetPartFileStatus(PS_ERROR);
679 // check hashcount, file status etc
680 if (GetHashCount() != GetED2KPartHashCount()){
681 m_hashsetneeded = true;
682 return true;
683 } else {
684 m_hashsetneeded = false;
685 for (size_t i = 0; i < m_hashlist.size(); ++i) {
686 if (IsComplete(i*PARTSIZE,((i+1)*PARTSIZE)-1)) {
687 SetPartFileStatus(PS_READY);
692 if (m_gaplist.empty()) { // is this file complete already?
693 CompleteFile(false);
694 return true;
697 if (!isnewstyle) { // not for importing
698 const time_t file_date = CPath::GetModificationTime(partFilePath);
699 if (m_lastDateChanged != file_date) {
700 // It's pointless to rehash an empty file, since the case
701 // where a user has zero'd a file is handled above ...
702 if (m_hpartfile.GetLength()) {
703 AddLogLineM(false, CFormat( _("Warning: %s might be corrupted (%i)") )
704 % partFilePath
705 % (m_lastDateChanged - file_date) );
706 // rehash
707 SetPartFileStatus(PS_WAITINGFORHASH);
709 CPath partFileName = m_partmetfilename.RemoveExt();
710 CThreadScheduler::AddTask(new CHashingTask(m_filePath, partFileName, this));
715 UpdateCompletedInfos();
716 if (completedsize > transferred) {
717 m_iGainDueToCompression = completedsize - transferred;
718 } else if (completedsize != transferred) {
719 m_iLostDueToCorruption = transferred - completedsize;
722 return true;
726 bool CPartFile::SavePartFile(bool Initial)
728 switch (status) {
729 case PS_WAITINGFORHASH:
730 case PS_HASHING:
731 case PS_COMPLETE:
732 return false;
735 /* Don't write anything to disk if less than 100 KB of free space is left. */
736 sint64 free = CPath::GetFreeSpaceAt(GetFilePath());
737 if ((free != wxInvalidOffset) && (free < (100 * 1024))) {
738 return false;
741 CFile file;
742 try {
743 if (!m_fullname.RemoveExt().FileExists()) {
744 throw wxString(wxT(".part file not found"));
747 uint32 lsc = lastseencomplete;
749 if (!Initial) {
750 CPath::BackupFile(m_fullname, wxT(".backup"));
751 CPath::RemoveFile(m_fullname);
754 file.Open(m_fullname, CFile::write);
755 if (!file.IsOpened()) {
756 throw wxString(wxT("Failed to open part.met file"));
759 // version
760 file.WriteUInt8(IsLargeFile() ? PARTFILE_VERSION_LARGEFILE : PARTFILE_VERSION);
762 file.WriteUInt32(CPath::GetModificationTime(m_fullname.RemoveExt()));
763 // hash
764 file.WriteHash(m_abyFileHash);
765 uint16 parts = m_hashlist.size();
766 file.WriteUInt16(parts);
767 for (int x = 0; x < parts; ++x) {
768 file.WriteHash(m_hashlist[x]);
770 // tags
771 #define FIXED_TAGS 15
772 uint32 tagcount = m_taglist.size() + FIXED_TAGS + (m_gaplist.size()*2);
773 if (!m_corrupted_list.empty()) {
774 ++tagcount;
777 if (m_pAICHHashSet->HasValidMasterHash() && (m_pAICHHashSet->GetStatus() == AICH_VERIFIED)){
778 ++tagcount;
781 if (GetLastPublishTimeKadSrc()){
782 ++tagcount;
785 if (GetLastPublishTimeKadNotes()){
786 ++tagcount;
789 if (GetDlActiveTime()){
790 ++tagcount;
793 file.WriteUInt32(tagcount);
795 //#warning Kry - Where are lost by coruption and gained by compression?
797 // 0 (unicoded part file name)
798 // We write it with BOM to keep eMule compatibility. Note that the 'printable' filename is saved,
799 // as presently the filename does not represent an actual file.
800 CTagString( FT_FILENAME, GetFileName().GetPrintable()).WriteTagToFile( &file, utf8strOptBOM );
801 CTagString( FT_FILENAME, GetFileName().GetPrintable()).WriteTagToFile( &file ); // 1
803 CTagIntSized( FT_FILESIZE, GetFileSize(), IsLargeFile() ? 64 : 32).WriteTagToFile( &file );// 2
804 CTagIntSized( FT_TRANSFERRED, transferred, IsLargeFile() ? 64 : 32).WriteTagToFile( &file ); // 3
805 CTagInt32( FT_STATUS, (m_paused?1:0)).WriteTagToFile( &file ); // 4
807 if ( IsAutoDownPriority() ) {
808 CTagInt32( FT_DLPRIORITY, (uint8)PR_AUTO ).WriteTagToFile( &file ); // 5
809 CTagInt32( FT_OLDDLPRIORITY, (uint8)PR_AUTO ).WriteTagToFile( &file ); // 6
810 } else {
811 CTagInt32( FT_DLPRIORITY, m_iDownPriority ).WriteTagToFile( &file ); // 5
812 CTagInt32( FT_OLDDLPRIORITY, m_iDownPriority ).WriteTagToFile( &file ); // 6
815 CTagInt32( FT_LASTSEENCOMPLETE, lsc ).WriteTagToFile( &file ); // 7
817 if ( IsAutoUpPriority() ) {
818 CTagInt32( FT_ULPRIORITY, (uint8)PR_AUTO ).WriteTagToFile( &file ); // 8
819 CTagInt32( FT_OLDULPRIORITY, (uint8)PR_AUTO ).WriteTagToFile( &file ); // 9
820 } else {
821 CTagInt32( FT_ULPRIORITY, GetUpPriority() ).WriteTagToFile( &file ); // 8
822 CTagInt32( FT_OLDULPRIORITY, GetUpPriority() ).WriteTagToFile( &file ); // 9
825 CTagInt32(FT_CATEGORY, m_category).WriteTagToFile( &file ); // 10
826 CTagInt32(FT_ATTRANSFERRED, statistic.GetAllTimeTransferred() & 0xFFFFFFFF).WriteTagToFile( &file );// 11
827 CTagInt32(FT_ATTRANSFERREDHI, statistic.GetAllTimeTransferred() >>32).WriteTagToFile( &file );// 12
828 CTagInt32(FT_ATREQUESTED, statistic.GetAllTimeRequests()).WriteTagToFile( &file ); // 13
829 CTagInt32(FT_ATACCEPTED, statistic.GetAllTimeAccepts()).WriteTagToFile( &file ); // 14
831 // currupt part infos
832 if (!m_corrupted_list.empty()) {
833 wxString strCorruptedParts;
834 std::list<uint16>::iterator it = m_corrupted_list.begin();
835 for (; it != m_corrupted_list.end(); ++it) {
836 uint16 uCorruptedPart = *it;
837 if (!strCorruptedParts.IsEmpty()) {
838 strCorruptedParts += wxT(",");
840 strCorruptedParts += wxString::Format(wxT("%u"), (unsigned)uCorruptedPart);
842 wxASSERT( !strCorruptedParts.IsEmpty() );
844 CTagString( FT_CORRUPTEDPARTS, strCorruptedParts ).WriteTagToFile( &file); // 11?
847 //AICH Filehash
848 if (m_pAICHHashSet->HasValidMasterHash() && (m_pAICHHashSet->GetStatus() == AICH_VERIFIED)){
849 CTagString aichtag(FT_AICH_HASH, m_pAICHHashSet->GetMasterHash().GetString() );
850 aichtag.WriteTagToFile(&file); // 12?
853 if (GetLastPublishTimeKadSrc()){
854 CTagInt32(FT_KADLASTPUBLISHSRC, GetLastPublishTimeKadSrc()).WriteTagToFile(&file); // 15?
857 if (GetLastPublishTimeKadNotes()){
858 CTagInt32(FT_KADLASTPUBLISHNOTES, GetLastPublishTimeKadNotes()).WriteTagToFile(&file); // 16?
861 if (GetDlActiveTime()){
862 CTagInt32(FT_DL_ACTIVE_TIME, GetDlActiveTime()).WriteTagToFile(&file); // 17
865 for (uint32 j = 0; j < (uint32)m_taglist.size();++j) {
866 m_taglist[j].WriteTagToFile(&file);
869 // gaps
870 unsigned i_pos = 0;
871 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
872 for (; it != m_gaplist.end(); ++it) {
873 wxString tagName = wxString::Format(wxT(" %u"), i_pos);
875 // gap start = first missing byte but gap ends = first non-missing byte
876 // in edonkey but I think its easier to user the real limits
877 tagName[0] = FT_GAPSTART;
878 CTagIntSized(tagName, (*it)->start , IsLargeFile() ? 64 : 32).WriteTagToFile( &file );
880 tagName[0] = FT_GAPEND;
881 CTagIntSized(tagName, ((*it)->end + 1), IsLargeFile() ? 64 : 32).WriteTagToFile( &file );
883 ++i_pos;
885 } catch (const wxString& error) {
886 AddLogLineM(false, CFormat( _("ERROR while saving partfile: %s (%s ==> %s)") )
887 % error
888 % m_partmetfilename
889 % GetFileName() );
891 wxString err = CFormat( _("ERROR while saving partfile: %s (%s ==> %s)") )
892 % error
893 % m_partmetfilename
894 % GetFileName();
896 printf("%s\n", (const char*)unicode2char(err));
898 return false;
899 } catch (const CIOFailureException& e) {
900 AddDebugLogLineM(true, logPartFile, wxT("IO failure while saving partfile: ") + e.what());
901 printf("IO failure while saving partfile: %s\n", (const char*)unicode2char(e.what()));
903 return false;
906 file.Close();
908 if (!Initial) {
909 CPath::RemoveFile(m_fullname.AppendExt(wxT(".backup")));
912 sint64 metLength = m_fullname.GetFileSize();
913 if (metLength == wxInvalidOffset) {
914 theApp->ShowAlert( CFormat( _("Could not retrieve length of '%s' - using %s file.") )
915 % m_fullname
916 % PARTMET_BAK_EXT,
917 _("Message"), wxOK);
919 CPath::CloneFile(m_fullname.AppendExt(PARTMET_BAK_EXT), m_fullname, true);
920 } else if (metLength == 0) {
921 // Don't backup if it's 0 size but raise a warning!!!
922 theApp->ShowAlert( CFormat( _("'%s' is 0 size somehow - using %s file.") )
923 % m_fullname
924 % PARTMET_BAK_EXT,
925 _("Message"), wxOK);
927 CPath::CloneFile(m_fullname.AppendExt(PARTMET_BAK_EXT), m_fullname, true);
928 } else {
929 // no error, just backup
930 CPath::BackupFile(m_fullname, PARTMET_BAK_EXT);
933 return true;
937 void CPartFile::SaveSourceSeeds()
939 #define MAX_SAVED_SOURCES 10
941 // Kry - Sources seeds
942 // Based on a Feature request, this saves the last MAX_SAVED_SOURCES
943 // sources of the file, giving a 'seed' for the next run.
944 // We save the last sources because:
945 // 1 - They could be the hardest to get
946 // 2 - They will more probably be available
947 // However, if we have downloading sources, they have preference because
948 // we probably have more credits on them.
949 // Anyway, source exchange will get us the rest of the sources
950 // This feature is currently used only on rare files (< 20 sources)
953 if (GetSourceCount()>20) {
954 return;
957 CClientPtrList source_seeds;
958 int n_sources = 0;
960 CClientPtrList::iterator it = m_downloadingSourcesList.begin();
961 for( ; it != m_downloadingSourcesList.end() && n_sources < MAX_SAVED_SOURCES; ++it) {
962 CUpDownClient *cur_src = *it;
963 if (!cur_src->HasLowID()) {
964 source_seeds.push_back(cur_src);
965 ++n_sources;
969 if (n_sources < MAX_SAVED_SOURCES) {
970 // Not enough downloading sources to fill the list, going to sources list
971 if (GetSourceCount() > 0) {
972 SourceSet::reverse_iterator rit = m_SrcList.rbegin();
973 for ( ; ((rit != m_SrcList.rend()) && (n_sources<MAX_SAVED_SOURCES)); ++rit) {
974 CUpDownClient* cur_src = *rit;
975 if (!cur_src->HasLowID()) {
976 source_seeds.push_back(cur_src);
977 ++n_sources;
983 // Write the file
984 if (!n_sources) {
985 return;
988 const CPath seedsPath = m_fullname.AppendExt(wxT(".seeds"));
990 CFile file;
991 file.Create(seedsPath, true);
992 if (!file.IsOpened()) {
993 AddLogLineM(false, CFormat( _("Failed to save part.met.seeds file for %s") )
994 % m_fullname);
995 return;
998 try {
999 file.WriteUInt8(0); // v3, to avoid v2 clients choking on it.
1000 file.WriteUInt8(source_seeds.size());
1002 CClientPtrList::iterator it2 = source_seeds.begin();
1003 for (; it2 != source_seeds.end(); ++it2) {
1004 CUpDownClient* cur_src = *it2;
1005 file.WriteUInt32(cur_src->GetUserIDHybrid());
1006 file.WriteUInt16(cur_src->GetUserPort());
1007 file.WriteHash(cur_src->GetUserHash());
1008 // CryptSettings - See SourceExchange V4
1009 const uint8 uSupportsCryptLayer = cur_src->SupportsCryptLayer() ? 1 : 0;
1010 const uint8 uRequestsCryptLayer = cur_src->RequestsCryptLayer() ? 1 : 0;
1011 const uint8 uRequiresCryptLayer = cur_src->RequiresCryptLayer() ? 1 : 0;
1012 const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0);
1013 file.WriteUInt8(byCryptOptions);
1016 /* v2: Added to keep track of too old seeds */
1017 file.WriteUInt32(wxDateTime::Now().GetTicks());
1019 AddLogLineM(false, CFormat( wxPLURAL("Saved %i source seed for partfile: %s (%s)", "Saved %i source seeds for partfile: %s (%s)", n_sources) )
1020 % n_sources
1021 % m_fullname
1022 % GetFileName());
1023 } catch (const CIOFailureException& e) {
1024 AddDebugLogLineM(true, logPartFile, CFormat( wxT("Error saving partfile's seeds file (%s - %s): %s") )
1025 % m_partmetfilename
1026 % GetFileName()
1027 % e.what() );
1029 n_sources = 0;
1030 file.Close();
1031 CPath::RemoveFile(seedsPath);
1035 void CPartFile::LoadSourceSeeds()
1037 CMemFile sources_data;
1039 bool valid_sources = false;
1041 const CPath seedsPath = m_fullname.AppendExt(wxT(".seeds"));
1042 if (!seedsPath.FileExists()) {
1043 return;
1046 CFile file(seedsPath, CFile::read);
1047 if (!file.IsOpened()) {
1048 AddLogLineM(false, CFormat( _("Partfile %s (%s) has no seeds file") )
1049 % m_partmetfilename
1050 % GetFileName() );
1051 return;
1055 try {
1056 if (file.GetLength() <= 1) {
1057 AddLogLineM(false, CFormat( _("Partfile %s (%s) has a void seeds file") )
1058 % m_partmetfilename
1059 % GetFileName() );
1060 return;
1063 uint8 src_count = file.ReadUInt8();
1065 bool bUseSX2Format = (src_count == 0);
1067 if (bUseSX2Format) {
1068 // v3 sources seeds
1069 src_count = file.ReadUInt8();
1072 sources_data.WriteUInt16(src_count);
1074 for (int i = 0; i< src_count; ++i) {
1075 uint32 dwID = file.ReadUInt32();
1076 uint16 nPort = file.ReadUInt16();
1078 sources_data.WriteUInt32(bUseSX2Format ? dwID : wxUINT32_SWAP_ALWAYS(dwID));
1079 sources_data.WriteUInt16(nPort);
1080 sources_data.WriteUInt32(0);
1081 sources_data.WriteUInt16(0);
1083 if (bUseSX2Format) {
1084 sources_data.WriteHash(file.ReadHash());
1085 sources_data.WriteUInt8(file.ReadUInt8());
1090 if (!file.Eof()) {
1092 // v2: Added to keep track of too old seeds
1093 time_t time = (time_t)file.ReadUInt32();
1095 // Time frame is 2 hours. More than enough to compile
1096 // your new aMule version!.
1097 if ((time + MIN2S(120)) >= wxDateTime::Now().GetTicks()) {
1098 valid_sources = true;
1101 } else {
1102 // v1 has no time data. We can safely use
1103 // the sources, next time will be saved.
1104 valid_sources = true;
1107 if (valid_sources) {
1108 sources_data.Seek(0);
1109 AddClientSources(&sources_data, SF_SOURCE_SEEDS, bUseSX2Format ? 4 : 1, bUseSX2Format);
1112 } catch (const CSafeIOException& e) {
1113 AddLogLineM(false, CFormat( _("Error reading partfile's seeds file (%s - %s): %s") )
1114 % m_partmetfilename
1115 % GetFileName()
1116 % e.what() );
1119 file.Close();
1122 void CPartFile::PartFileHashFinished(CKnownFile* result)
1124 m_lastDateChanged = result->m_lastDateChanged;
1125 bool errorfound = false;
1126 if (GetED2KPartHashCount() == 0){
1127 if (IsComplete(0, GetFileSize()-1)){
1128 if (result->GetFileHash() != GetFileHash()){
1129 AddLogLineM(false,
1130 CFormat(wxPLURAL(
1131 "Found corrupted part (%d) in %d part file %s - FileResultHash |%s| FileHash |%s|",
1132 "Found corrupted part (%d) in %d parts file %s - FileResultHash |%s| FileHash |%s|",
1137 % GetFileName()
1138 % result->GetFileHash().Encode()
1139 % GetFileHash().Encode() );
1140 AddGap(0, GetFileSize()-1);
1141 errorfound = true;
1145 else{
1146 for (size_t i = 0; i < m_hashlist.size(); ++i){
1147 // Kry - trel_ar's completed parts check on rehashing.
1148 // Very nice feature, if a file is completed but .part.met don't believe it,
1149 // update it.
1151 if (!( i < result->GetHashCount() && (result->GetPartHash(i) == GetPartHash(i)))){
1152 if (IsComplete(i*PARTSIZE,((i+1)*PARTSIZE)-1)) {
1153 CMD4Hash wronghash;
1154 if ( i < result->GetHashCount() )
1155 wronghash = result->GetPartHash(i);
1157 AddLogLineM(false,
1158 CFormat(wxPLURAL(
1159 "Found corrupted part (%d) in %d part file %s - FileResultHash |%s| FileHash |%s|",
1160 "Found corrupted part (%d) in %d parts file %s - FileResultHash |%s| FileHash |%s|",
1161 GetED2KPartHashCount())
1163 % ( i + 1 )
1164 % GetED2KPartHashCount()
1165 % GetFileName()
1166 % wronghash.Encode()
1167 % GetPartHash(i).Encode() );
1169 AddGap(i*PARTSIZE,
1170 ((uint64)(((i+1)*PARTSIZE)-1) >= GetFileSize()) ?
1171 GetFileSize()-1 : ((i+1)*PARTSIZE)-1);
1172 errorfound = true;
1174 } else {
1175 if (!IsComplete(i*PARTSIZE,((i+1)*PARTSIZE)-1)){
1176 AddLogLineM(false, CFormat( _("Found completed part (%i) in %s") )
1177 % ( i + 1 )
1178 % GetFileName() );
1180 FillGap(i*PARTSIZE,
1181 ((uint64)(((i+1)*PARTSIZE)-1) >= GetFileSize()) ?
1182 GetFileSize()-1 : ((i+1)*PARTSIZE)-1);
1183 RemoveBlockFromList(i*PARTSIZE,
1184 ((uint64)(((i+1)*PARTSIZE)-1) >= GetFileSize()) ?
1185 GetFileSize()-1 : ((i+1)*PARTSIZE)-1);
1191 if ( !errorfound &&
1192 result->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE &&
1193 status == PS_COMPLETING) {
1194 delete m_pAICHHashSet;
1195 m_pAICHHashSet = result->GetAICHHashset();
1196 result->SetAICHHashset(NULL);
1197 m_pAICHHashSet->SetOwner(this);
1199 else if (status == PS_COMPLETING) {
1200 AddDebugLogLineM(false, logPartFile,
1201 CFormat(wxT("Failed to store new AICH Hashset for completed file: %s"))
1202 % GetFileName());
1206 delete result;
1207 if (!errorfound){
1208 if (status == PS_COMPLETING){
1209 CompleteFile(true);
1210 return;
1212 else {
1213 AddLogLineM(false, CFormat( _("Finished rehashing %s") ) % GetFileName());
1216 else{
1217 SetStatus(PS_READY);
1218 SavePartFile();
1219 return;
1221 SetStatus(PS_READY);
1222 SavePartFile();
1223 theApp->sharedfiles->SafeAddKFile(this);
1226 void CPartFile::AddGap(uint64 start, uint64 end)
1228 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
1229 while (it != m_gaplist.end()) {
1230 std::list<Gap_Struct*>::iterator it2 = it++;
1231 Gap_Struct* cur_gap = *it2;
1233 if (cur_gap->start >= start && cur_gap->end <= end) {
1234 // this gap is inside the new gap - delete
1235 m_gaplist.erase(it2);
1236 delete cur_gap;
1237 continue;
1238 } else if (cur_gap->start >= start && cur_gap->start <= end + 1) {
1239 // head of this gap is in the new gap, or this gap is
1240 // directly behind the new gap - extend limit and delete
1241 end = cur_gap->end;
1242 m_gaplist.erase(it2);
1243 delete cur_gap;
1244 continue;
1245 } else if (cur_gap->end <= end && cur_gap->end >= start - 1) {
1246 // tail of this gap is in the new gap, or this gap is
1247 // directly before the new gap - extend limit and delete
1248 start = cur_gap->start;
1249 m_gaplist.erase(it2);
1250 delete cur_gap;
1251 continue;
1252 } else if (start >= cur_gap->start && end <= cur_gap->end){
1253 // new gap is already inside this gap - return
1254 return;
1255 // now all cases of overlap are ruled out
1256 } else if (cur_gap->start > start) {
1257 // this gap is the first behind the new gap -> insert before it
1258 it = it2;
1259 break;
1263 Gap_Struct* new_gap = new Gap_Struct;
1264 new_gap->start = start;
1265 new_gap->end = end;
1266 m_gaplist.insert(it, new_gap);
1267 UpdateDisplayedInfo();
1270 bool CPartFile::IsAlreadyRequested(uint64 start, uint64 end)
1272 std::list<Requested_Block_Struct*>::iterator it = m_requestedblocks_list.begin();
1273 for (; it != m_requestedblocks_list.end(); ++it) {
1274 Requested_Block_Struct* cur_block = *it;
1276 if ((start <= cur_block->EndOffset) && (end >= cur_block->StartOffset)) {
1277 return true;
1280 return false;
1283 bool CPartFile::GetNextEmptyBlockInPart(uint16 partNumber, Requested_Block_Struct *result)
1285 Gap_Struct *firstGap;
1286 Gap_Struct *currentGap;
1287 uint64 end;
1288 uint64 blockLimit;
1290 // Find start of this part
1291 uint64 partStart = (PARTSIZE * partNumber);
1292 uint64 start = partStart;
1294 // What is the end limit of this block, i.e. can't go outside part (or filesize)
1295 uint64 partEnd = (PARTSIZE * (partNumber + 1)) - 1;
1296 if (partEnd >= GetFileSize()) {
1297 partEnd = GetFileSize() - 1;
1299 // Loop until find a suitable gap and return true, or no more gaps and return false
1300 while (true) {
1301 firstGap = NULL;
1303 // Find the first gap from the start position
1304 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
1305 for (; it != m_gaplist.end(); ++it) {
1306 currentGap = *it;
1308 // Want gaps that overlap start<->partEnd
1309 if ((currentGap->start <= partEnd) && (currentGap->end >= start)) {
1310 // Is this the first gap?
1311 if ((firstGap == NULL) || (currentGap->start < firstGap->start)) {
1312 firstGap = currentGap;
1317 // If no gaps after start, exit
1318 if (firstGap == NULL) {
1319 return false;
1321 // Update start position if gap starts after current pos
1322 if (start < firstGap->start) {
1323 start = firstGap->start;
1325 // If this is not within part, exit
1326 if (start > partEnd) {
1327 return false;
1329 // Find end, keeping within the max block size and the part limit
1330 end = firstGap->end;
1331 blockLimit = partStart + (BLOCKSIZE * (((start - partStart) / BLOCKSIZE) + 1)) - 1;
1332 if (end > blockLimit) {
1333 end = blockLimit;
1335 if (end > partEnd) {
1336 end = partEnd;
1338 // If this gap has not already been requested, we have found a valid entry
1339 if (!IsAlreadyRequested(start, end)) {
1340 // Was this block to be returned
1341 if (result != NULL) {
1342 result->StartOffset = start;
1343 result->EndOffset = end;
1344 md4cpy(result->FileID, GetFileHash().GetHash());
1345 result->transferred = 0;
1347 return true;
1348 } else {
1349 // Reposition to end of that gap
1350 start = end + 1;
1352 // If tried all gaps then break out of the loop
1353 if (end == partEnd) {
1354 break;
1357 // No suitable gap found
1358 return false;
1362 void CPartFile::FillGap(uint64 start, uint64 end)
1364 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
1365 while (it != m_gaplist.end()) {
1366 std::list<Gap_Struct*>::iterator it2 = it++;
1367 Gap_Struct* cur_gap = *it2;
1369 if (cur_gap->start >= start && cur_gap->end <= end) {
1370 // our part fills this gap completly
1371 m_gaplist.erase(it2);
1372 delete cur_gap;
1373 continue;
1374 } else if (cur_gap->start >= start && cur_gap->start <= end) {
1375 // a part of this gap is in the part - set limit
1376 cur_gap->start = end+1;
1377 } else if (cur_gap->end <= end && cur_gap->end >= start) {
1378 // a part of this gap is in the part - set limit
1379 cur_gap->end = start-1;
1380 } else if (start >= cur_gap->start && end <= cur_gap->end) {
1381 uint64 buffer = cur_gap->end;
1382 cur_gap->end = start-1;
1383 cur_gap = new Gap_Struct;
1384 cur_gap->start = end+1;
1385 cur_gap->end = buffer;
1386 m_gaplist.insert(++it2, cur_gap);
1387 break;
1390 UpdateCompletedInfos();
1391 UpdateDisplayedInfo();
1395 void CPartFile::UpdateCompletedInfos()
1397 uint64 allgaps = 0;
1399 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
1400 for (; it != m_gaplist.end(); ) {
1401 std::list<Gap_Struct*>::iterator it2 = it++;
1402 Gap_Struct* cur_gap = *it2;
1404 if ((cur_gap->end > GetFileSize()) || (cur_gap->start >= GetFileSize())) {
1405 m_gaplist.erase(it2);
1406 } else {
1407 allgaps += cur_gap->end - cur_gap->start + 1;
1411 if ((!m_gaplist.empty()) || (!m_requestedblocks_list.empty())) {
1412 percentcompleted = (1.0f-(double)allgaps/GetFileSize()) * 100;
1413 completedsize = GetFileSize() - allgaps;
1414 } else {
1415 percentcompleted = 100;
1416 completedsize = GetFileSize();
1421 void CPartFile::WritePartStatus(CMemFile* file)
1423 uint16 parts = GetED2KPartCount();
1424 file->WriteUInt16(parts);
1425 uint16 done = 0;
1426 while (done != parts){
1427 uint8 towrite = 0;
1428 for (uint32 i = 0;i != 8;++i) {
1429 if (IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1)) {
1430 towrite |= (1<<i);
1432 ++done;
1433 if (done == parts) {
1434 break;
1437 file->WriteUInt8(towrite);
1441 void CPartFile::WriteCompleteSourcesCount(CMemFile* file)
1443 file->WriteUInt16(m_nCompleteSourcesCount);
1446 uint32 CPartFile::Process(uint32 reducedownload/*in percent*/,uint8 m_icounter)
1448 uint16 old_trans;
1449 uint32 dwCurTick = ::GetTickCount();
1451 // If buffer size exceeds limit, or if not written within time limit, flush data
1452 if ( (m_nTotalBufferData > thePrefs::GetFileBufferSize()) ||
1453 (dwCurTick > (m_nLastBufferFlushTime + BUFFER_TIME_LIMIT))) {
1454 // Avoid flushing while copying preview file
1455 if (!m_bPreviewing) {
1456 FlushBuffer();
1461 // check if we want new sources from server --> MOVED for 16.40 version
1462 old_trans=transferingsrc;
1463 transferingsrc = 0;
1464 kBpsDown = 0.0;
1466 if (m_icounter < 10) {
1467 // Update only downloading sources.
1468 CClientPtrList::iterator it = m_downloadingSourcesList.begin();
1469 for( ; it != m_downloadingSourcesList.end(); ) {
1470 CUpDownClient *cur_src = *it++;
1471 if(cur_src->GetDownloadState() == DS_DOWNLOADING) {
1472 ++transferingsrc;
1473 kBpsDown += cur_src->SetDownloadLimit(reducedownload);
1476 } else {
1477 // Update all sources (including downloading sources)
1478 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ) {
1479 CUpDownClient* cur_src = *it++;
1480 switch (cur_src->GetDownloadState()) {
1481 case DS_DOWNLOADING: {
1482 ++transferingsrc;
1483 kBpsDown += cur_src->SetDownloadLimit(reducedownload);
1484 break;
1486 case DS_BANNED: {
1487 break;
1489 case DS_ERROR: {
1490 break;
1492 case DS_LOWTOLOWIP: {
1493 if ( cur_src->HasLowID() && !theApp->DoCallback( cur_src ) ) {
1494 // If we are almost maxed on sources,
1495 // slowly remove these client to see
1496 // if we can find a better source.
1497 if( ((dwCurTick - lastpurgetime) > 30000) &&
1498 (GetSourceCount() >= (thePrefs::GetMaxSourcePerFile()*.8))) {
1499 RemoveSource( cur_src );
1500 lastpurgetime = dwCurTick;
1501 break;
1503 } else {
1504 cur_src->SetDownloadState(DS_ONQUEUE);
1507 break;
1509 case DS_NONEEDEDPARTS: {
1510 // we try to purge noneeded source, even without reaching the limit
1511 if((dwCurTick - lastpurgetime) > 40000) {
1512 if(!cur_src->SwapToAnotherFile(false , false, false , NULL)) {
1513 //however we only delete them if reaching the limit
1514 if (GetSourceCount() >= (thePrefs::GetMaxSourcePerFile()*.8 )) {
1515 RemoveSource(cur_src);
1516 lastpurgetime = dwCurTick;
1517 break; //Johnny-B - nothing more to do here (good eye!)
1519 } else {
1520 lastpurgetime = dwCurTick;
1521 break;
1524 // doubled reasktime for no needed parts - save connections and traffic
1525 if ( !((!cur_src->GetLastAskedTime()) ||
1526 (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME*2)) {
1527 break;
1529 // Recheck this client to see if still NNP..
1530 // Set to DS_NONE so that we force a TCP reask next time..
1531 cur_src->SetDownloadState(DS_NONE);
1533 break;
1535 case DS_ONQUEUE: {
1536 if( cur_src->IsRemoteQueueFull()) {
1537 if( ((dwCurTick - lastpurgetime) > 60000) &&
1538 (GetSourceCount() >= (thePrefs::GetMaxSourcePerFile()*.8 )) ) {
1539 RemoveSource( cur_src );
1540 lastpurgetime = dwCurTick;
1541 break; //Johnny-B - nothing more to do here (good eye!)
1545 // Give up to 1 min for UDP to respond..
1546 // If we are within on min on TCP, do not try..
1547 if ( theApp->IsConnected() &&
1548 ( (!cur_src->GetLastAskedTime()) ||
1549 (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME-20000)) {
1550 cur_src->UDPReaskForDownload();
1553 // No break here, since the next case takes care of asking for downloads.
1555 case DS_CONNECTING:
1556 case DS_TOOMANYCONNS:
1557 case DS_NONE:
1558 case DS_WAITCALLBACK:
1559 case DS_WAITCALLBACKKAD: {
1560 if ( theApp->IsConnected() &&
1561 ( (!cur_src->GetLastAskedTime()) ||
1562 (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME)) {
1563 if (!cur_src->AskForDownload()) {
1564 // I left this break here just as a reminder
1565 // just in case re rearange things..
1566 break;
1569 break;
1574 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
1575 if (IsA4AFAuto() && ((!m_LastNoNeededCheck) || (dwCurTick - m_LastNoNeededCheck > 900000))) {
1576 m_LastNoNeededCheck = dwCurTick;
1577 for ( SourceSet::iterator it = m_A4AFsrclist.begin(); it != m_A4AFsrclist.end(); ) {
1578 CUpDownClient *cur_source = *it++;
1579 uint8 download_state=cur_source->GetDownloadState();
1580 if( download_state != DS_DOWNLOADING
1581 && cur_source->GetRequestFile()
1582 && ((!cur_source->GetRequestFile()->IsA4AFAuto()) || download_state == DS_NONEEDEDPARTS))
1584 cur_source->SwapToAnotherFile(false, false, false, this);
1588 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
1590 // swap No needed partfiles if possible
1592 if (((old_trans==0) && (transferingsrc>0)) || ((old_trans>0) && (transferingsrc==0))) {
1593 SetPartFileStatus(status);
1596 // Kad source search
1597 if( GetMaxSourcePerFileUDP() > GetSourceCount()){
1598 //Once we can handle lowID users in Kad, we remove the second IsConnected
1599 if (theApp->downloadqueue->DoKademliaFileRequest() && (Kademlia::CKademlia::GetTotalFile() < KADEMLIATOTALFILE) && (dwCurTick > m_LastSearchTimeKad) && Kademlia::CKademlia::IsConnected() && theApp->IsConnected() && !IsStopped()){
1600 //Kademlia
1601 theApp->downloadqueue->SetLastKademliaFileRequest();
1603 if (GetKadFileSearchID()) {
1604 /* This will never happen anyway. We're talking a
1605 1h timespan and searches are at max 45secs */
1606 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), false);
1609 Kademlia::CUInt128 kadFileID(GetFileHash().GetHash());
1610 Kademlia::CSearch* pSearch = Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::FILE, true, kadFileID);
1611 AddDebugLogLineM(false, logKadSearch, CFormat(wxT("Preparing a Kad Search for '%s'")) % GetFileName());
1612 if (pSearch) {
1613 AddDebugLogLineM(false, logKadSearch, CFormat(wxT("Kad lookup started for '%s'")) % GetFileName());
1614 if(m_TotalSearchesKad < 7) {
1615 m_TotalSearchesKad++;
1617 m_LastSearchTimeKad = dwCurTick + (KADEMLIAREASKTIME*m_TotalSearchesKad);
1618 SetKadFileSearchID(pSearch->GetSearchID());
1621 } else {
1622 if(GetKadFileSearchID()) {
1623 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), true);
1627 // check if we want new sources from server
1628 if ( !m_localSrcReqQueued &&
1629 ( (!m_lastsearchtime) ||
1630 (dwCurTick - m_lastsearchtime) > SERVERREASKTIME) &&
1631 theApp->IsConnectedED2K() &&
1632 thePrefs::GetMaxSourcePerFileSoft() > GetSourceCount() &&
1633 !m_stopped ) {
1634 m_localSrcReqQueued = true;
1635 theApp->downloadqueue->SendLocalSrcRequest(this);
1638 // calculate datarate, set limit etc.
1641 ++m_count;
1643 // Kry - does the 3 / 30 difference produce too much flickering or CPU?
1644 if (m_count >= 30) {
1645 m_count = 0;
1646 UpdateAutoDownPriority();
1647 UpdateDisplayedInfo();
1648 if(m_bPercentUpdated == false) {
1649 UpdateCompletedInfos();
1651 m_bPercentUpdated = false;
1652 if (thePrefs::ShowCatTabInfos()) {
1653 Notify_ShowUpdateCatTabTitles();
1657 return (uint32)(kBpsDown*1024.0);
1660 bool CPartFile::CanAddSource(uint32 userid, uint16 port, uint32 serverip, uint16 serverport, uint8* pdebug_lowiddropped, bool ed2kID)
1663 //The incoming ID could have the userid in the Hybrid format..
1664 uint32 hybridID = 0;
1665 if( ed2kID ) {
1666 if (IsLowID(userid)) {
1667 hybridID = userid;
1668 } else {
1669 hybridID = wxUINT32_SWAP_ALWAYS(userid);
1671 } else {
1672 hybridID = userid;
1673 if (!IsLowID(userid)) {
1674 userid = wxUINT32_SWAP_ALWAYS(userid);
1678 // MOD Note: Do not change this part - Merkur
1679 if (theApp->IsConnectedED2K()) {
1680 if(::IsLowID(theApp->GetED2KID())) {
1681 if(theApp->GetED2KID() == userid && theApp->serverconnect->GetCurrentServer()->GetIP() == serverip && theApp->serverconnect->GetCurrentServer()->GetPort() == serverport ) {
1682 return false;
1684 if(theApp->GetPublicIP() == userid) {
1685 return false;
1687 } else {
1688 if(theApp->GetED2KID() == userid && thePrefs::GetPort() == port) {
1689 return false;
1694 if (Kademlia::CKademlia::IsConnected()) {
1695 if(!Kademlia::CKademlia::IsFirewalled()) {
1696 if(Kademlia::CKademlia::GetIPAddress() == hybridID && thePrefs::GetPort() == port) {
1697 return false;
1702 //This allows *.*.*.0 clients to not be removed if Ed2kID == false
1703 if ( IsLowID(hybridID) && theApp->IsFirewalled()) {
1704 if (pdebug_lowiddropped) {
1705 (*pdebug_lowiddropped)++;
1707 return false;
1709 // MOD Note - end
1710 return true;
1713 void CPartFile::AddSources(CMemFile& sources,uint32 serverip, uint16 serverport, unsigned origin, bool bWithObfuscationAndHash)
1715 uint8 count = sources.ReadUInt8();
1716 uint8 debug_lowiddropped = 0;
1717 uint8 debug_possiblesources = 0;
1718 CMD4Hash achUserHash;
1720 if (m_stopped) {
1721 // since we may received multiple search source UDP results we have to "consume" all data of that packet
1722 AddDebugLogLineM(false, logPartFile, wxT("Trying to add sources for a stopped file"));
1723 sources.Seek(count*(4+2), wxFromCurrent);
1724 return;
1727 for (int i = 0;i != count;++i) {
1728 uint32 userid = sources.ReadUInt32();
1729 uint16 port = sources.ReadUInt16();
1731 uint8 byCryptOptions = 0;
1732 if (bWithObfuscationAndHash){
1733 byCryptOptions = sources.ReadUInt8();
1734 if ((byCryptOptions & 0x80) > 0) {
1735 achUserHash = sources.ReadHash();
1738 if ((thePrefs::IsClientCryptLayerRequested() && (byCryptOptions & 0x01/*supported*/) > 0 && (byCryptOptions & 0x80) == 0)
1739 || (thePrefs::IsClientCryptLayerSupported() && (byCryptOptions & 0x02/*requested*/) > 0 && (byCryptOptions & 0x80) == 0)) {
1740 AddDebugLogLineM(false, logPartFile, wxString::Format(wxT("Server didn't provide UserHash for source %u, even if it was expected to (or local obfuscationsettings changed during serverconnect"), userid));
1741 } else if (!thePrefs::IsClientCryptLayerRequested() && (byCryptOptions & 0x02/*requested*/) == 0 && (byCryptOptions & 0x80) != 0) {
1742 AddDebugLogLineM(false, logPartFile, wxString::Format(wxT("Server provided UserHash for source %u, even if it wasn't expected to (or local obfuscationsettings changed during serverconnect"), userid));
1747 // "Filter LAN IPs" and "IPfilter" the received sources IP addresses
1748 if (!IsLowID(userid)) {
1749 // check for 0-IP, localhost and optionally for LAN addresses
1750 if ( !IsGoodIP(userid, thePrefs::FilterLanIPs()) ) {
1751 continue;
1753 if (theApp->ipfilter->IsFiltered(userid)) {
1754 continue;
1758 if (!CanAddSource(userid, port, serverip, serverport, &debug_lowiddropped)) {
1759 continue;
1762 if(thePrefs::GetMaxSourcePerFile() > GetSourceCount()) {
1763 ++debug_possiblesources;
1764 CUpDownClient* newsource = new CUpDownClient(port,userid,serverip,serverport,this, true, true);
1766 newsource->SetSourceFrom((ESourceFrom)origin);
1767 newsource->SetCryptLayerSupport((byCryptOptions & 0x01) != 0);
1768 newsource->SetCryptLayerRequest((byCryptOptions & 0x02) != 0);
1769 newsource->SetCryptLayerRequires((byCryptOptions & 0x04) != 0);
1770 if ((byCryptOptions & 0x80) != 0) {
1771 newsource->SetUserHash(achUserHash);
1774 theApp->downloadqueue->CheckAndAddSource(this,newsource);
1775 } else {
1776 AddDebugLogLineM(false, logPartFile, wxT("Consuming a packet because of max sources reached"));
1777 // Since we may receive multiple search source UDP results we have to "consume" all data of that packet
1778 // This '+1' is added because 'i' counts from 0.
1779 sources.Seek((count-(i+1))*(4+2), wxFromCurrent);
1780 if (GetKadFileSearchID()) {
1781 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), false);
1783 break;
1788 void CPartFile::UpdatePartsInfo()
1790 if( !IsPartFile() ) {
1791 CKnownFile::UpdatePartsInfo();
1792 return;
1795 // Cache part count
1796 uint16 partcount = GetPartCount();
1797 bool flag = (time(NULL) - m_nCompleteSourcesTime > 0);
1799 // Ensure the frequency-list is ready
1800 if ( m_SrcpartFrequency.size() != GetPartCount() ) {
1801 m_SrcpartFrequency.clear();
1802 m_SrcpartFrequency.insert(m_SrcpartFrequency.begin(), GetPartCount(), 0);
1805 // Find number of available parts
1806 uint16 availablecounter = 0;
1807 for ( uint16 i = 0; i < partcount; ++i ) {
1808 if ( m_SrcpartFrequency[i] )
1809 ++availablecounter;
1812 if ( ( availablecounter == partcount ) && ( m_availablePartsCount < partcount ) ) {
1813 lastseencomplete = time(NULL);
1816 m_availablePartsCount = availablecounter;
1818 if ( flag ) {
1819 ArrayOfUInts16 count;
1821 count.reserve(GetSourceCount());
1823 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it ) {
1824 if ( !(*it)->GetUpPartStatus().empty() && (*it)->GetUpPartCount() == partcount ) {
1825 count.push_back((*it)->GetUpCompleteSourcesCount());
1829 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo = m_nCompleteSourcesCountHi = 0;
1831 for (uint16 i = 0; i < partcount; ++i) {
1832 if( !i ) {
1833 m_nCompleteSourcesCount = m_SrcpartFrequency[i];
1835 else if( m_nCompleteSourcesCount > m_SrcpartFrequency[i]) {
1836 m_nCompleteSourcesCount = m_SrcpartFrequency[i];
1839 count.push_back(m_nCompleteSourcesCount);
1841 int32 n = count.size();
1842 if (n > 0) {
1843 std::sort(count.begin(), count.end(), std::less<uint16>());
1845 // calculate range
1846 int32 i= n >> 1; // (n / 2)
1847 int32 j= (n * 3) >> 2; // (n * 3) / 4
1848 int32 k= (n * 7) >> 3; // (n * 7) / 8
1850 //When still a part file, adjust your guesses by 20% to what you see..
1853 if (n < 5) {
1854 //Not many sources, so just use what you see..
1855 // welcome to 'plain stupid code'
1856 // m_nCompleteSourcesCount;
1857 m_nCompleteSourcesCountLo= m_nCompleteSourcesCount;
1858 m_nCompleteSourcesCountHi= m_nCompleteSourcesCount;
1859 } else if (n < 20) {
1860 // For low guess and normal guess count
1861 // If we see more sources then the guessed low and normal, use what we see.
1862 // If we see less sources then the guessed low, adjust network accounts for 80%,
1863 // we account for 20% with what we see and make sure we are still above the normal.
1864 // For high guess
1865 // Adjust 80% network and 20% what we see.
1866 if ( count[i] < m_nCompleteSourcesCount ) {
1867 m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1868 } else {
1869 m_nCompleteSourcesCountLo =
1870 (uint16)((float)(count[i]*.8) +
1871 (float)(m_nCompleteSourcesCount*.2));
1873 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo;
1874 m_nCompleteSourcesCountHi =
1875 (uint16)((float)(count[j]*.8) +
1876 (float)(m_nCompleteSourcesCount*.2));
1877 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1878 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1880 } else {
1881 // Many sources
1882 // ------------
1883 // For low guess
1884 // Use what we see.
1885 // For normal guess
1886 // Adjust network accounts for 80%, we account for 20% with what
1887 // we see and make sure we are still above the low.
1888 // For high guess
1889 // Adjust network accounts for 80%, we account for 20% with what
1890 // we see and make sure we are still above the normal.
1892 m_nCompleteSourcesCountLo= m_nCompleteSourcesCount;
1893 m_nCompleteSourcesCount= (uint16)((float)(count[j]*.8)+(float)(m_nCompleteSourcesCount*.2));
1894 if( m_nCompleteSourcesCount < m_nCompleteSourcesCountLo ) {
1895 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo;
1897 m_nCompleteSourcesCountHi= (uint16)((float)(count[k]*.8)+(float)(m_nCompleteSourcesCount*.2));
1898 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1899 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1903 m_nCompleteSourcesTime = time(NULL) + (60);
1905 UpdateDisplayedInfo();
1908 // Kry - Updated to 0.42e + bugfix
1909 // [Maella -Enhanced Chunk Selection- (based on jicxicmic)]
1910 bool CPartFile::GetNextRequestedBlock(CUpDownClient* sender, Requested_Block_Struct** newblocks, uint16* count)
1913 // The purpose of this function is to return a list of blocks (~180KB) to
1914 // download. To avoid a prematurely stop of the downloading, all blocks that
1915 // are requested from the same source must be located within the same
1916 // chunk (=> part ~9MB).
1918 // The selection of the chunk to download is one of the CRITICAL parts of the
1919 // edonkey network. The selection algorithm must insure the best spreading
1920 // of files.
1922 // The selection is based on 4 criteria:
1923 // 1. Frequency of the chunk (availability), very rare chunks must be downloaded
1924 // as quickly as possible to become a new available source.
1925 // 2. Parts used for preview (first + last chunk), preview or check a
1926 // file (e.g. movie, mp3)
1927 // 3. Request state (downloading in process), try to ask each source for another
1928 // chunk. Spread the requests between all sources.
1929 // 4. Completion (shortest-to-complete), partially retrieved chunks should be
1930 // completed before starting to download other one.
1932 // The frequency criterion defines three zones: very rare (<10%), rare (<50%)
1933 // and common (>30%). Inside each zone, the criteria have a specific weight, used
1934 // to calculate the priority of chunks. The chunk(s) with the highest
1935 // priority (highest=0, lowest=0xffff) is/are selected first.
1937 // very rare (preview) rare common
1938 // 0% <---- +0 pt ----> 10% <----- +10000 pt -----> 50% <---- +20000 pt ----> 100%
1939 // 1. <------- frequency: +25*frequency pt ----------->
1940 // 2. <- preview: +1 pt --><-------------- preview: set to 10000 pt ------------->
1941 // 3. <------ request: download in progress +20000 pt ------>
1942 // 4a. <- completion: 0% +100, 25% +75 .. 100% +0 pt --><-- !req => completion --->
1943 // 4b. <--- req => !completion -->
1945 // Unrolled, the priority scale is:
1947 // 0..xxxx unrequested and requested very rare chunks
1948 // 10000..1xxxx unrequested rare chunks + unrequested preview chunks
1949 // 20000..2xxxx unrequested common chunks (priority to the most complete)
1950 // 30000..3xxxx requested rare chunks + requested preview chunks
1951 // 40000..4xxxx requested common chunks (priority to the least complete)
1953 // This algorithm usually selects first the rarest chunk(s). However, partially
1954 // complete chunk(s) that is/are close to completion may overtake the priority
1955 // (priority inversion).
1956 // For the common chuncks, the algorithm tries to spread the dowload between
1957 // the sources
1960 // Check input parameters
1961 if(count == NULL) {
1962 return false;
1964 if ( sender->GetPartStatus().empty() ) {
1965 return false;
1967 // Define and create the list of the chunks to download
1968 const uint16 partCount = GetPartCount();
1969 ChunkList chunksList;
1971 // Main loop
1972 uint16 newBlockCount = 0;
1973 while(newBlockCount != *count) {
1974 // Create a request block stucture if a chunk has been previously selected
1975 if(sender->GetLastPartAsked() != 0xffff) {
1976 Requested_Block_Struct* pBlock = new Requested_Block_Struct;
1977 if(GetNextEmptyBlockInPart(sender->GetLastPartAsked(), pBlock) == true) {
1978 // Keep a track of all pending requested blocks
1979 m_requestedblocks_list.push_back(pBlock);
1980 // Update list of blocks to return
1981 newblocks[newBlockCount++] = pBlock;
1982 // Skip end of loop (=> CPU load)
1983 continue;
1984 } else {
1985 // All blocks for this chunk have been already requested
1986 delete pBlock;
1987 // => Try to select another chunk
1988 sender->SetLastPartAsked(0xffff);
1992 // Check if a new chunk must be selected (e.g. download starting, previous chunk complete)
1993 if(sender->GetLastPartAsked() == 0xffff) {
1994 // Quantify all chunks (create list of chunks to download)
1995 // This is done only one time and only if it is necessary (=> CPU load)
1996 if(chunksList.empty()) {
1997 // Indentify the locally missing part(s) that this source has
1998 for(uint16 i=0; i < partCount; ++i) {
1999 if(sender->IsPartAvailable(i) == true && GetNextEmptyBlockInPart(i, NULL) == true) {
2000 // Create a new entry for this chunk and add it to the list
2001 Chunk newEntry;
2002 newEntry.part = i;
2003 newEntry.frequency = m_SrcpartFrequency[i];
2004 chunksList.push_back(newEntry);
2008 // Check if any bloks(s) could be downloaded
2009 if(chunksList.empty()) {
2010 break; // Exit main loop while()
2013 // Define the bounds of the three zones (very rare, rare)
2014 // more depending on available sources
2015 uint8 modif=10;
2016 if (GetSourceCount()>800) {
2017 modif=2;
2018 } else if (GetSourceCount()>200) {
2019 modif=5;
2021 uint16 limit= modif*GetSourceCount()/ 100;
2022 if (limit==0) {
2023 limit=1;
2025 const uint16 veryRareBound = limit;
2026 const uint16 rareBound = 2*limit;
2028 // Cache Preview state (Criterion 2)
2029 FileType type = GetFiletype(GetFileName());
2030 const bool isPreviewEnable =
2031 thePrefs::GetPreviewPrio() &&
2032 (type == ftArchive || type == ftVideo);
2034 // Collect and calculate criteria for all chunks
2035 for (ChunkList::iterator it = chunksList.begin(); it != chunksList.end(); ++it) {
2036 Chunk& cur_chunk = *it;
2038 // Offsets of chunk
2039 const uint64 uStart = cur_chunk.part * PARTSIZE;
2040 const uint64 uEnd =
2041 ((GetFileSize() - 1) < (uStart + PARTSIZE - 1)) ?
2042 (GetFileSize() - 1) : (uStart + PARTSIZE - 1);
2043 // Criterion 2. Parts used for preview
2044 // Remark: - We need to download the first part and the last part(s).
2045 // - When the last part is very small, it's necessary to
2046 // download the two last parts.
2047 bool critPreview = false;
2048 if(isPreviewEnable == true) {
2049 if(cur_chunk.part == 0) {
2050 critPreview = true; // First chunk
2051 } else if(cur_chunk.part == partCount-1) {
2052 critPreview = true; // Last chunk
2053 } else if(cur_chunk.part == partCount-2) {
2054 // Last chunk - 1 (only if last chunk is too small)
2055 const uint32 sizeOfLastChunk = GetFileSize() - uEnd;
2056 if(sizeOfLastChunk < PARTSIZE/3) {
2057 critPreview = true; // Last chunk - 1
2062 // Criterion 3. Request state (downloading in process from other source(s))
2063 // => CPU load
2064 const bool critRequested =
2065 cur_chunk.frequency > veryRareBound &&
2066 IsAlreadyRequested(uStart, uEnd);
2068 // Criterion 4. Completion
2069 uint64 partSize = PARTSIZE;
2071 std::list<Gap_Struct*>::iterator it2 = m_gaplist.begin();
2072 for (; it2 != m_gaplist.end(); ++it2) {
2073 const Gap_Struct* cur_gap = *it2;
2074 // Check if Gap is into the limit
2075 if(cur_gap->start < uStart) {
2076 if(cur_gap->end > uStart && cur_gap->end < uEnd) {
2077 partSize -= cur_gap->end - uStart + 1;
2078 } else if(cur_gap->end >= uEnd) {
2079 partSize = 0;
2080 break; // exit loop for()
2082 } else if(cur_gap->start <= uEnd) {
2083 if(cur_gap->end < uEnd) {
2084 partSize -= cur_gap->end - cur_gap->start + 1;
2085 } else {
2086 partSize -= uEnd - cur_gap->start + 1;
2090 const uint16 critCompletion = (uint16)(partSize/(PARTSIZE/100)); // in [%]
2092 // Calculate priority with all criteria
2093 if(cur_chunk.frequency <= veryRareBound) {
2094 // 0..xxxx unrequested + requested very rare chunks
2095 cur_chunk.rank = (25 * cur_chunk.frequency) + // Criterion 1
2096 ((critPreview == true) ? 0 : 1) + // Criterion 2
2097 (100 - critCompletion); // Criterion 4
2098 } else if(critPreview == true) {
2099 // 10000..10100 unrequested preview chunks
2100 // 30000..30100 requested preview chunks
2101 cur_chunk.rank = ((critRequested == false) ? 10000 : 30000) + // Criterion 3
2102 (100 - critCompletion); // Criterion 4
2103 } else if(cur_chunk.frequency <= rareBound) {
2104 // 10101..1xxxx unrequested rare chunks
2105 // 30101..3xxxx requested rare chunks
2106 cur_chunk.rank = (25 * cur_chunk.frequency) + // Criterion 1
2107 ((critRequested == false) ? 10101 : 30101) + // Criterion 3
2108 (100 - critCompletion); // Criterion 4
2109 } else {
2110 // common chunk
2111 if(critRequested == false) { // Criterion 3
2112 // 20000..2xxxx unrequested common chunks
2113 cur_chunk.rank = 20000 + // Criterion 3
2114 (100 - critCompletion); // Criterion 4
2115 } else {
2116 // 40000..4xxxx requested common chunks
2117 // Remark: The weight of the completion criterion is inversed
2118 // to spead the requests over the completing chunks.
2119 // Without this, the chunk closest to completion will
2120 // received every new sources.
2121 cur_chunk.rank = 40000 + // Criterion 3
2122 (critCompletion); // Criterion 4
2128 // Select the next chunk to download
2129 if(!chunksList.empty()) {
2130 // Find and count the chunck(s) with the highest priority
2131 uint16 chunkCount = 0; // Number of found chunks with same priority
2132 uint16 rank = 0xffff; // Highest priority found
2134 // Collect and calculate criteria for all chunks
2135 for (ChunkList::iterator it = chunksList.begin(); it != chunksList.end(); ++it) {
2136 const Chunk& cur_chunk = *it;
2137 if(cur_chunk.rank < rank) {
2138 chunkCount = 1;
2139 rank = cur_chunk.rank;
2140 } else if(cur_chunk.rank == rank) {
2141 ++chunkCount;
2145 // Use a random access to avoid that everybody tries to download the
2146 // same chunks at the same time (=> spread the selected chunk among clients)
2147 uint16 randomness = 1 + (int) (((float)(chunkCount-1))*rand()/(RAND_MAX+1.0));
2149 for (ChunkList::iterator it = chunksList.begin(); it != chunksList.end(); ++it) {
2150 const Chunk& cur_chunk = *it;
2151 if(cur_chunk.rank == rank) {
2152 randomness--;
2153 if(randomness == 0) {
2154 // Selection process is over
2155 sender->SetLastPartAsked(cur_chunk.part);
2156 // Remark: this list might be reused up to *count times
2157 chunksList.erase(it);
2158 break; // exit loop for()
2162 } else {
2163 // There is no remaining chunk to download
2164 break; // Exit main loop while()
2168 // Return the number of the blocks
2169 *count = newBlockCount;
2170 // Return
2171 return (newBlockCount > 0);
2173 // Maella end
2174 // Kry EOI
2177 void CPartFile::RemoveBlockFromList(uint64 start,uint64 end)
2179 std::list<Requested_Block_Struct*>::iterator it = m_requestedblocks_list.begin();
2180 while (it != m_requestedblocks_list.end()) {
2181 std::list<Requested_Block_Struct*>::iterator it2 = it++;
2183 if ((*it2)->StartOffset <= start && (*it2)->EndOffset >= end) {
2184 m_requestedblocks_list.erase(it2);
2190 void CPartFile::RemoveAllRequestedBlocks(void)
2192 m_requestedblocks_list.clear();
2196 void CPartFile::CompleteFile(bool bIsHashingDone)
2198 if (GetKadFileSearchID()) {
2199 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), false);
2202 theApp->downloadqueue->RemoveLocalServerRequest(this);
2204 AddDebugLogLineM( false, logPartFile, wxString( wxT("CPartFile::CompleteFile: Hash ") ) + ( bIsHashingDone ? wxT("done") : wxT("not done") ) );
2206 if (!bIsHashingDone) {
2207 SetPartFileStatus(PS_COMPLETING);
2208 kBpsDown = 0.0;
2210 CPath partFile = m_partmetfilename.RemoveExt();
2211 CThreadScheduler::AddTask(new CHashingTask(GetFilePath(), partFile, this));
2212 return;
2213 } else {
2214 StopFile();
2215 m_is_A4AF_auto=false;
2216 SetPartFileStatus(PS_COMPLETING);
2217 // guess I was wrong about not need to spaw a thread ...
2218 // It is if the temp and incoming dirs are on different
2219 // partitions/drives and the file is large...[oz]
2222 PerformFileComplete();
2226 if (thePrefs::ShowCatTabInfos()) {
2227 Notify_ShowUpdateCatTabTitles();
2229 UpdateDisplayedInfo(true);
2233 void CPartFile::CompleteFileEnded(bool errorOccured, const CPath& newname)
2235 if (errorOccured) {
2236 m_paused = true;
2237 SetPartFileStatus(PS_ERROR);
2238 AddLogLineM(true, CFormat( _("Unexpected file error while completing %s. File paused") )% GetFileName() );
2239 } else {
2240 m_fullname = newname;
2242 SetFilePath(m_fullname.GetPath());
2243 SetFileName(m_fullname.GetFullName());
2245 SetPartFileStatus(PS_COMPLETE);
2246 m_paused = false;
2247 ClearPriority();
2249 // TODO: What the f*** if it is already known?
2250 theApp->knownfiles->SafeAddKFile(this);
2252 // remove the file from the suspended uploads list
2253 theApp->uploadqueue->ResumeUpload(GetFileHash());
2254 theApp->downloadqueue->RemoveFile(this);
2255 theApp->sharedfiles->SafeAddKFile(this);
2256 UpdateDisplayedInfo(true);
2258 // republish that file to the ed2k-server to update the 'FT_COMPLETE_SOURCES' counter on the server.
2259 theApp->sharedfiles->RepublishFile(this);
2261 // Ensure that completed shows the correct value
2262 completedsize = GetFileSize();
2264 AddLogLineM(true, CFormat( _("Finished downloading: %s") ) % GetFileName() );
2267 theApp->downloadqueue->StartNextFile(this);
2271 void CPartFile::PerformFileComplete()
2273 // add this file to the suspended uploads list
2274 theApp->uploadqueue->SuspendUpload(GetFileHash());
2275 FlushBuffer();
2277 // close permanent handle
2278 if (m_hpartfile.IsOpened()) {
2279 m_hpartfile.Close();
2282 // Schedule task for completion of the file
2283 CThreadScheduler::AddTask(new CCompletionTask(this));
2287 void CPartFile::RemoveAllSources(bool bTryToSwap)
2289 for( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end();) {
2290 CUpDownClient* cur_src = *it++;
2291 if (bTryToSwap) {
2292 if (!cur_src->SwapToAnotherFile(true, true, true, NULL)) {
2293 RemoveSource(cur_src,true,false);
2294 // If it was not swapped, it's not on any file anymore, and should die
2296 } else {
2297 RemoveSource(cur_src,true,false);
2301 UpdatePartsInfo();
2303 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
2304 // remove all links A4AF in sources to this file
2305 if(!m_A4AFsrclist.empty()) {
2306 for( SourceSet::iterator it = m_A4AFsrclist.begin(); it != m_A4AFsrclist.end(); ) {
2307 CUpDownClient* cur_src = *it++;
2308 if ( cur_src->DeleteFileRequest( this ) ) {
2309 Notify_DownloadCtrlRemoveSource(cur_src, this);
2312 m_A4AFsrclist.clear();
2314 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
2315 UpdateFileRatingCommentAvail();
2319 void CPartFile::Delete()
2321 AddLogLineM(false, CFormat(_("Deleting file: %s")) % GetFileName());
2322 // Barry - Need to tell any connected clients to stop sending the file
2323 StopFile(true);
2324 AddDebugLogLineM(false, logPartFile, wxT("\tStopped"));
2326 theApp->sharedfiles->RemoveFile(this);
2327 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved from shared"));
2328 theApp->downloadqueue->RemoveFile(this);
2329 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved from download queue"));
2330 Notify_DownloadCtrlRemoveFile(this);
2331 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved transferwnd"));
2333 // Kry - WTF?
2334 // eMule had same problem with lseek error ... and override with a simple
2335 // check for INVALID_HANDLE_VALUE (that, btw, does not exist on linux)
2336 // So we just guess is < 0 on error and > 2 if ok (0 stdin, 1 stdout, 2 stderr)
2337 if (m_hpartfile.fd() > 2) { // 0 stdin, 1 stdout, 2 stderr
2338 m_hpartfile.Close();
2341 AddDebugLogLineM(false, logPartFile, wxT("\tClosed"));
2343 if (!CPath::RemoveFile(m_fullname)) {
2344 AddDebugLogLineM(true, logPartFile, CFormat(wxT("\tFailed to delete '%s'")) % m_fullname);
2345 } else {
2346 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved .part.met"));
2349 CPath partFile = m_fullname.RemoveExt();
2350 if (!CPath::RemoveFile(partFile)) {
2351 AddDebugLogLineM(true, logPartFile, CFormat(wxT("Failed to delete '%s'")) % partFile);
2352 } else {
2353 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved .part"));
2356 CPath BAKName = m_fullname.AppendExt(PARTMET_BAK_EXT);
2357 if (!CPath::RemoveFile(BAKName)) {
2358 AddDebugLogLineM(true, logPartFile, CFormat(wxT("Failed to delete '%s'")) % BAKName);
2359 } else {
2360 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved .BAK"));
2363 CPath SEEDSName = m_fullname.AppendExt(wxT(".seeds"));
2364 if (SEEDSName.FileExists()) {
2365 if (CPath::RemoveFile(SEEDSName)) {
2366 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved .seeds"));
2367 } else {
2368 AddDebugLogLineM(true, logPartFile, CFormat(wxT("Failed to delete '%s'")) % SEEDSName);
2372 AddDebugLogLineM(false, logPartFile, wxT("Done"));
2374 delete this;
2378 bool CPartFile::HashSinglePart(uint16 partnumber)
2380 if ((GetHashCount() <= partnumber) && (GetPartCount() > 1)) {
2381 AddLogLineM(true,
2382 CFormat( _("Warning: Unable to hash downloaded part - hashset incomplete for '%s'") )
2383 % GetFileName() );
2384 m_hashsetneeded = true;
2385 return true;
2386 } else if ((GetHashCount() <= partnumber) && GetPartCount() != 1) {
2387 AddLogLineM(true, CFormat( _("Error: Unable to hash downloaded part - hashset incomplete (%s). This should never happen")) % GetFileName() );
2388 m_hashsetneeded = true;
2389 return true;
2390 } else {
2391 CMD4Hash hashresult;
2392 uint64 length = PARTSIZE;
2393 const uint64 offset = length * partnumber;
2394 try {
2395 m_hpartfile.Seek(offset, wxFromStart);
2396 if (offset + PARTSIZE > m_hpartfile.GetLength()) {
2397 length = m_hpartfile.GetLength() - offset;
2398 wxASSERT( length <= PARTSIZE );
2400 CreateHashFromFile(&m_hpartfile, length, &hashresult, NULL);
2401 } catch (const CIOFailureException& e) {
2402 AddLogLineM(true, CFormat( wxT("EOF while hashing downloaded part %u with length %u (max %u) of partfile '%s' with length %u: %s"))
2403 % partnumber % length % (offset+length) % GetFileName() % GetFileSize() % e.what());
2404 SetPartFileStatus(PS_ERROR);
2405 return false;
2406 } catch (const CEOFException& e) {
2407 AddLogLineM(true, CFormat( wxT("EOF while hashing downloaded part %u with length %u (max %u) of partfile '%s' with length %u: %s"))
2408 % partnumber % length % (offset+length) % GetFileName() % GetFileSize() % e.what());
2409 return false;
2412 if (GetPartCount() > 1) {
2413 if (hashresult != GetPartHash(partnumber)) {
2414 AddDebugLogLineM(false, logPartFile, CFormat( wxT("%s: Expected part-hash: %s")) % GetFileName() % GetPartHash(partnumber).Encode() );
2415 AddDebugLogLineM(false, logPartFile, CFormat( wxT("%s: Actual part-hash: %s")) % GetFileName() % hashresult.Encode() );
2416 return false;
2417 } else {
2418 return true;
2420 } else {
2421 if (hashresult != m_abyFileHash) {
2422 return false;
2423 } else {
2424 return true;
2431 bool CPartFile::IsCorruptedPart(uint16 partnumber)
2433 return std::find(m_corrupted_list.begin(), m_corrupted_list.end(), partnumber)
2434 != m_corrupted_list.end();
2438 void CPartFile::SetDownPriority(uint8 np, bool bSave, bool bRefresh )
2440 if ( m_iDownPriority != np ) {
2441 m_iDownPriority = np;
2442 if ( bRefresh )
2443 UpdateDisplayedInfo(true);
2444 if ( bSave )
2445 SavePartFile();
2450 void CPartFile::StopFile(bool bCancel)
2452 // Kry - Need to set it here to get into SetPartFileStatus(status) correctly
2453 m_stopped = true;
2455 // Barry - Need to tell any connected clients to stop sending the file
2456 PauseFile();
2458 m_LastSearchTimeKad = 0;
2459 m_TotalSearchesKad = 0;
2461 RemoveAllSources(true);
2462 kBpsDown = 0.0;
2463 transferingsrc = 0;
2464 memset(m_anStates,0,sizeof(m_anStates));
2466 if (!bCancel) {
2467 FlushBuffer(true);
2470 UpdateDisplayedInfo(true);
2474 void CPartFile::StopPausedFile()
2476 if (!IsStopped()) {
2477 // Once an hour, remove any sources for files which are no longer active downloads
2478 switch (GetStatus()) {
2479 case PS_PAUSED:
2480 case PS_INSUFFICIENT:
2481 case PS_ERROR:
2482 if (time(NULL) - m_iLastPausePurge > (60*60)) {
2483 m_iLastPausePurge = time(NULL);
2484 StopFile();
2491 void CPartFile::PauseFile(bool bInsufficient)
2493 SetActive(false);
2495 if ( status == PS_COMPLETE || status == PS_COMPLETING ) {
2496 return;
2499 if (GetKadFileSearchID()) {
2500 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), true);
2501 // If we were in the middle of searching, reset timer so they can resume searching.
2502 m_LastSearchTimeKad = 0;
2505 m_iLastPausePurge = time(NULL);
2507 theApp->downloadqueue->RemoveLocalServerRequest(this);
2509 CPacket packet( OP_CANCELTRANSFER, 0, OP_EDONKEYPROT );
2510 for( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ) {
2511 CUpDownClient* cur_src = *it++;
2512 if (cur_src->GetDownloadState() == DS_DOWNLOADING) {
2513 if (!cur_src->GetSentCancelTransfer()) {
2514 theStats::AddUpOverheadOther( packet.GetPacketSize() );
2515 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + cur_src->GetFullIP() );
2516 cur_src->SendPacket( &packet, false, true );
2517 cur_src->SetSentCancelTransfer( true );
2519 cur_src->SetDownloadState(DS_ONQUEUE);
2524 m_insufficient = bInsufficient;
2525 m_paused = true;
2528 kBpsDown = 0.0;
2529 transferingsrc = 0;
2530 m_anStates[DS_DOWNLOADING] = 0;
2532 SetStatus(status);
2536 void CPartFile::ResumeFile()
2538 if ( status == PS_COMPLETE || status == PS_COMPLETING ) {
2539 return;
2542 if ( m_insufficient && !CheckFreeDiskSpace() ) {
2543 // Still not enough free discspace
2544 return;
2547 m_paused = false;
2548 m_stopped = false;
2549 m_insufficient = false;
2551 m_lastsearchtime = 0;
2552 SetStatus(status);
2553 SetActive(theApp->IsConnected());
2555 if (m_gaplist.empty() && (GetStatus() == PS_ERROR)) {
2556 // The file has already been hashed at this point
2557 CompleteFile(true);
2560 UpdateDisplayedInfo(true);
2564 bool CPartFile::CheckFreeDiskSpace( uint32 neededSpace )
2566 uint64 free = CPath::GetFreeSpaceAt(GetFilePath());
2567 if (free == static_cast<uint64>(wxInvalidOffset)) {
2568 // If GetFreeSpaceAt() fails, then the path probably does not exist.
2569 return false;
2572 // The very least acceptable diskspace is a single PART
2573 if ( free < PARTSIZE ) {
2574 // Always fail in this case, since we risk losing data if we try to
2575 // write on a full partition.
2576 return false;
2579 // All other checks are only made if the user has enabled them
2580 if ( thePrefs::IsCheckDiskspaceEnabled() ) {
2581 neededSpace += thePrefs::GetMinFreeDiskSpace();
2583 // Due to the the existance of sparse files, we cannot assume that
2584 // writes within the file doesn't cause new blocks to be allocated.
2585 // Therefore, we have to simply stop writing the moment the limit has
2586 // been exceeded.
2587 return free >= neededSpace;
2590 return true;
2594 void CPartFile::SetLastAnsweredTime()
2596 m_ClientSrcAnswered = ::GetTickCount();
2599 void CPartFile::SetLastAnsweredTimeTimeout()
2601 m_ClientSrcAnswered = 2 * CONNECTION_LATENCY + ::GetTickCount() - SOURCECLIENTREASKS;
2604 CPacket *CPartFile::CreateSrcInfoPacket(const CUpDownClient* forClient, uint8 byRequestedVersion, uint16 nRequestedOptions)
2607 if ( m_SrcList.empty() ) {
2608 return NULL;
2611 if(!IsPartFile()) {
2612 return CKnownFile::CreateSrcInfoPacket(forClient, byRequestedVersion, nRequestedOptions);
2615 if (((forClient->GetRequestFile() != this)
2616 && (forClient->GetUploadFile() != this)) || forClient->GetUploadFileID() != GetFileHash()) {
2617 wxString file1 = _("Unknown");
2618 if (forClient->GetRequestFile() && forClient->GetRequestFile()->GetFileName().IsOk()) {
2619 file1 = forClient->GetRequestFile()->GetFileName().GetPrintable();
2620 } else if (forClient->GetUploadFile() && forClient->GetUploadFile()->GetFileName().IsOk()) {
2621 file1 = forClient->GetUploadFile()->GetFileName().GetPrintable();
2623 wxString file2 = _("Unknown");
2624 if (GetFileName().IsOk()) {
2625 file2 = GetFileName().GetPrintable();
2627 AddDebugLogLineM(false, logPartFile, wxT("File mismatch on source packet (P) Sending: ") + file1 + wxT(" From: ") + file2);
2628 return NULL;
2631 if ( !(GetStatus() == PS_READY || GetStatus() == PS_EMPTY)) {
2632 return NULL;
2635 const BitVector& reqstatus = forClient->GetPartStatus();
2636 bool KnowNeededParts = !reqstatus.empty();
2637 //wxASSERT(rcvstatus.size() == GetPartCount()); // Obviously!
2638 if (reqstatus.size() != GetPartCount()) {
2639 // Yuck. Same file but different part count? Seriously fucked up.
2640 AddDebugLogLineM(false, logPartFile, wxString::Format(wxT("Impossible situation: different partcounts for the same part file: %i (client) and %i (file)"),reqstatus.size(),GetPartCount()));
2641 return NULL;
2644 CMemFile data(1024);
2646 uint8 byUsedVersion;
2647 bool bIsSX2Packet;
2648 if (forClient->SupportsSourceExchange2() && byRequestedVersion > 0){
2649 // the client uses SourceExchange2 and requested the highest version he knows
2650 // and we send the highest version we know, but of course not higher than his request
2651 byUsedVersion = std::min(byRequestedVersion, (uint8)SOURCEEXCHANGE2_VERSION);
2652 bIsSX2Packet = true;
2653 data.WriteUInt8(byUsedVersion);
2655 // we don't support any special SX2 options yet, reserved for later use
2656 if (nRequestedOptions != 0) {
2657 AddDebugLogLineM(false, logKnownFiles, CFormat(wxT("Client requested unknown options for SourceExchange2: %u")) % nRequestedOptions);
2659 } else {
2660 byUsedVersion = forClient->GetSourceExchange1Version();
2661 bIsSX2Packet = false;
2662 if (forClient->SupportsSourceExchange2()) {
2663 AddDebugLogLineM(false, logKnownFiles, wxT("Client which announced to support SX2 sent SX1 packet instead"));
2667 uint16 nCount = 0;
2669 data.WriteHash(m_abyFileHash);
2670 data.WriteUInt16(nCount);
2671 bool bNeeded;
2672 for (SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it ) {
2673 bNeeded = false;
2674 CUpDownClient* cur_src = *it;
2676 int state = cur_src->GetDownloadState();
2677 int valid = ( state == DS_DOWNLOADING ) || ( state == DS_ONQUEUE && !cur_src->IsRemoteQueueFull() );
2679 if ( cur_src->HasLowID() || !valid ) {
2680 continue;
2683 // only send source which have needed parts for this client if possible
2684 const BitVector& srcstatus = cur_src->GetPartStatus();
2685 if ( !srcstatus.empty() ) {
2686 //wxASSERT(srcstatus.size() == GetPartCount()); // Obviously!
2687 if (srcstatus.size() != GetPartCount()) {
2688 continue;
2690 if ( KnowNeededParts ) {
2691 // only send sources which have needed parts for this client
2692 for (int x = 0; x < GetPartCount(); ++x) {
2693 if (srcstatus[x] && !reqstatus[x]) {
2694 bNeeded = true;
2695 break;
2698 } else {
2699 // if we don't know the need parts for this client,
2700 // return any source currently a client sends it's
2701 // file status only after it has at least one complete part
2702 if (srcstatus.size() != GetPartCount()) {
2703 continue;
2705 for (int x = 0; x < GetPartCount(); ++x){
2706 if (srcstatus[x]) {
2707 bNeeded = true;
2708 break;
2713 if(bNeeded) {
2714 ++nCount;
2715 uint32 dwID;
2716 if(forClient->GetSourceExchange1Version() > 2) {
2717 dwID = cur_src->GetUserIDHybrid();
2718 } else {
2719 dwID = wxUINT32_SWAP_ALWAYS(cur_src->GetUserIDHybrid());
2721 data.WriteUInt32(dwID);
2722 data.WriteUInt16(cur_src->GetUserPort());
2723 data.WriteUInt32(cur_src->GetServerIP());
2724 data.WriteUInt16(cur_src->GetServerPort());
2726 if (byUsedVersion >= 2) {
2727 data.WriteHash(cur_src->GetUserHash());
2730 if (byUsedVersion >= 4){
2731 // CryptSettings - SourceExchange V4
2732 // 5 Reserved (!)
2733 // 1 CryptLayer Required
2734 // 1 CryptLayer Requested
2735 // 1 CryptLayer Supported
2736 const uint8 uSupportsCryptLayer = cur_src->SupportsCryptLayer() ? 1 : 0;
2737 const uint8 uRequestsCryptLayer = cur_src->RequestsCryptLayer() ? 1 : 0;
2738 const uint8 uRequiresCryptLayer = cur_src->RequiresCryptLayer() ? 1 : 0;
2739 const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0);
2740 data.WriteUInt8(byCryptOptions);
2743 if (nCount > 500) {
2744 break;
2748 if (!nCount) {
2749 return 0;
2751 data.Seek(bIsSX2Packet ? 17 : 16, wxFromStart);
2752 data.WriteUInt16(nCount);
2754 CPacket* result = new CPacket(data, OP_EMULEPROT, bIsSX2Packet ? OP_ANSWERSOURCES2 : OP_ANSWERSOURCES);
2756 // 16+2+501*(4+2+4+2+16) = 14046 bytes max.
2757 if (result->GetPacketSize() > 354) {
2758 result->PackPacket();
2761 return result;
2764 void CPartFile::AddClientSources(CMemFile* sources, unsigned nSourceFrom, uint8 uClientSXVersion, bool bSourceExchange2, const CUpDownClient* /*pClient*/)
2766 // Kad reviewed
2768 if (m_stopped) {
2769 return;
2772 uint32 nCount = 0;
2773 uint8 uPacketSXVersion = 0;
2774 if (!bSourceExchange2) {
2775 nCount = sources->ReadUInt16();
2777 // Check if the data size matches the 'nCount' for v1 or v2 and eventually correct the source
2778 // exchange version while reading the packet data. Otherwise we could experience a higher
2779 // chance in dealing with wrong source data, userhashs and finally duplicate sources.
2780 uint32 uDataSize = sources->GetLength() - sources->GetPosition();
2782 if ((uint32)(nCount*(4+2+4+2)) == uDataSize) { //Checks if version 1 packet is correct size
2783 if(uClientSXVersion != 1) {
2784 return;
2786 uPacketSXVersion = 1;
2787 } else if ((uint32)(nCount*(4+2+4+2+16)) == uDataSize) { // Checks if version 2&3 packet is correct size
2788 if (uClientSXVersion == 2) {
2789 uPacketSXVersion = 2;
2790 } else if (uClientSXVersion > 2) {
2791 uPacketSXVersion = 3;
2792 } else {
2793 return;
2795 } else if (nCount*(4+2+4+2+16+1) == uDataSize) {
2796 if (uClientSXVersion != 4 ) {
2797 return;
2799 uPacketSXVersion = 4;
2800 } else {
2801 // If v5 inserts additional data (like v2), the above code will correctly filter those packets.
2802 // If v5 appends additional data after <count>(<Sources>)[count], we are in trouble with the
2803 // above code. Though a client which does not understand v5+ should never receive such a packet.
2804 AddDebugLogLineM(false, logClient, CFormat(wxT("Received invalid source exchange packet (v%u) of data size %u for %s")) % uClientSXVersion % uDataSize % GetFileName());
2805 return;
2807 } else {
2808 // for SX2:
2809 // We only check if the version is known by us and do a quick sanitize check on known version
2810 // other then SX1, the packet will be ignored if any error appears, sicne it can't be a "misunderstanding" anymore
2811 if (uClientSXVersion > SOURCEEXCHANGE2_VERSION || uClientSXVersion == 0 ){
2812 AddDebugLogLineM(false, logPartFile, CFormat(wxT("Invalid source exchange type version: %i")) % uClientSXVersion);
2813 return;
2816 // all known versions use the first 2 bytes as count and unknown version are already filtered above
2817 nCount = sources->ReadUInt16();
2818 uint32 uDataSize = (uint32)(sources->GetLength() - sources->GetPosition());
2819 bool bError = false;
2820 switch (uClientSXVersion){
2821 case 1:
2822 bError = nCount*(4+2+4+2) != uDataSize;
2823 break;
2824 case 2:
2825 case 3:
2826 bError = nCount*(4+2+4+2+16) != uDataSize;
2827 break;
2828 case 4:
2829 bError = nCount*(4+2+4+2+16+1) != uDataSize;
2830 break;
2831 default:
2832 wxASSERT( 0 );
2835 if (bError){
2836 wxASSERT( 0 );
2837 AddDebugLogLineM(false, logPartFile, wxT("Invalid source exchange data size."));
2838 return;
2840 uPacketSXVersion = uClientSXVersion;
2843 for (uint16 i = 0;i != nCount;++i) {
2845 uint32 dwID = sources->ReadUInt32();
2846 uint16 nPort = sources->ReadUInt16();
2847 uint32 dwServerIP = sources->ReadUInt32();
2848 uint16 nServerPort = sources->ReadUInt16();
2850 CMD4Hash userHash;
2851 if (uPacketSXVersion > 1) {
2852 userHash = sources->ReadHash();
2855 uint8 byCryptOptions = 0;
2856 if (uPacketSXVersion >= 4) {
2857 byCryptOptions = sources->ReadUInt8();
2860 //Clients send ID's the the Hyrbid format so highID clients with *.*.*.0 won't be falsely switched to a lowID..
2861 uint32 dwIDED2K;
2862 if (uPacketSXVersion >= 3) {
2863 dwIDED2K = wxUINT32_SWAP_ALWAYS(dwID);
2864 } else {
2865 dwIDED2K = dwID;
2868 // check the HighID(IP) - "Filter LAN IPs" and "IPfilter" the received sources IP addresses
2869 if (!IsLowID(dwID)) {
2870 if (!IsGoodIP(dwIDED2K, thePrefs::FilterLanIPs())) {
2871 // check for 0-IP, localhost and optionally for LAN addresses
2872 AddDebugLogLineM(false, logIPFilter, CFormat(wxT("Ignored source (IP=%s) received via %s - bad IP")) % Uint32toStringIP(dwIDED2K) % OriginToText(nSourceFrom));
2873 continue;
2875 if (theApp->ipfilter->IsFiltered(dwIDED2K)) {
2876 AddDebugLogLineM(false, logIPFilter, CFormat(wxT("Ignored source (IP=%s) received via %s - IPFilter")) % Uint32toStringIP(dwIDED2K) % OriginToText(nSourceFrom));
2877 continue;
2879 if (theApp->clientlist->IsBannedClient(dwIDED2K)){
2880 continue;
2884 // additionally check for LowID and own IP
2885 if (!CanAddSource(dwID, nPort, dwServerIP, nServerPort, NULL, false)) {
2886 AddDebugLogLineM(false, logIPFilter, CFormat(wxT("Ignored source (IP=%s) received via source exchange")) % Uint32toStringIP(dwIDED2K));
2887 continue;
2890 if(thePrefs::GetMaxSourcePerFile() > GetSourceCount()) {
2891 CUpDownClient* newsource = new CUpDownClient(nPort,dwID,dwServerIP,nServerPort,this, (uPacketSXVersion < 3), true);
2892 if (uPacketSXVersion > 1) {
2893 newsource->SetUserHash(userHash);
2896 if (uPacketSXVersion >= 4) {
2897 newsource->SetCryptLayerSupport((byCryptOptions & 0x01) != 0);
2898 newsource->SetCryptLayerRequest((byCryptOptions & 0x02) != 0);
2899 newsource->SetCryptLayerRequires((byCryptOptions & 0x04) != 0);
2902 newsource->SetSourceFrom((ESourceFrom)nSourceFrom);
2903 theApp->downloadqueue->CheckAndAddSource(this,newsource);
2905 } else {
2906 break;
2911 void CPartFile::UpdateAutoDownPriority()
2913 if (!IsAutoDownPriority()) {
2914 return;
2916 if (GetSourceCount() <= RARE_FILE) {
2917 if ( GetDownPriority() != PR_HIGH )
2918 SetDownPriority(PR_HIGH, false, false);
2919 } else if (GetSourceCount() < 100) {
2920 if ( GetDownPriority() != PR_NORMAL )
2921 SetDownPriority(PR_NORMAL, false, false);
2922 } else {
2923 if ( GetDownPriority() != PR_LOW )
2924 SetDownPriority(PR_LOW, false, false);
2928 // making this function return a higher when more sources have the extended
2929 // protocol will force you to ask a larger variety of people for sources
2931 int CPartFile::GetCommonFilePenalty()
2933 //TODO: implement, but never return less than MINCOMMONPENALTY!
2934 return MINCOMMONPENALTY;
2937 /* Barry - Replaces BlockReceived()
2939 Originally this only wrote to disk when a full 180k block
2940 had been received from a client, and only asked for data in
2941 180k blocks.
2943 This meant that on average 90k was lost for every connection
2944 to a client data source. That is a lot of wasted data.
2946 To reduce the lost data, packets are now written to a buffer
2947 and flushed to disk regularly regardless of size downloaded.
2948 This includes compressed packets.
2950 Data is also requested only where gaps are, not in 180k blocks.
2951 The requests will still not exceed 180k, but may be smaller to
2952 fill a gap.
2955 // Kry - transize is 32bits, no packet can be more than that (this is
2956 // compressed size). Even 32bits is too much imho.As for the return size,
2957 // look at the lenData below.
2958 uint32 CPartFile::WriteToBuffer(uint32 transize, byte* data, uint64 start, uint64 end, Requested_Block_Struct *block)
2960 // Increment transferred bytes counter for this file
2961 transferred += transize;
2963 // This is needed a few times
2964 // Kry - should not need a uint64 here - no block is larger than
2965 // 2GB even after uncompressed.
2966 uint32 lenData = (uint32) (end - start + 1);
2968 if(lenData > transize) {
2969 m_iGainDueToCompression += lenData-transize;
2972 // Occasionally packets are duplicated, no point writing it twice
2973 if (IsComplete(start, end)) {
2974 AddDebugLogLineM(false, logPartFile,
2975 CFormat(wxT("File '%s' has already been written from %u to %u"))
2976 % GetFileName() % start % end);
2977 return 0;
2980 // Create copy of data as new buffer
2981 byte *buffer = new byte[lenData];
2982 memcpy(buffer, data, lenData);
2984 // Create a new buffered queue entry
2985 PartFileBufferedData *item = new PartFileBufferedData;
2986 item->data = buffer;
2987 item->start = start;
2988 item->end = end;
2989 item->block = block;
2991 // Add to the queue in the correct position (most likely the end)
2992 bool added = false;
2994 std::list<PartFileBufferedData*>::iterator it = m_BufferedData_list.begin();
2995 for (; it != m_BufferedData_list.end(); ++it) {
2996 PartFileBufferedData* queueItem = *it;
2998 if (item->end <= queueItem->end) {
2999 if (it != m_BufferedData_list.begin()) {
3000 added = true;
3002 m_BufferedData_list.insert(--it, item);
3005 break;
3009 if (!added) {
3010 m_BufferedData_list.push_front(item);
3013 // Increment buffer size marker
3014 m_nTotalBufferData += lenData;
3016 // Mark this small section of the file as filled
3017 FillGap(item->start, item->end);
3019 // Update the flushed mark on the requested block
3020 // The loop here is unfortunate but necessary to detect deleted blocks.
3022 std::list<Requested_Block_Struct*>::iterator it2 = m_requestedblocks_list.begin();
3023 for (; it2 != m_requestedblocks_list.end(); ++it2) {
3024 if (*it2 == item->block) {
3025 item->block->transferred += lenData;
3029 if (m_gaplist.empty()) {
3030 FlushBuffer(true);
3033 // Return the length of data written to the buffer
3034 return lenData;
3037 void CPartFile::FlushBuffer(bool /*forcewait*/, bool bForceICH, bool bNoAICH)
3039 m_nLastBufferFlushTime = GetTickCount();
3041 if (m_BufferedData_list.empty()) {
3042 return;
3046 uint32 partCount = GetPartCount();
3047 std::vector<bool> changedPart(partCount);
3049 // Remember which parts need to be checked at the end of the flush
3050 for ( uint32 i = 0; i < partCount; ++i ) {
3051 changedPart[ i ] = false;
3055 // Ensure file is big enough to write data to (the last item will be the furthest from the start)
3056 uint32 newData = 0;
3058 std::list<PartFileBufferedData*>::iterator it = m_BufferedData_list.begin();
3059 for (; it != m_BufferedData_list.end(); ++it) {
3060 PartFileBufferedData* item = *it;
3061 wxASSERT((item->end - item->start) < 0xFFFFFFFF);
3062 newData += (uint32) (item->end - item->start + 1);
3065 if ( !CheckFreeDiskSpace( newData ) ) {
3066 // Not enough free space to write the last item, bail
3067 AddLogLineM(true, CFormat( _("WARNING: Not enough free disk-space! Pausing file: %s") ) % GetFileName());
3069 PauseFile( true );
3070 return;
3073 // Loop through queue
3074 while ( !m_BufferedData_list.empty() ) {
3075 // Get top item and remove it from the queue
3076 PartFileBufferedData* item = m_BufferedData_list.front();
3077 m_BufferedData_list.pop_front();
3079 // This is needed a few times
3080 wxASSERT((item->end - item->start) < 0xFFFFFFFF);
3081 uint32 lenData = (uint32)(item->end - item->start + 1);
3083 // SLUGFILLER: SafeHash - could be more than one part
3084 for (uint32 curpart = (item->start/PARTSIZE); curpart <= (item->end/PARTSIZE); ++curpart) {
3085 wxASSERT(curpart < partCount);
3086 changedPart[curpart] = true;
3088 // SLUGFILLER: SafeHash
3090 // Go to the correct position in file and write block of data
3091 try {
3092 m_hpartfile.Seek(item->start);
3093 m_hpartfile.Write(item->data, lenData);
3094 } catch (const CIOFailureException& e) {
3095 AddDebugLogLineM(true, logPartFile, wxT("Error while saving part-file: ") + e.what());
3096 SetPartFileStatus(PS_ERROR);
3099 // Decrease buffer size
3100 m_nTotalBufferData -= lenData;
3102 // Release memory used by this item
3103 delete [] item->data;
3104 delete item;
3108 // Update last-changed date
3109 m_lastDateChanged = wxDateTime::GetTimeNow();
3111 try {
3112 // Partfile should never be too large
3113 if (m_hpartfile.GetLength() > GetFileSize()) {
3114 // it's "last chance" correction. the real bugfix has to be applied 'somewhere' else
3115 m_hpartfile.SetLength(GetFileSize());
3117 } catch (const CIOFailureException& e) {
3118 AddDebugLogLineM(true, logPartFile,
3119 CFormat(wxT("Error while truncating part-file (%s): %s"))
3120 % m_fullname.RemoveExt() % e.what());
3121 SetPartFileStatus(PS_ERROR);
3126 // Check each part of the file
3127 uint32 partRange = 0;
3128 try {
3129 uint64 curLength = m_hpartfile.GetLength();
3131 partRange = (uint32)((curLength % PARTSIZE > 0) ? ((curLength % PARTSIZE) - 1) : (PARTSIZE - 1));
3132 } catch (const CIOFailureException& e) {
3133 AddDebugLogLineM(true, logPartFile,
3134 CFormat(wxT("Error while accessing part-file (%s): %s"))
3135 % m_fullname.RemoveExt() % e.what());
3136 SetPartFileStatus(PS_ERROR);
3139 wxASSERT(partRange);
3140 for (int partNumber = partCount-1; partRange && partNumber >= 0; partNumber--) {
3141 if (changedPart[partNumber] == false) {
3142 // Any parts other than last must be full size
3143 partRange = PARTSIZE - 1;
3144 continue;
3147 // Is this 9MB part complete
3148 if (IsComplete(PARTSIZE * partNumber, (PARTSIZE * (partNumber + 1)) - 1)) {
3149 // Is part corrupt
3150 if (!HashSinglePart(partNumber)) {
3151 AddLogLineM(true, CFormat(
3152 _("Downloaded part %i is corrupt in file: %s") ) % partNumber % GetFileName() );
3153 AddGap(PARTSIZE*partNumber, (PARTSIZE*partNumber + partRange));
3154 // add part to corrupted list, if not already there
3155 if (!IsCorruptedPart(partNumber)) {
3156 m_corrupted_list.push_back(partNumber);
3158 // request AICH recovery data
3159 if (!bNoAICH) {
3160 RequestAICHRecovery((uint16)partNumber);
3162 // Reduce transferred amount by corrupt amount
3163 m_iLostDueToCorruption += (partRange + 1);
3164 } else {
3165 if (!m_hashsetneeded) {
3166 AddDebugLogLineM(false, logPartFile, CFormat(
3167 wxT("Finished part %u of '%s'")) % partNumber % GetFileName());
3170 // if this part was successfully completed (although ICH is active), remove from corrupted list
3171 EraseFirstValue(m_corrupted_list, partNumber);
3173 if (status == PS_EMPTY) {
3174 if (theApp->IsRunning()) { // may be called during shutdown!
3175 if (GetHashCount() == GetED2KPartHashCount() && !m_hashsetneeded) {
3176 // Successfully completed part, make it available for sharing
3177 SetStatus(PS_READY);
3178 theApp->sharedfiles->SafeAddKFile(this);
3183 } else if ( IsCorruptedPart(partNumber) && (thePrefs::IsICHEnabled() || bForceICH)) {
3184 // Try to recover with minimal loss
3185 if (HashSinglePart(partNumber)) {
3186 ++m_iTotalPacketsSavedDueToICH;
3188 uint64 uMissingInPart = GetTotalGapSizeInPart(partNumber);
3189 FillGap(PARTSIZE*partNumber,(PARTSIZE*partNumber+partRange));
3190 RemoveBlockFromList(PARTSIZE*partNumber,(PARTSIZE*partNumber + partRange));
3192 // remove from corrupted list
3193 EraseFirstValue(m_corrupted_list, partNumber);
3195 AddLogLineM(true, CFormat( _("ICH: Recovered corrupted part %i for %s -> Saved bytes: %s") )
3196 % partNumber
3197 % GetFileName()
3198 % CastItoXBytes(uMissingInPart));
3200 if (GetHashCount() == GetED2KPartHashCount() && !m_hashsetneeded) {
3201 if (status == PS_EMPTY) {
3202 // Successfully recovered part, make it available for sharing
3203 SetStatus(PS_READY);
3204 if (theApp->IsRunning()) // may be called during shutdown!
3205 theApp->sharedfiles->SafeAddKFile(this);
3210 // Any parts other than last must be full size
3211 partRange = PARTSIZE - 1;
3214 // Update met file
3215 SavePartFile();
3217 if (theApp->IsRunning()) { // may be called during shutdown!
3218 // Is this file finished ?
3219 if (m_gaplist.empty()) {
3220 CompleteFile(false);
3226 void CPartFile::UpdateFileRatingCommentAvail()
3228 bool prevComment = m_hasComment;
3229 int prevRating = m_iUserRating;
3231 m_hasComment = false;
3232 m_iUserRating = 0;
3233 int ratingCount = 0;
3235 SourceSet::iterator it = m_SrcList.begin();
3236 for (; it != m_SrcList.end(); ++it) {
3237 CUpDownClient* cur_src = *it;
3239 if (!cur_src->GetFileComment().IsEmpty()) {
3240 if (thePrefs::IsCommentFiltered(cur_src->GetFileComment())) {
3241 continue;
3243 m_hasComment = true;
3246 uint8 rating = cur_src->GetFileRating();
3247 if (rating) {
3248 wxASSERT(rating <= 5);
3250 ratingCount++;
3251 m_iUserRating += rating;
3255 if (ratingCount) {
3256 m_iUserRating /= ratingCount;
3257 wxASSERT(m_iUserRating > 0 && m_iUserRating <= 5);
3260 if ((prevComment != m_hasComment) || (prevRating != m_iUserRating)) {
3261 UpdateDisplayedInfo();
3266 void CPartFile::SetCategory(uint8 cat)
3268 wxASSERT( cat < theApp->glob_prefs->GetCatCount() );
3270 m_category = cat;
3271 SavePartFile();
3274 bool CPartFile::RemoveSource(CUpDownClient* toremove, bool updatewindow, bool bDoStatsUpdate)
3276 wxASSERT( toremove );
3278 bool result = theApp->downloadqueue->RemoveSource( toremove, updatewindow, bDoStatsUpdate );
3280 // Check if the client should be deleted, but not if the client is already dying
3281 if ( !toremove->GetSocket() && !toremove->HasBeenDeleted() ) {
3282 if ( toremove->Disconnected(wxT("RemoveSource - purged")) ) {
3283 toremove->Safe_Delete();
3287 return result;
3290 void CPartFile::AddDownloadingSource(CUpDownClient* client)
3292 CClientPtrList::iterator it =
3293 std::find(m_downloadingSourcesList.begin(), m_downloadingSourcesList.end(), client);
3294 if (it == m_downloadingSourcesList.end()) {
3295 m_downloadingSourcesList.push_back(client);
3300 void CPartFile::RemoveDownloadingSource(CUpDownClient* client)
3302 CClientPtrList::iterator it =
3303 std::find(m_downloadingSourcesList.begin(), m_downloadingSourcesList.end(), client);
3304 if (it != m_downloadingSourcesList.end()) {
3305 m_downloadingSourcesList.erase(it);
3310 void CPartFile::SetPartFileStatus(uint8 newstatus)
3312 status=newstatus;
3314 if (thePrefs::GetAllcatType()) {
3315 Notify_DownloadCtrlUpdateItem(this);
3318 Notify_DownloadCtrlSort();
3322 uint64 CPartFile::GetNeededSpace()
3324 try {
3325 uint64 length = m_hpartfile.GetLength();
3327 if (length > GetFileSize()) {
3328 return 0; // Shouldn't happen, but just in case
3331 return GetFileSize() - length;
3332 } catch (const CIOFailureException& e) {
3333 AddDebugLogLineM(true, logPartFile,
3334 CFormat(wxT("Error while retrieving file-length (%s): %s"))
3335 % m_fullname.RemoveExt() % e.what());
3336 SetPartFileStatus(PS_ERROR);
3337 return 0;
3341 void CPartFile::SetStatus(uint8 in)
3343 wxASSERT( in != PS_PAUSED && in != PS_INSUFFICIENT );
3345 status = in;
3347 if (theApp->IsRunning()) {
3348 UpdateDisplayedInfo( true );
3350 if ( thePrefs::ShowCatTabInfos() ) {
3351 Notify_ShowUpdateCatTabTitles();
3357 uint64 CPartFile::GetTotalGapSizeInRange(uint64 uRangeStart, uint64 uRangeEnd) const
3359 uint64 uTotalGapSize = 0;
3361 if (uRangeEnd >= GetFileSize()) {
3362 uRangeEnd = GetFileSize() - 1;
3365 std::list<Gap_Struct*>::const_iterator it = m_gaplist.begin();
3366 for (; it != m_gaplist.end(); ++it) {
3367 const Gap_Struct* pGap = *it;
3369 if (pGap->start < uRangeStart && pGap->end > uRangeEnd) {
3370 uTotalGapSize += uRangeEnd - uRangeStart + 1;
3371 break;
3374 if (pGap->start >= uRangeStart && pGap->start <= uRangeEnd) {
3375 uint64 uEnd = (pGap->end > uRangeEnd) ? uRangeEnd : pGap->end;
3376 uTotalGapSize += uEnd - pGap->start + 1;
3377 } else if (pGap->end >= uRangeStart && pGap->end <= uRangeEnd) {
3378 uTotalGapSize += pGap->end - uRangeStart + 1;
3382 wxASSERT( uTotalGapSize <= uRangeEnd - uRangeStart + 1 );
3384 return uTotalGapSize;
3387 uint64 CPartFile::GetTotalGapSizeInPart(uint32 uPart) const
3389 uint64 uRangeStart = uPart * PARTSIZE;
3390 uint64 uRangeEnd = uRangeStart + PARTSIZE - 1;
3391 if (uRangeEnd >= GetFileSize()) {
3392 uRangeEnd = GetFileSize();
3394 return GetTotalGapSizeInRange(uRangeStart, uRangeEnd);
3398 void CPartFile::RequestAICHRecovery(uint16 nPart)
3401 if ( !m_pAICHHashSet->HasValidMasterHash() ||
3402 (m_pAICHHashSet->GetStatus() != AICH_TRUSTED && m_pAICHHashSet->GetStatus() != AICH_VERIFIED)){
3403 AddDebugLogLineM( false, logAICHRecovery, wxT("Unable to request AICH Recoverydata because we have no trusted Masterhash") );
3404 return;
3406 if (GetFileSize() <= EMBLOCKSIZE || GetFileSize() - PARTSIZE*nPart <= EMBLOCKSIZE)
3407 return;
3408 if (CAICHHashSet::IsClientRequestPending(this, nPart)){
3409 AddDebugLogLineM( false, logAICHRecovery, wxT("RequestAICHRecovery: Already a request for this part pending"));
3410 return;
3413 // first check if we have already the recoverydata, no need to rerequest it then
3414 if (m_pAICHHashSet->IsPartDataAvailable(nPart*PARTSIZE)){
3415 AddDebugLogLineM( false, logAICHRecovery, wxT("Found PartRecoveryData in memory"));
3416 AICHRecoveryDataAvailable(nPart);
3417 return;
3420 wxASSERT( nPart < GetPartCount() );
3421 // find some random client which support AICH to ask for the blocks
3422 // first lets see how many we have at all, we prefer high id very much
3423 uint32 cAICHClients = 0;
3424 uint32 cAICHLowIDClients = 0;
3425 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it) {
3426 CUpDownClient* pCurClient = *(it);
3427 if ( pCurClient->IsSupportingAICH() &&
3428 pCurClient->GetReqFileAICHHash() != NULL &&
3429 !pCurClient->IsAICHReqPending()
3430 && (*pCurClient->GetReqFileAICHHash()) == m_pAICHHashSet->GetMasterHash())
3432 if (pCurClient->HasLowID()) {
3433 ++cAICHLowIDClients;
3434 } else {
3435 ++cAICHClients;
3439 if ((cAICHClients | cAICHLowIDClients) == 0){
3440 AddDebugLogLineM( false, logAICHRecovery, wxT("Unable to request AICH Recoverydata because found no client who supports it and has the same hash as the trusted one"));
3441 return;
3443 uint32 nSeclectedClient;
3444 if (cAICHClients > 0) {
3445 nSeclectedClient = (rand() % cAICHClients) + 1;
3446 } else {
3447 nSeclectedClient = (rand() % cAICHLowIDClients) + 1;
3449 CUpDownClient* pClient = NULL;
3450 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it) {
3451 CUpDownClient* pCurClient = *(it);
3452 if (pCurClient->IsSupportingAICH() && pCurClient->GetReqFileAICHHash() != NULL && !pCurClient->IsAICHReqPending()
3453 && (*pCurClient->GetReqFileAICHHash()) == m_pAICHHashSet->GetMasterHash())
3455 if (cAICHClients > 0){
3456 if (!pCurClient->HasLowID())
3457 nSeclectedClient--;
3459 else{
3460 wxASSERT( pCurClient->HasLowID());
3461 nSeclectedClient--;
3463 if (nSeclectedClient == 0){
3464 pClient = pCurClient;
3465 break;
3469 if (pClient == NULL){
3470 wxASSERT( false );
3471 return;
3474 AddDebugLogLineM( false, logAICHRecovery, CFormat( wxT("Requesting AICH Hash (%s) form client %s") ) % ( cAICHClients ? wxT("HighId") : wxT("LowID") ) % pClient->GetClientFullInfo() );
3475 pClient->SendAICHRequest(this, nPart);
3480 void CPartFile::AICHRecoveryDataAvailable(uint16 nPart)
3482 if (GetPartCount() < nPart){
3483 wxASSERT( false );
3484 return;
3487 FlushBuffer(true, true, true);
3489 uint64 length = PARTSIZE;
3491 try {
3492 if ((unsigned)(PARTSIZE * (nPart + 1)) > m_hpartfile.GetLength()){
3493 length = (m_hpartfile.GetLength() - (PARTSIZE * nPart));
3494 wxASSERT( length <= PARTSIZE );
3496 } catch (const CIOFailureException& e) {
3497 AddDebugLogLineM(true, logPartFile,
3498 CFormat(wxT("Error while retrieving file-length (%s): %s"))
3499 % m_fullname.RemoveExt() % e.what());
3500 SetPartFileStatus(PS_ERROR);
3501 return;
3504 // if the part was already ok, it would now be complete
3505 if (IsComplete(nPart*PARTSIZE, ((nPart*PARTSIZE)+length)-1)){
3506 AddDebugLogLineM( false, logAICHRecovery,
3507 wxString::Format( wxT("Processing AICH Recovery data: The part (%u) is already complete, canceling"), nPart ) );
3508 return;
3513 CAICHHashTree* pVerifiedHash = m_pAICHHashSet->m_pHashTree.FindHash(nPart*PARTSIZE, length);
3514 if (pVerifiedHash == NULL || !pVerifiedHash->GetHashValid()){
3515 AddDebugLogLineM( true, logAICHRecovery, wxT("Processing AICH Recovery data: Unable to get verified hash from hashset (should never happen)") );
3516 wxASSERT( false );
3517 return;
3519 CAICHHashTree htOurHash(pVerifiedHash->GetNDataSize(), pVerifiedHash->GetIsLeftBranch(), pVerifiedHash->GetNBaseSize());
3520 try {
3521 m_hpartfile.Seek(PARTSIZE * nPart,wxFromStart);
3522 CreateHashFromFile(&m_hpartfile,length, NULL, &htOurHash);
3523 } catch (const CIOFailureException& e) {
3524 AddDebugLogLineM(true, logAICHRecovery,
3525 CFormat(wxT("IO failure while hashing part-file '%s': %s"))
3526 % m_hpartfile.GetFilePath() % e.what());
3527 SetPartFileStatus(PS_ERROR);
3528 return;
3531 if (!htOurHash.GetHashValid()){
3532 AddDebugLogLineM( false, logAICHRecovery, wxT("Processing AICH Recovery data: Failed to retrieve AICH Hashset of corrupt part") );
3533 wxASSERT( false );
3534 return;
3537 // now compare the hash we just did, to the verified hash and readd all blocks which are ok
3538 uint32 nRecovered = 0;
3539 for (uint32 pos = 0; pos < length; pos += EMBLOCKSIZE){
3540 const uint32 nBlockSize = min<uint32>(EMBLOCKSIZE, length - pos);
3541 CAICHHashTree* pVerifiedBlock = pVerifiedHash->FindHash(pos, nBlockSize);
3542 CAICHHashTree* pOurBlock = htOurHash.FindHash(pos, nBlockSize);
3543 if ( pVerifiedBlock == NULL || pOurBlock == NULL || !pVerifiedBlock->GetHashValid() || !pOurBlock->GetHashValid()){
3544 wxASSERT( false );
3545 continue;
3547 if (pOurBlock->GetHash() == pVerifiedBlock->GetHash()){
3548 FillGap(PARTSIZE*nPart+pos, PARTSIZE*nPart + pos + (nBlockSize-1));
3549 RemoveBlockFromList(PARTSIZE*nPart, PARTSIZE*nPart + (nBlockSize-1));
3550 nRecovered += nBlockSize;
3554 // ok now some sanity checks
3555 if (IsComplete(nPart*PARTSIZE, ((nPart*PARTSIZE)+length)-1)){
3556 // this is a bad, but it could probably happen under some rare circumstances
3557 // make sure that MD4 agrres to this fact too
3558 if (!HashSinglePart(nPart)){
3559 AddDebugLogLineM( false, logAICHRecovery,
3560 wxString::Format(wxT("Processing AICH Recovery data: The part (%u) got completed while recovering - but MD4 says it corrupt! Setting hashset to error state, deleting part"), nPart));
3561 // now we are fu... unhappy
3562 m_pAICHHashSet->SetStatus(AICH_ERROR);
3563 AddGap(PARTSIZE*nPart, ((nPart*PARTSIZE)+length)-1);
3564 wxASSERT( false );
3565 return;
3567 else{
3568 AddDebugLogLineM( false, logAICHRecovery, wxString::Format(
3569 wxT("Processing AICH Recovery data: The part (%u) got completed while recovering and MD4 agrees"), nPart) );
3570 // alrighty not so bad
3571 EraseFirstValue(m_corrupted_list, nPart);
3572 if (status == PS_EMPTY && theApp->IsRunning()){
3573 if (GetHashCount() == GetED2KPartHashCount() && !m_hashsetneeded){
3574 // Successfully recovered part, make it available for sharing
3575 SetStatus(PS_READY);
3576 theApp->sharedfiles->SafeAddKFile(this);
3580 if (theApp->IsRunning()){
3581 // Is this file finished?
3582 if (m_gaplist.empty()) {
3583 CompleteFile(false);
3587 } // end sanity check
3588 // Update met file
3589 SavePartFile();
3591 // make sure the user appreciates our great recovering work :P
3592 AddDebugLogLineM( true, logAICHRecovery, CFormat(
3593 wxT("AICH successfully recovered %s of %s from part %u for %s") )
3594 % CastItoXBytes(nRecovered)
3595 % CastItoXBytes(length)
3596 % nPart
3597 % GetFileName() );
3601 void CPartFile::ClientStateChanged( int oldState, int newState )
3603 if ( oldState == newState )
3604 return;
3606 // If the state is -1, then it's an entirely new item
3607 if ( oldState != -1 ) {
3608 // Was the old state a valid state?
3609 if ( oldState == DS_ONQUEUE || oldState == DS_DOWNLOADING ) {
3610 m_validSources--;
3611 } else {
3612 if ( oldState == DS_CONNECTED /* || oldState == DS_REMOTEQUEUEFULL */ ) {
3613 m_validSources--;
3616 m_notCurrentSources--;
3620 // If the state is -1, then the source is being removed
3621 if ( newState != -1 ) {
3622 // Was the old state a valid state?
3623 if ( newState == DS_ONQUEUE || newState == DS_DOWNLOADING ) {
3624 ++m_validSources;
3625 } else {
3626 if ( newState == DS_CONNECTED /* || newState == DS_REMOTEQUEUEFULL */ ) {
3627 ++m_validSources;
3630 ++m_notCurrentSources;
3636 bool CPartFile::AddSource( CUpDownClient* client )
3638 if (m_SrcList.insert( client ).second) {
3639 theStats::AddFoundSource();
3640 theStats::AddSourceOrigin(client->GetSourceFrom());
3641 return true;
3642 } else {
3643 return false;
3648 bool CPartFile::DelSource( CUpDownClient* client )
3650 if (m_SrcList.erase( client )) {
3651 theStats::RemoveSourceOrigin(client->GetSourceFrom());
3652 theStats::RemoveFoundSource();
3653 return true;
3654 } else {
3655 return false;
3660 void CPartFile::UpdatePartsFrequency( CUpDownClient* client, bool increment )
3662 const BitVector& freq = client->GetPartStatus();
3664 if ( m_SrcpartFrequency.size() != GetPartCount() ) {
3665 m_SrcpartFrequency.clear();
3666 m_SrcpartFrequency.insert(m_SrcpartFrequency.begin(), GetPartCount(), 0);
3668 if ( !increment ) {
3669 return;
3673 unsigned int size = freq.size();
3674 if ( size != m_SrcpartFrequency.size() ) {
3675 return;
3678 if ( increment ) {
3679 for ( unsigned int i = 0; i < size; i++ ) {
3680 if ( freq[i] ) {
3681 m_SrcpartFrequency[i]++;
3684 } else {
3685 for ( unsigned int i = 0; i < size; i++ ) {
3686 if ( freq[i] ) {
3687 m_SrcpartFrequency[i]--;
3693 const FileRatingList &CPartFile::GetRatingAndComments()
3695 m_FileRatingList.clear();
3696 // This can be pre-processed, but is it worth the CPU?
3697 CPartFile::SourceSet::iterator it = m_SrcList.begin();
3698 for ( ; it != m_SrcList.end(); ++it ) {
3699 CUpDownClient *cur_src = *it;
3700 if (cur_src->GetFileComment().Length()>0 || cur_src->GetFileRating()>0) {
3701 // AddDebugLogLineM(false, logPartFile, wxString(wxT("found a comment for ")) << GetFileName());
3702 m_FileRatingList.push_back(SFileRating(*cur_src));
3706 return m_FileRatingList;
3709 #else // CLIENT_GUI
3711 CPartFile::CPartFile(CEC_PartFile_Tag *tag)
3713 Init();
3715 SetFileName(CPath(tag->FileName()));
3716 m_abyFileHash = tag->ID();
3717 SetFileSize(tag->SizeFull());
3718 m_partmetfilename = CPath(tag->PartMetName());
3719 transferred = tag->SizeXfer();
3720 percentcompleted = (100.0*completedsize) / GetFileSize();
3721 completedsize = tag->SizeDone();
3723 m_category = tag->FileCat();
3725 m_iPartCount = ((uint64)GetFileSize() + (PARTSIZE - 1)) / PARTSIZE;
3726 m_SrcpartFrequency.insert(m_SrcpartFrequency.end(), m_iPartCount, 0);
3727 m_iDownPriority = tag->Prio();
3728 if ( m_iDownPriority >= 10 ) {
3729 m_iDownPriority-= 10;
3730 m_bAutoDownPriority = true;
3731 } else {
3732 m_bAutoDownPriority = false;
3734 // FIXME: !
3735 m_category = 0;
3737 m_source_count = 0;
3738 m_a4af_source_count = 0;
3742 * Remote gui specific code
3744 CPartFile::~CPartFile()
3748 const FileRatingList &CPartFile::GetRatingAndComments()
3750 return m_FileRatingList;
3752 #endif // !CLIENT_GUI
3755 void CPartFile::UpdateDisplayedInfo(bool force)
3757 uint32 curTick = ::GetTickCount();
3758 m_CommentUpdated = true;
3760 // Wait 1.5s between each redraw
3761 if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE ) {
3762 Notify_DownloadCtrlUpdateItem(this);
3763 m_lastRefreshedDLDisplay = curTick;
3769 void CPartFile::Init()
3771 m_showSources = false;
3772 m_lastsearchtime = 0;
3773 lastpurgetime = ::GetTickCount();
3774 m_paused = false;
3775 m_stopped = false;
3776 m_insufficient = false;
3778 status = PS_EMPTY;
3780 transferred = 0;
3781 m_iLastPausePurge = time(NULL);
3783 if(thePrefs::GetNewAutoDown()) {
3784 m_iDownPriority = PR_HIGH;
3785 m_bAutoDownPriority = true;
3786 } else {
3787 m_iDownPriority = PR_NORMAL;
3788 m_bAutoDownPriority = false;
3791 memset(m_anStates,0,sizeof(m_anStates));
3793 transferingsrc = 0; // new
3795 kBpsDown = 0.0;
3797 m_CommentUpdated = false;
3798 m_hashsetneeded = true;
3799 m_count = 0;
3800 percentcompleted = 0;
3801 completedsize=0;
3802 m_bPreviewing = false;
3803 lastseencomplete = 0;
3804 m_availablePartsCount=0;
3805 m_ClientSrcAnswered = 0;
3806 m_LastNoNeededCheck = 0;
3807 m_iRating = 0;
3808 m_nTotalBufferData = 0;
3809 m_nLastBufferFlushTime = 0;
3810 m_bPercentUpdated = false;
3811 m_bRecoveringArchive = false;
3812 m_iGainDueToCompression = 0;
3813 m_iLostDueToCorruption = 0;
3814 m_iTotalPacketsSavedDueToICH = 0;
3815 m_category = 0;
3816 m_lastRefreshedDLDisplay = 0;
3817 m_nDlActiveTime = 0;
3818 m_tActivated = 0;
3819 m_is_A4AF_auto = false;
3820 m_localSrcReqQueued = false;
3821 m_nCompleteSourcesTime = time(NULL);
3822 m_nCompleteSourcesCount = 0;
3823 m_nCompleteSourcesCountLo = 0;
3824 m_nCompleteSourcesCountHi = 0;
3826 m_validSources = 0;
3827 m_notCurrentSources = 0;
3829 // Kad
3830 m_LastSearchTimeKad = 0;
3831 m_TotalSearchesKad = 0;
3835 wxString CPartFile::getPartfileStatus() const
3838 wxString mybuffer;
3840 if ((status == PS_HASHING) || (status == PS_WAITINGFORHASH)) {
3841 mybuffer=_("Hashing");
3842 } else {
3843 switch (GetStatus()) {
3844 case PS_COMPLETING:
3845 mybuffer=_("Completing");
3846 break;
3847 case PS_COMPLETE:
3848 mybuffer=_("Complete");
3849 break;
3850 case PS_PAUSED:
3851 mybuffer=_("Paused");
3852 break;
3853 case PS_ERROR:
3854 mybuffer=_("Erroneous");
3855 break;
3856 case PS_INSUFFICIENT:
3857 mybuffer = _("Insufficient Diskspace");
3858 break;
3859 default:
3860 if (GetTransferingSrcCount()>0) {
3861 mybuffer=_("Downloading");
3862 } else {
3863 mybuffer=_("Waiting");
3865 break;
3867 if (m_stopped && (GetStatus()!=PS_COMPLETE)) {
3868 mybuffer=_("Stopped");
3872 return mybuffer;
3875 int CPartFile::getPartfileStatusRang() const
3878 int tempstatus=0;
3879 if (GetTransferingSrcCount()==0) tempstatus=1;
3880 switch (GetStatus()) {
3881 case PS_HASHING:
3882 case PS_WAITINGFORHASH:
3883 tempstatus=3;
3884 break;
3885 case PS_COMPLETING:
3886 tempstatus=4;
3887 break;
3888 case PS_COMPLETE:
3889 tempstatus=5;
3890 break;
3891 case PS_PAUSED:
3892 tempstatus=2;
3893 break;
3894 case PS_ERROR:
3895 tempstatus=6;
3896 break;
3898 return tempstatus;
3902 wxString CPartFile::GetFeedback() const
3904 wxString retval = CKnownFile::GetFeedback();
3905 if (GetStatus() != PS_COMPLETE) {
3906 retval += wxString(_("Downloaded")) + wxT(": ") + CastItoXBytes(GetCompletedSize()) + wxString::Format(wxT(" (%.2f%%)\n"), GetPercentCompleted())
3907 + _("Sources") + CFormat(wxT(": %u\n")) % GetSourceCount();
3909 return retval + _("Status") + wxT(": ") + getPartfileStatus() + wxT("\n");
3913 sint32 CPartFile::getTimeRemaining() const
3915 if (GetKBpsDown() < 0.001)
3916 return -1;
3917 else
3918 return((GetFileSize()-GetCompletedSize()) / ((int)(GetKBpsDown()*1024.0)));
3921 bool CPartFile::PreviewAvailable()
3923 FileType type = GetFiletype(GetFileName());
3925 return (((type == ftVideo) || (type == ftAudio)) && IsComplete(0, 256*1024));
3928 bool CPartFile::CheckShowItemInGivenCat(int inCategory)
3930 // easy normal cases
3931 bool IsInCat;
3932 bool IsNotFiltered = true;
3934 IsInCat = ((inCategory==0) || (inCategory>0 && inCategory==GetCategory()));
3936 switch (thePrefs::GetAllcatType()) {
3937 case 1:
3938 IsNotFiltered = GetCategory() == 0 || inCategory > 0;
3939 break;
3940 case 2:
3941 IsNotFiltered = IsPartFile();
3942 break;
3943 case 3:
3944 IsNotFiltered = !IsPartFile();
3945 break;
3946 case 4:
3947 IsNotFiltered =
3948 (GetStatus() == PS_READY || GetStatus() == PS_EMPTY) &&
3949 GetTransferingSrcCount() == 0;
3950 break;
3951 case 5:
3952 IsNotFiltered =
3953 (GetStatus() == PS_READY || GetStatus()==PS_EMPTY) &&
3954 GetTransferingSrcCount() > 0;
3955 break;
3956 case 6:
3957 IsNotFiltered = GetStatus() == PS_ERROR;
3958 break;
3959 case 7:
3960 IsNotFiltered = GetStatus() == PS_PAUSED && !IsStopped();
3961 break;
3962 case 8:
3963 IsNotFiltered = IsStopped();
3964 break;
3965 case 9:
3966 IsNotFiltered = GetFiletype(GetFileName()) == ftVideo;
3967 break;
3968 case 10:
3969 IsNotFiltered = GetFiletype(GetFileName()) == ftAudio;
3970 break;
3971 case 11:
3972 IsNotFiltered = GetFiletype(GetFileName()) == ftArchive;
3973 break;
3974 case 12:
3975 IsNotFiltered = GetFiletype(GetFileName()) == ftCDImage;
3976 break;
3977 case 13:
3978 IsNotFiltered = GetFiletype(GetFileName()) == ftPicture;
3979 break;
3980 case 14:
3981 IsNotFiltered = GetFiletype(GetFileName()) == ftText;
3982 break;
3983 case 15:
3984 IsNotFiltered = !IsStopped() && GetStatus() != PS_PAUSED;
3985 break;
3988 return IsNotFiltered && IsInCat;
3991 bool CPartFile::IsComplete(uint64 start, uint64 end)
3993 if (end >= GetFileSize()) {
3994 end = GetFileSize()-1;
3997 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
3998 for (; it != m_gaplist.end(); ++it) {
3999 Gap_Struct* cur_gap = *it;
4000 if ((cur_gap->start >= start && cur_gap->end <= end)||(cur_gap->start >= start
4001 && cur_gap->start <= end)||(cur_gap->end <= end && cur_gap->end >= start)
4002 ||(start >= cur_gap->start && end <= cur_gap->end)) {
4003 return false;
4006 return true;
4010 void CPartFile::SetActive(bool bActive)
4012 time_t tNow = time(NULL);
4013 if (bActive) {
4014 if (theApp->IsConnected()) {
4015 if (m_tActivated == 0) {
4016 m_tActivated = tNow;
4019 } else {
4020 if (m_tActivated != 0) {
4021 m_nDlActiveTime += tNow - m_tActivated;
4022 m_tActivated = 0;
4028 uint32 CPartFile::GetDlActiveTime() const
4030 uint32 nDlActiveTime = m_nDlActiveTime;
4031 if (m_tActivated != 0) {
4032 nDlActiveTime += time(NULL) - m_tActivated;
4034 return nDlActiveTime;
4038 #ifndef CLIENT_GUI
4040 uint8 CPartFile::GetStatus(bool ignorepause) const
4042 if ( (!m_paused && !m_insufficient) ||
4043 status == PS_ERROR ||
4044 status == PS_COMPLETING ||
4045 status == PS_COMPLETE ||
4046 ignorepause) {
4047 return status;
4048 } else if ( m_insufficient ) {
4049 return PS_INSUFFICIENT;
4050 } else {
4051 return PS_PAUSED;
4055 void CPartFile::AddDeadSource(const CUpDownClient* client)
4057 m_deadSources.AddDeadSource( client );
4061 bool CPartFile::IsDeadSource(const CUpDownClient* client)
4063 return m_deadSources.IsDeadSource( client );
4066 void CPartFile::SetFileName(const CPath& fileName)
4068 CKnownFile* pFile = theApp->sharedfiles->GetFileByID(GetFileHash());
4070 bool is_shared = (pFile && pFile == this);
4072 if (is_shared) {
4073 // The file is shared, we must clear the search keywords so we don't
4074 // publish the old name anymore.
4075 theApp->sharedfiles->RemoveKeywords(this);
4078 CKnownFile::SetFileName(fileName);
4080 if (is_shared) {
4081 // And of course, we must advertise the new name if the file is shared.
4082 theApp->sharedfiles->AddKeywords(this);
4085 UpdateDisplayedInfo(true);
4089 uint16 CPartFile::GetMaxSources() const
4091 // This is just like this, while we don't import the private max sources per file
4092 return thePrefs::GetMaxSourcePerFile();
4096 uint16 CPartFile::GetMaxSourcePerFileSoft() const
4098 unsigned int temp = ((unsigned int)GetMaxSources() * 9L) / 10;
4099 if (temp > MAX_SOURCES_FILE_SOFT) {
4100 return MAX_SOURCES_FILE_SOFT;
4102 return temp;
4105 uint16 CPartFile::GetMaxSourcePerFileUDP() const
4107 unsigned int temp = ((unsigned int)GetMaxSources() * 3L) / 4;
4108 if (temp > MAX_SOURCES_FILE_UDP) {
4109 return MAX_SOURCES_FILE_UDP;
4111 return temp;
4114 #define DROP_FACTOR 2
4116 CUpDownClient* CPartFile::GetSlowerDownloadingClient(uint32 speed, CUpDownClient* caller) {
4117 // printf("Start slower source calculation\n");
4118 for( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ) {
4119 CUpDownClient* cur_src = *it++;
4120 if ((cur_src->GetDownloadState() == DS_DOWNLOADING) && (cur_src != caller)) {
4121 uint32 factored_bytes_per_second = static_cast<uint32>(
4122 (cur_src->GetKBpsDown() * 1024) * DROP_FACTOR);
4123 if ( factored_bytes_per_second< speed) {
4124 // printf("Selecting source %p to drop: %d < %d\n", cur_src, factored_bytes_per_second, speed);
4125 // printf("End slower source calculation\n");
4126 return cur_src;
4127 } else {
4128 // printf("Not selecting source %p to drop: %d > %d\n", cur_src, factored_bytes_per_second, speed);
4132 // printf("End slower source calculation\n");
4133 return NULL;
4136 #endif
4137 // File_checked_for_headers