Upstream tarball 20080928
[amule.git] / src / PartFile.cpp
blobe14da18a01be49d3ce7aef01ceae6f4c28840f1a
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-2008 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/CAllocateFileTask
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 #ifndef CLIENT_GUI
264 // use lowest free partfilenumber for free file (InterCeptor)
265 int i = 0;
266 do {
267 ++i;
268 m_partmetfilename = CPath(wxString::Format(wxT("%03i.part.met"), i));
269 m_fullname = thePrefs::GetTempDir().JoinPaths(m_partmetfilename);
270 } while (m_fullname.FileExists());
272 wxString strPartName = m_partmetfilename.RemoveExt().GetRaw();
273 m_taglist.push_back(CTagString(FT_PARTFILENAME, strPartName ));
275 Gap_Struct* gap = new Gap_Struct;
276 gap->start = 0;
277 gap->end = GetFileSize() - 1;
279 m_gaplist.push_back(gap);
281 CPath partPath = m_fullname.RemoveExt();
282 bool fileCreated;
283 if (thePrefs::GetAllocFullFile()) {
284 fileCreated = m_hpartfile.Create(partPath.GetRaw(), true);
285 m_hpartfile.Close();
286 } else {
287 fileCreated = PlatformSpecific::CreateSparseFile(partPath, GetFileSize());
289 if (!fileCreated) {
290 AddLogLineM(false,_("ERROR: Failed to create partfile)"));
291 SetPartFileStatus(PS_ERROR);
294 SetFilePath(thePrefs::GetTempDir());
296 if (thePrefs::GetAllocFullFile()) {
297 SetPartFileStatus(PS_ALLOCATING);
298 CThreadScheduler::AddTask(new CAllocateFileTask(this, thePrefs::AddNewFilesPaused()));
299 } else {
300 AllocationFinished();
303 m_hashsetneeded = (GetED2KPartHashCount() > 0);
305 SavePartFile(true);
306 SetActive(theApp->IsConnected());
307 #endif
311 uint8 CPartFile::LoadPartFile(const CPath& in_directory, const CPath& filename, bool from_backup, bool getsizeonly)
313 bool isnewstyle = false;
314 uint8 version,partmettype=PMT_UNKNOWN;
316 std::map<uint16, Gap_Struct*> gap_map; // Slugfiller
317 transferred = 0;
319 m_partmetfilename = filename;
320 m_filePath = in_directory;
321 m_fullname = m_filePath.JoinPaths(m_partmetfilename);
323 // readfile data form part.met file
324 CPath curMetFilename = m_fullname;
325 if (from_backup) {
326 curMetFilename = curMetFilename.AppendExt(PARTMET_BAK_EXT);
327 AddLogLineM(false, CFormat( _("Trying to load backup of met-file from %s") )
328 % curMetFilename );
331 try {
332 CFile metFile(curMetFilename, CFile::read);
333 if (!metFile.IsOpened()) {
334 AddLogLineM(false, CFormat( _("ERROR: Failed to open part.met file: %s ==> %s") )
335 % curMetFilename
336 % GetFileName() );
338 return false;
339 } else if (metFile.GetLength() == 0) {
340 AddLogLineM(false, CFormat( _("ERROR: part.met file is 0 size: %s ==> %s") )
341 % m_partmetfilename
342 % GetFileName() );
344 return false;
347 version = metFile.ReadUInt8();
348 if (version != PARTFILE_VERSION && version != PARTFILE_SPLITTEDVERSION && version != PARTFILE_VERSION_LARGEFILE){
349 metFile.Close();
350 //if (version == 83) return ImportShareazaTempFile(...)
351 AddLogLineM(false, CFormat( _("ERROR: Invalid part.met file version: %s ==> %s") )
352 % m_partmetfilename
353 % GetFileName() );
354 return false;
357 isnewstyle = (version == PARTFILE_SPLITTEDVERSION);
358 partmettype = isnewstyle ? PMT_SPLITTED : PMT_DEFAULTOLD;
360 if (!isnewstyle) {
361 uint8 test[4];
362 metFile.Seek(24, wxFromStart);
363 metFile.Read(test,4);
365 metFile.Seek(1, wxFromStart);
366 if (test[0]==0 && test[1]==0 && test[2]==2 && test[3]==1) {
367 isnewstyle=true; // edonkeys so called "old part style"
368 partmettype=PMT_NEWOLD;
372 if (isnewstyle) {
373 uint32 temp = metFile.ReadUInt32();
375 if (temp==0) { // 0.48 partmets - different again
376 LoadHashsetFromFile(&metFile, false);
377 } else {
378 metFile.Seek(2, wxFromStart);
379 LoadDateFromFile(&metFile);
380 m_abyFileHash = metFile.ReadHash();
383 } else {
384 LoadDateFromFile(&metFile);
385 LoadHashsetFromFile(&metFile, false);
388 uint32 tagcount = metFile.ReadUInt32();
390 for (uint32 j = 0; j < tagcount; ++j) {
391 CTag newtag(metFile,true);
392 if ( !getsizeonly ||
393 (getsizeonly &&
394 (newtag.GetNameID() == FT_FILESIZE ||
395 newtag.GetNameID() == FT_FILENAME))) {
396 switch(newtag.GetNameID()) {
397 case FT_FILENAME: {
398 if (!GetFileName().IsOk()) {
399 // If it's not empty, we already loaded the unicoded one
400 SetFileName(CPath(newtag.GetStr()));
402 break;
404 case FT_LASTSEENCOMPLETE: {
405 lastseencomplete = newtag.GetInt();
406 break;
408 case FT_FILESIZE: {
409 SetFileSize(newtag.GetInt());
410 break;
412 case FT_TRANSFERRED: {
413 transferred = newtag.GetInt();
414 break;
416 case FT_FILETYPE:{
417 //#warning needs setfiletype string
418 //SetFileType(newtag.GetStr());
419 break;
421 case FT_CATEGORY: {
422 m_category = newtag.GetInt();
423 if (m_category > theApp->glob_prefs->GetCatCount() - 1 ) {
424 m_category = 0;
426 break;
428 case FT_OLDDLPRIORITY:
429 case FT_DLPRIORITY: {
430 if (!isnewstyle){
431 m_iDownPriority = newtag.GetInt();
432 if( m_iDownPriority == PR_AUTO ){
433 m_iDownPriority = PR_HIGH;
434 SetAutoDownPriority(true);
436 else{
437 if ( m_iDownPriority != PR_LOW &&
438 m_iDownPriority != PR_NORMAL &&
439 m_iDownPriority != PR_HIGH)
440 m_iDownPriority = PR_NORMAL;
441 SetAutoDownPriority(false);
444 break;
446 case FT_STATUS: {
447 m_paused = (newtag.GetInt() == 1);
448 m_stopped = m_paused;
449 break;
451 case FT_OLDULPRIORITY:
452 case FT_ULPRIORITY: {
453 if (!isnewstyle){
454 SetUpPriority(newtag.GetInt(), false);
455 if( GetUpPriority() == PR_AUTO ){
456 SetUpPriority(PR_HIGH, false);
457 SetAutoUpPriority(true);
458 } else {
459 SetAutoUpPriority(false);
462 break;
464 case FT_KADLASTPUBLISHSRC:{
465 SetLastPublishTimeKadSrc(newtag.GetInt(), 0);
466 if(GetLastPublishTimeKadSrc() > (uint32)time(NULL)+KADEMLIAREPUBLISHTIMES) {
467 //There may be a posibility of an older client that saved a random number here.. This will check for that..
468 SetLastPublishTimeKadSrc(0,0);
470 break;
472 case FT_KADLASTPUBLISHNOTES:{
473 SetLastPublishTimeKadNotes(newtag.GetInt());
474 break;
476 // old tags: as long as they are not needed, take the chance to purge them
477 case FT_PERMISSIONS:
478 case FT_KADLASTPUBLISHKEY:
479 break;
480 case FT_DL_ACTIVE_TIME:
481 if (newtag.IsInt()) {
482 m_nDlActiveTime = newtag.GetInt();
484 break;
485 case FT_CORRUPTEDPARTS: {
486 wxASSERT(m_corrupted_list.empty());
487 wxString strCorruptedParts(newtag.GetStr());
488 wxStringTokenizer tokenizer(strCorruptedParts, wxT(","));
489 while ( tokenizer.HasMoreTokens() ) {
490 wxString token = tokenizer.GetNextToken();
491 unsigned long uPart;
492 if (token.ToULong(&uPart)) {
493 if (uPart < GetPartCount() && !IsCorruptedPart(uPart)) {
494 m_corrupted_list.push_back(uPart);
498 break;
500 case FT_AICH_HASH:{
501 CAICHHash hash;
502 bool hashSizeOk =
503 hash.DecodeBase32(newtag.GetStr()) == CAICHHash::GetHashSize();
504 wxASSERT(hashSizeOk);
505 if (hashSizeOk) {
506 m_pAICHHashSet->SetMasterHash(hash, AICH_VERIFIED);
508 break;
510 case FT_ATTRANSFERRED:{
511 statistic.SetAllTimeTransferred(statistic.GetAllTimeTransferred() + (uint64)newtag.GetInt());
512 break;
514 case FT_ATTRANSFERREDHI:{
515 statistic.SetAllTimeTransferred(statistic.GetAllTimeTransferred() + (((uint64)newtag.GetInt()) << 32));
516 break;
518 case FT_ATREQUESTED:{
519 statistic.SetAllTimeRequests(newtag.GetInt());
520 break;
522 case FT_ATACCEPTED:{
523 statistic.SetAllTimeAccepts(newtag.GetInt());
524 break;
526 default: {
527 // Start Changes by Slugfiller for better exception handling
529 wxCharBuffer tag_ansi_name = newtag.GetName().ToAscii();
530 char gap_mark = tag_ansi_name ? tag_ansi_name[0u] : 0;
531 if ( newtag.IsInt() && (newtag.GetName().Length() > 1) &&
532 ((gap_mark == FT_GAPSTART) ||
533 (gap_mark == FT_GAPEND))) {
534 Gap_Struct *gap = NULL;
535 unsigned long int gapkey;
536 if (newtag.GetName().Mid(1).ToULong(&gapkey)) {
537 if ( gap_map.find( gapkey ) == gap_map.end() ) {
538 gap = new Gap_Struct;
539 gap_map[gapkey] = gap;
540 gap->start = (uint64)-1;
541 gap->end = (uint64)-1;
542 } else {
543 gap = gap_map[ gapkey ];
545 if (gap_mark == FT_GAPSTART) {
546 gap->start = newtag.GetInt();
548 if (gap_mark == FT_GAPEND) {
549 gap->end = newtag.GetInt()-1;
551 } else {
552 printf("Wrong gap map key while reading met file!\n");
553 wxASSERT(0);
555 // End Changes by Slugfiller for better exception handling
556 } else {
557 m_taglist.push_back(newtag);
561 } else {
562 // Nothing. Else, nothing.
566 // load the hashsets from the hybridstylepartmet
567 if (isnewstyle && !getsizeonly && (metFile.GetPosition()<metFile.GetLength()) ) {
568 metFile.Seek(1, wxFromCurrent);
570 uint16 parts=GetPartCount(); // assuming we will get all hashsets
572 for (uint16 i = 0; i < parts && (metFile.GetPosition()+16<metFile.GetLength()); ++i){
573 CMD4Hash cur_hash = metFile.ReadHash();
574 m_hashlist.push_back(cur_hash);
577 CMD4Hash checkhash;
578 if (!m_hashlist.empty()) {
579 CreateHashFromHashlist(m_hashlist, &checkhash);
581 bool flag=false;
582 if (m_abyFileHash == checkhash) {
583 flag=true;
584 } else {
585 m_hashlist.clear();
586 flag=false;
589 } catch (const CInvalidPacket& e) {
590 AddLogLineM(true, CFormat(wxT("Error: %s (%s) is corrupt (bad tags: %s), unable to load file."))
591 % m_partmetfilename
592 % GetFileName()
593 % e.what());
594 return false;
595 } catch (const CIOFailureException& e) {
596 AddDebugLogLineM(true, logPartFile, CFormat( wxT("IO failure while loading '%s': %s") )
597 % m_partmetfilename
598 % e.what() );
599 return false;
600 } catch (const CEOFException& WXUNUSED(e)) {
601 AddLogLineM(true, CFormat( _("ERROR: %s (%s) is corrupt (wrong tagcount), unable to load file.") )
602 % m_partmetfilename
603 % GetFileName() );
604 AddLogLineM(true, _("Trying to recover file info..."));
606 // Safe file is that who have
607 // - FileSize
608 if (GetFileSize()) {
609 // We have filesize, try other needed info
611 // Do we need to check gaps? I think not,
612 // because they are checked below. Worst
613 // scenario will only mark file as 0 bytes downloaded.
615 // -Filename
616 if (!GetFileName().IsOk()) {
617 // Not critical, let's put a random filename.
618 AddLogLineM(true, _(
619 "Recovering no-named file - will try to recover it as RecoveredFile.dat"));
620 SetFileName(CPath(wxT("RecoveredFile.dat")));
623 AddLogLineM(true,
624 _("Recovered all available file info :D - Trying to use it..."));
625 } else {
626 AddLogLineM(true, _("Unable to recover file info :("));
627 return false;
631 if (getsizeonly) {
632 return partmettype;
634 // Now to flush the map into the list (Slugfiller)
635 std::map<uint16, Gap_Struct*>::iterator it = gap_map.begin();
636 for ( ; it != gap_map.end(); ++it ) {
637 Gap_Struct* gap = it->second;
638 // SLUGFILLER: SafeHash - revised code, and extra safety
639 if ( (gap->start != (uint64)-1) &&
640 (gap->end != (uint64)-1) &&
641 gap->start <= gap->end &&
642 gap->start < GetFileSize()) {
643 if (gap->end >= GetFileSize()) {
644 gap->end = GetFileSize()-1; // Clipping
646 AddGap(gap->start, gap->end); // All tags accounted for, use safe adding
648 delete gap;
649 // SLUGFILLER: SafeHash
652 //check if this is a backup
653 if ( m_fullname.GetExt().MakeLower() == wxT("backup" )) {
654 m_fullname = m_fullname.RemoveExt();
657 // open permanent handle
658 CPath partFilePath = m_fullname.RemoveExt();
659 if ( !m_hpartfile.Open(partFilePath, CFile::read_write)) {
660 AddLogLineM(false, CFormat( _("Failed to open %s (%s)") )
661 % m_fullname
662 % GetFileName() );
663 return false;
666 SetPartFileStatus(PS_EMPTY);
668 try {
669 // SLUGFILLER: SafeHash - final safety, make sure any missing part of the file is gap
670 if (m_hpartfile.GetLength() < GetFileSize())
671 AddGap(m_hpartfile.GetLength(), GetFileSize()-1);
672 // Goes both ways - Partfile should never be too large
673 if (m_hpartfile.GetLength() > GetFileSize()) {
674 AddDebugLogLineM( true, logPartFile, CFormat( wxT("Partfile \"%s\" is too large! Truncating %llu bytes.") ) % GetFileName() % (m_hpartfile.GetLength() - GetFileSize()));
675 m_hpartfile.SetLength(GetFileSize());
677 // SLUGFILLER: SafeHash
678 } catch (const CIOFailureException& e) {
679 AddDebugLogLineM( true, logPartFile, CFormat( wxT("Error while accessing partfile \"%s\": %s") ) % GetFileName() % e.what());
680 SetPartFileStatus(PS_ERROR);
684 // check hashcount, file status etc
685 if (GetHashCount() != GetED2KPartHashCount()){
686 m_hashsetneeded = true;
687 return true;
688 } else {
689 m_hashsetneeded = false;
690 for (size_t i = 0; i < m_hashlist.size(); ++i) {
691 if (IsComplete(i*PARTSIZE,((i+1)*PARTSIZE)-1)) {
692 SetPartFileStatus(PS_READY);
697 if (m_gaplist.empty()) { // is this file complete already?
698 CompleteFile(false);
699 return true;
702 if (!isnewstyle) { // not for importing
703 const time_t file_date = CPath::GetModificationTime(partFilePath);
704 if (m_lastDateChanged != file_date) {
705 // It's pointless to rehash an empty file, since the case
706 // where a user has zero'd a file is handled above ...
707 if (m_hpartfile.GetLength()) {
708 AddLogLineM(false, CFormat( _("WARNING: %s might be corrupted (%i)") )
709 % partFilePath
710 % (m_lastDateChanged - file_date) );
711 // rehash
712 SetPartFileStatus(PS_WAITINGFORHASH);
714 CPath partFileName = m_partmetfilename.RemoveExt();
715 CThreadScheduler::AddTask(new CHashingTask(m_filePath, partFileName, this));
720 UpdateCompletedInfos();
721 if (completedsize > transferred) {
722 m_iGainDueToCompression = completedsize - transferred;
723 } else if (completedsize != transferred) {
724 m_iLostDueToCorruption = transferred - completedsize;
727 return true;
731 bool CPartFile::SavePartFile(bool Initial)
733 switch (status) {
734 case PS_WAITINGFORHASH:
735 case PS_HASHING:
736 case PS_COMPLETE:
737 return false;
740 /* Don't write anything to disk if less than 100 KB of free space is left. */
741 sint64 free = CPath::GetFreeSpaceAt(GetFilePath());
742 if ((free != wxInvalidOffset) && (free < (100 * 1024))) {
743 return false;
746 CFile file;
747 try {
748 if (!m_fullname.RemoveExt().FileExists()) {
749 throw wxString(wxT(".part file not found"));
752 uint32 lsc = lastseencomplete;
754 if (!Initial) {
755 CPath::BackupFile(m_fullname, wxT(".backup"));
756 CPath::RemoveFile(m_fullname);
759 file.Open(m_fullname, CFile::write);
760 if (!file.IsOpened()) {
761 throw wxString(wxT("Failed to open part.met file"));
764 // version
765 file.WriteUInt8(IsLargeFile() ? PARTFILE_VERSION_LARGEFILE : PARTFILE_VERSION);
767 file.WriteUInt32(CPath::GetModificationTime(m_fullname.RemoveExt()));
768 // hash
769 file.WriteHash(m_abyFileHash);
770 uint16 parts = m_hashlist.size();
771 file.WriteUInt16(parts);
772 for (int x = 0; x < parts; ++x) {
773 file.WriteHash(m_hashlist[x]);
775 // tags
776 #define FIXED_TAGS 15
777 uint32 tagcount = m_taglist.size() + FIXED_TAGS + (m_gaplist.size()*2);
778 if (!m_corrupted_list.empty()) {
779 ++tagcount;
782 if (m_pAICHHashSet->HasValidMasterHash() && (m_pAICHHashSet->GetStatus() == AICH_VERIFIED)){
783 ++tagcount;
786 if (GetLastPublishTimeKadSrc()){
787 ++tagcount;
790 if (GetLastPublishTimeKadNotes()){
791 ++tagcount;
794 if (GetDlActiveTime()){
795 ++tagcount;
798 file.WriteUInt32(tagcount);
800 //#warning Kry - Where are lost by coruption and gained by compression?
802 // 0 (unicoded part file name)
803 // We write it with BOM to keep eMule compatibility. Note that the 'printable' filename is saved,
804 // as presently the filename does not represent an actual file.
805 CTagString( FT_FILENAME, GetFileName().GetPrintable()).WriteTagToFile( &file, utf8strOptBOM );
806 CTagString( FT_FILENAME, GetFileName().GetPrintable()).WriteTagToFile( &file ); // 1
808 CTagIntSized( FT_FILESIZE, GetFileSize(), IsLargeFile() ? 64 : 32).WriteTagToFile( &file );// 2
809 CTagIntSized( FT_TRANSFERRED, transferred, IsLargeFile() ? 64 : 32).WriteTagToFile( &file ); // 3
810 CTagInt32( FT_STATUS, (m_paused?1:0)).WriteTagToFile( &file ); // 4
812 if ( IsAutoDownPriority() ) {
813 CTagInt32( FT_DLPRIORITY, (uint8)PR_AUTO ).WriteTagToFile( &file ); // 5
814 CTagInt32( FT_OLDDLPRIORITY, (uint8)PR_AUTO ).WriteTagToFile( &file ); // 6
815 } else {
816 CTagInt32( FT_DLPRIORITY, m_iDownPriority ).WriteTagToFile( &file ); // 5
817 CTagInt32( FT_OLDDLPRIORITY, m_iDownPriority ).WriteTagToFile( &file ); // 6
820 CTagInt32( FT_LASTSEENCOMPLETE, lsc ).WriteTagToFile( &file ); // 7
822 if ( IsAutoUpPriority() ) {
823 CTagInt32( FT_ULPRIORITY, (uint8)PR_AUTO ).WriteTagToFile( &file ); // 8
824 CTagInt32( FT_OLDULPRIORITY, (uint8)PR_AUTO ).WriteTagToFile( &file ); // 9
825 } else {
826 CTagInt32( FT_ULPRIORITY, GetUpPriority() ).WriteTagToFile( &file ); // 8
827 CTagInt32( FT_OLDULPRIORITY, GetUpPriority() ).WriteTagToFile( &file ); // 9
830 CTagInt32(FT_CATEGORY, m_category).WriteTagToFile( &file ); // 10
831 CTagInt32(FT_ATTRANSFERRED, statistic.GetAllTimeTransferred() & 0xFFFFFFFF).WriteTagToFile( &file );// 11
832 CTagInt32(FT_ATTRANSFERREDHI, statistic.GetAllTimeTransferred() >>32).WriteTagToFile( &file );// 12
833 CTagInt32(FT_ATREQUESTED, statistic.GetAllTimeRequests()).WriteTagToFile( &file ); // 13
834 CTagInt32(FT_ATACCEPTED, statistic.GetAllTimeAccepts()).WriteTagToFile( &file ); // 14
836 // currupt part infos
837 if (!m_corrupted_list.empty()) {
838 wxString strCorruptedParts;
839 std::list<uint16>::iterator it = m_corrupted_list.begin();
840 for (; it != m_corrupted_list.end(); ++it) {
841 uint16 uCorruptedPart = *it;
842 if (!strCorruptedParts.IsEmpty()) {
843 strCorruptedParts += wxT(",");
845 strCorruptedParts += wxString::Format(wxT("%u"), (unsigned)uCorruptedPart);
847 wxASSERT( !strCorruptedParts.IsEmpty() );
849 CTagString( FT_CORRUPTEDPARTS, strCorruptedParts ).WriteTagToFile( &file); // 11?
852 //AICH Filehash
853 if (m_pAICHHashSet->HasValidMasterHash() && (m_pAICHHashSet->GetStatus() == AICH_VERIFIED)){
854 CTagString aichtag(FT_AICH_HASH, m_pAICHHashSet->GetMasterHash().GetString() );
855 aichtag.WriteTagToFile(&file); // 12?
858 if (GetLastPublishTimeKadSrc()){
859 CTagInt32(FT_KADLASTPUBLISHSRC, GetLastPublishTimeKadSrc()).WriteTagToFile(&file); // 15?
862 if (GetLastPublishTimeKadNotes()){
863 CTagInt32(FT_KADLASTPUBLISHNOTES, GetLastPublishTimeKadNotes()).WriteTagToFile(&file); // 16?
866 if (GetDlActiveTime()){
867 CTagInt32(FT_DL_ACTIVE_TIME, GetDlActiveTime()).WriteTagToFile(&file); // 17
870 for (uint32 j = 0; j < (uint32)m_taglist.size();++j) {
871 m_taglist[j].WriteTagToFile(&file);
874 // gaps
875 unsigned i_pos = 0;
876 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
877 for (; it != m_gaplist.end(); ++it) {
878 wxString tagName = wxString::Format(wxT(" %u"), i_pos);
880 // gap start = first missing byte but gap ends = first non-missing byte
881 // in edonkey but I think its easier to user the real limits
882 tagName[0] = FT_GAPSTART;
883 CTagIntSized(tagName, (*it)->start , IsLargeFile() ? 64 : 32).WriteTagToFile( &file );
885 tagName[0] = FT_GAPEND;
886 CTagIntSized(tagName, ((*it)->end + 1), IsLargeFile() ? 64 : 32).WriteTagToFile( &file );
888 ++i_pos;
890 } catch (const wxString& error) {
891 AddLogLineM(false, CFormat( _("ERROR while saving partfile: %s (%s ==> %s)") )
892 % error
893 % m_partmetfilename
894 % GetFileName() );
896 wxString err = CFormat( _("ERROR while saving partfile: %s (%s ==> %s)") )
897 % error
898 % m_partmetfilename
899 % GetFileName();
901 printf("%s\n", (const char*)unicode2char(err));
903 return false;
904 } catch (const CIOFailureException& e) {
905 AddDebugLogLineM(true, logPartFile, wxT("IO failure while saving partfile: ") + e.what());
906 printf("IO failure while saving partfile: %s\n", (const char*)unicode2char(e.what()));
908 return false;
911 file.Close();
913 if (!Initial) {
914 CPath::RemoveFile(m_fullname.AppendExt(wxT(".backup")));
917 sint64 metLength = m_fullname.GetFileSize();
918 if (metLength == wxInvalidOffset) {
919 theApp->ShowAlert( CFormat( _("Could not retrieve length of '%s' - using %s file.") )
920 % m_fullname
921 % PARTMET_BAK_EXT,
922 _("Message"), wxOK);
924 CPath::CloneFile(m_fullname.AppendExt(PARTMET_BAK_EXT), m_fullname, true);
925 } else if (metLength == 0) {
926 // Don't backup if it's 0 size but raise a warning!!!
927 theApp->ShowAlert( CFormat( _("'%s' is 0 size somehow - using %s file.") )
928 % m_fullname
929 % PARTMET_BAK_EXT,
930 _("Message"), wxOK);
932 CPath::CloneFile(m_fullname.AppendExt(PARTMET_BAK_EXT), m_fullname, true);
933 } else {
934 // no error, just backup
935 CPath::BackupFile(m_fullname, PARTMET_BAK_EXT);
938 return true;
942 void CPartFile::SaveSourceSeeds()
944 #define MAX_SAVED_SOURCES 10
946 // Kry - Sources seeds
947 // Based on a Feature request, this saves the last MAX_SAVED_SOURCES
948 // sources of the file, giving a 'seed' for the next run.
949 // We save the last sources because:
950 // 1 - They could be the hardest to get
951 // 2 - They will more probably be available
952 // However, if we have downloading sources, they have preference because
953 // we probably have more credits on them.
954 // Anyway, source exchange will get us the rest of the sources
955 // This feature is currently used only on rare files (< 20 sources)
958 if (GetSourceCount()>20) {
959 return;
962 CClientPtrList source_seeds;
963 int n_sources = 0;
965 CClientPtrList::iterator it = m_downloadingSourcesList.begin();
966 for( ; it != m_downloadingSourcesList.end() && n_sources < MAX_SAVED_SOURCES; ++it) {
967 CUpDownClient *cur_src = *it;
968 if (!cur_src->HasLowID()) {
969 source_seeds.push_back(cur_src);
970 ++n_sources;
974 if (n_sources < MAX_SAVED_SOURCES) {
975 // Not enough downloading sources to fill the list, going to sources list
976 if (GetSourceCount() > 0) {
977 SourceSet::reverse_iterator rit = m_SrcList.rbegin();
978 for ( ; ((rit != m_SrcList.rend()) && (n_sources<MAX_SAVED_SOURCES)); ++rit) {
979 CUpDownClient* cur_src = *rit;
980 if (!cur_src->HasLowID()) {
981 source_seeds.push_back(cur_src);
982 ++n_sources;
988 // Write the file
989 if (!n_sources) {
990 return;
993 const CPath seedsPath = m_fullname.AppendExt(wxT(".seeds"));
995 CFile file;
996 file.Create(seedsPath, true);
997 if (!file.IsOpened()) {
998 AddLogLineM(false, CFormat( _("Failed to save part.met.seeds file for %s") )
999 % m_fullname);
1000 return;
1003 try {
1004 file.WriteUInt8(0); // v3, to avoid v2 clients choking on it.
1005 file.WriteUInt8(source_seeds.size());
1007 CClientPtrList::iterator it2 = source_seeds.begin();
1008 for (; it2 != source_seeds.end(); ++it2) {
1009 CUpDownClient* cur_src = *it2;
1010 file.WriteUInt32(cur_src->GetUserIDHybrid());
1011 file.WriteUInt16(cur_src->GetUserPort());
1012 file.WriteHash(cur_src->GetUserHash());
1013 // CryptSettings - See SourceExchange V4
1014 const uint8 uSupportsCryptLayer = cur_src->SupportsCryptLayer() ? 1 : 0;
1015 const uint8 uRequestsCryptLayer = cur_src->RequestsCryptLayer() ? 1 : 0;
1016 const uint8 uRequiresCryptLayer = cur_src->RequiresCryptLayer() ? 1 : 0;
1017 const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0);
1018 file.WriteUInt8(byCryptOptions);
1021 /* v2: Added to keep track of too old seeds */
1022 file.WriteUInt32(wxDateTime::Now().GetTicks());
1024 AddLogLineM(false, CFormat( wxPLURAL("Saved %i source seed for partfile: %s (%s)", "Saved %i source seeds for partfile: %s (%s)", n_sources) )
1025 % n_sources
1026 % m_fullname
1027 % GetFileName());
1028 } catch (const CIOFailureException& e) {
1029 AddDebugLogLineM(true, logPartFile, CFormat( wxT("Error saving partfile's seeds file (%s - %s): %s") )
1030 % m_partmetfilename
1031 % GetFileName()
1032 % e.what() );
1034 n_sources = 0;
1035 file.Close();
1036 CPath::RemoveFile(seedsPath);
1040 void CPartFile::LoadSourceSeeds()
1042 CMemFile sources_data;
1044 bool valid_sources = false;
1046 const CPath seedsPath = m_fullname.AppendExt(wxT(".seeds"));
1047 if (!seedsPath.FileExists()) {
1048 return;
1051 CFile file(seedsPath, CFile::read);
1052 if (!file.IsOpened()) {
1053 AddLogLineM(false, CFormat( _("Partfile %s (%s) has no seeds file") )
1054 % m_partmetfilename
1055 % GetFileName() );
1056 return;
1060 try {
1061 if (file.GetLength() <= 1) {
1062 AddLogLineM(false, CFormat( _("Partfile %s (%s) has a void seeds file") )
1063 % m_partmetfilename
1064 % GetFileName() );
1065 return;
1068 uint8 src_count = file.ReadUInt8();
1070 bool bUseSX2Format = (src_count == 0);
1072 if (bUseSX2Format) {
1073 // v3 sources seeds
1074 src_count = file.ReadUInt8();
1077 sources_data.WriteUInt16(src_count);
1079 for (int i = 0; i< src_count; ++i) {
1080 uint32 dwID = file.ReadUInt32();
1081 uint16 nPort = file.ReadUInt16();
1083 sources_data.WriteUInt32(bUseSX2Format ? dwID : wxUINT32_SWAP_ALWAYS(dwID));
1084 sources_data.WriteUInt16(nPort);
1085 sources_data.WriteUInt32(0);
1086 sources_data.WriteUInt16(0);
1088 if (bUseSX2Format) {
1089 sources_data.WriteHash(file.ReadHash());
1090 sources_data.WriteUInt8(file.ReadUInt8());
1095 if (!file.Eof()) {
1097 // v2: Added to keep track of too old seeds
1098 time_t time = (time_t)file.ReadUInt32();
1100 // Time frame is 2 hours. More than enough to compile
1101 // your new aMule version!.
1102 if ((time + MIN2S(120)) >= wxDateTime::Now().GetTicks()) {
1103 valid_sources = true;
1106 } else {
1107 // v1 has no time data. We can safely use
1108 // the sources, next time will be saved.
1109 valid_sources = true;
1112 if (valid_sources) {
1113 sources_data.Seek(0);
1114 AddClientSources(&sources_data, SF_SOURCE_SEEDS, bUseSX2Format ? 4 : 1, bUseSX2Format);
1117 } catch (const CSafeIOException& e) {
1118 AddLogLineM(false, CFormat( _("Error reading partfile's seeds file (%s - %s): %s") )
1119 % m_partmetfilename
1120 % GetFileName()
1121 % e.what() );
1124 file.Close();
1127 void CPartFile::PartFileHashFinished(CKnownFile* result)
1129 m_lastDateChanged = result->m_lastDateChanged;
1130 bool errorfound = false;
1131 if (GetED2KPartHashCount() == 0){
1132 if (IsComplete(0, GetFileSize()-1)){
1133 if (result->GetFileHash() != GetFileHash()){
1134 AddLogLineM(false,
1135 CFormat(wxPLURAL(
1136 "Found corrupted part (%d) in %d part file %s - FileResultHash |%s| FileHash |%s|",
1137 "Found corrupted part (%d) in %d parts file %s - FileResultHash |%s| FileHash |%s|",
1142 % GetFileName()
1143 % result->GetFileHash().Encode()
1144 % GetFileHash().Encode() );
1145 AddGap(0, GetFileSize()-1);
1146 errorfound = true;
1150 else{
1151 for (size_t i = 0; i < m_hashlist.size(); ++i){
1152 // Kry - trel_ar's completed parts check on rehashing.
1153 // Very nice feature, if a file is completed but .part.met don't believe it,
1154 // update it.
1156 if (!( i < result->GetHashCount() && (result->GetPartHash(i) == GetPartHash(i)))){
1157 if (IsComplete(i*PARTSIZE,((i+1)*PARTSIZE)-1)) {
1158 CMD4Hash wronghash;
1159 if ( i < result->GetHashCount() )
1160 wronghash = result->GetPartHash(i);
1162 AddLogLineM(false,
1163 CFormat(wxPLURAL(
1164 "Found corrupted part (%d) in %d part file %s - FileResultHash |%s| FileHash |%s|",
1165 "Found corrupted part (%d) in %d parts file %s - FileResultHash |%s| FileHash |%s|",
1166 GetED2KPartHashCount())
1168 % ( i + 1 )
1169 % GetED2KPartHashCount()
1170 % GetFileName()
1171 % wronghash.Encode()
1172 % GetPartHash(i).Encode() );
1174 AddGap(i*PARTSIZE,
1175 ((uint64)(((i+1)*PARTSIZE)-1) >= GetFileSize()) ?
1176 GetFileSize()-1 : ((i+1)*PARTSIZE)-1);
1177 errorfound = true;
1179 } else {
1180 if (!IsComplete(i*PARTSIZE,((i+1)*PARTSIZE)-1)){
1181 AddLogLineM(false, CFormat( _("Found completed part (%i) in %s") )
1182 % ( i + 1 )
1183 % GetFileName() );
1185 FillGap(i*PARTSIZE,
1186 ((uint64)(((i+1)*PARTSIZE)-1) >= GetFileSize()) ?
1187 GetFileSize()-1 : ((i+1)*PARTSIZE)-1);
1188 RemoveBlockFromList(i*PARTSIZE,
1189 ((uint64)(((i+1)*PARTSIZE)-1) >= GetFileSize()) ?
1190 GetFileSize()-1 : ((i+1)*PARTSIZE)-1);
1196 if ( !errorfound &&
1197 result->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE &&
1198 status == PS_COMPLETING) {
1199 delete m_pAICHHashSet;
1200 m_pAICHHashSet = result->GetAICHHashset();
1201 result->SetAICHHashset(NULL);
1202 m_pAICHHashSet->SetOwner(this);
1204 else if (status == PS_COMPLETING) {
1205 AddDebugLogLineM(false, logPartFile,
1206 CFormat(wxT("Failed to store new AICH Hashset for completed file: %s"))
1207 % GetFileName());
1211 delete result;
1212 if (!errorfound){
1213 if (status == PS_COMPLETING){
1214 CompleteFile(true);
1215 return;
1217 else {
1218 AddLogLineM(false, CFormat( _("Finished rehashing %s") ) % GetFileName());
1221 else{
1222 SetStatus(PS_READY);
1223 SavePartFile();
1224 return;
1226 SetStatus(PS_READY);
1227 SavePartFile();
1228 theApp->sharedfiles->SafeAddKFile(this);
1231 void CPartFile::AddGap(uint64 start, uint64 end)
1233 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
1234 while (it != m_gaplist.end()) {
1235 std::list<Gap_Struct*>::iterator it2 = it++;
1236 Gap_Struct* cur_gap = *it2;
1238 if (cur_gap->start >= start && cur_gap->end <= end) {
1239 // this gap is inside the new gap - delete
1240 m_gaplist.erase(it2);
1241 delete cur_gap;
1242 continue;
1243 } else if (cur_gap->start >= start && cur_gap->start <= end + 1) {
1244 // head of this gap is in the new gap, or this gap is
1245 // directly behind the new gap - extend limit and delete
1246 end = cur_gap->end;
1247 m_gaplist.erase(it2);
1248 delete cur_gap;
1249 continue;
1250 } else if (cur_gap->end <= end && cur_gap->end >= start - 1) {
1251 // tail of this gap is in the new gap, or this gap is
1252 // directly before the new gap - extend limit and delete
1253 start = cur_gap->start;
1254 m_gaplist.erase(it2);
1255 delete cur_gap;
1256 continue;
1257 } else if (start >= cur_gap->start && end <= cur_gap->end){
1258 // new gap is already inside this gap - return
1259 return;
1260 // now all cases of overlap are ruled out
1261 } else if (cur_gap->start > start) {
1262 // this gap is the first behind the new gap -> insert before it
1263 it = it2;
1264 break;
1268 Gap_Struct* new_gap = new Gap_Struct;
1269 new_gap->start = start;
1270 new_gap->end = end;
1271 m_gaplist.insert(it, new_gap);
1272 UpdateDisplayedInfo();
1275 bool CPartFile::IsAlreadyRequested(uint64 start, uint64 end)
1277 std::list<Requested_Block_Struct*>::iterator it = m_requestedblocks_list.begin();
1278 for (; it != m_requestedblocks_list.end(); ++it) {
1279 Requested_Block_Struct* cur_block = *it;
1281 if ((start <= cur_block->EndOffset) && (end >= cur_block->StartOffset)) {
1282 return true;
1285 return false;
1288 bool CPartFile::GetNextEmptyBlockInPart(uint16 partNumber, Requested_Block_Struct *result)
1290 Gap_Struct *firstGap;
1291 Gap_Struct *currentGap;
1292 uint64 end;
1293 uint64 blockLimit;
1295 // Find start of this part
1296 uint64 partStart = (PARTSIZE * partNumber);
1297 uint64 start = partStart;
1299 // What is the end limit of this block, i.e. can't go outside part (or filesize)
1300 uint64 partEnd = (PARTSIZE * (partNumber + 1)) - 1;
1301 if (partEnd >= GetFileSize()) {
1302 partEnd = GetFileSize() - 1;
1304 // Loop until find a suitable gap and return true, or no more gaps and return false
1305 while (true) {
1306 firstGap = NULL;
1308 // Find the first gap from the start position
1309 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
1310 for (; it != m_gaplist.end(); ++it) {
1311 currentGap = *it;
1313 // Want gaps that overlap start<->partEnd
1314 if ((currentGap->start <= partEnd) && (currentGap->end >= start)) {
1315 // Is this the first gap?
1316 if ((firstGap == NULL) || (currentGap->start < firstGap->start)) {
1317 firstGap = currentGap;
1322 // If no gaps after start, exit
1323 if (firstGap == NULL) {
1324 return false;
1326 // Update start position if gap starts after current pos
1327 if (start < firstGap->start) {
1328 start = firstGap->start;
1330 // If this is not within part, exit
1331 if (start > partEnd) {
1332 return false;
1334 // Find end, keeping within the max block size and the part limit
1335 end = firstGap->end;
1336 blockLimit = partStart + (BLOCKSIZE * (((start - partStart) / BLOCKSIZE) + 1)) - 1;
1337 if (end > blockLimit) {
1338 end = blockLimit;
1340 if (end > partEnd) {
1341 end = partEnd;
1343 // If this gap has not already been requested, we have found a valid entry
1344 if (!IsAlreadyRequested(start, end)) {
1345 // Was this block to be returned
1346 if (result != NULL) {
1347 result->StartOffset = start;
1348 result->EndOffset = end;
1349 md4cpy(result->FileID, GetFileHash().GetHash());
1350 result->transferred = 0;
1352 return true;
1353 } else {
1354 // Reposition to end of that gap
1355 start = end + 1;
1357 // If tried all gaps then break out of the loop
1358 if (end == partEnd) {
1359 break;
1362 // No suitable gap found
1363 return false;
1367 void CPartFile::FillGap(uint64 start, uint64 end)
1369 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
1370 while (it != m_gaplist.end()) {
1371 std::list<Gap_Struct*>::iterator it2 = it++;
1372 Gap_Struct* cur_gap = *it2;
1374 if (cur_gap->start >= start && cur_gap->end <= end) {
1375 // our part fills this gap completly
1376 m_gaplist.erase(it2);
1377 delete cur_gap;
1378 continue;
1379 } else if (cur_gap->start >= start && cur_gap->start <= end) {
1380 // a part of this gap is in the part - set limit
1381 cur_gap->start = end+1;
1382 } else if (cur_gap->end <= end && cur_gap->end >= start) {
1383 // a part of this gap is in the part - set limit
1384 cur_gap->end = start-1;
1385 } else if (start >= cur_gap->start && end <= cur_gap->end) {
1386 uint64 buffer = cur_gap->end;
1387 cur_gap->end = start-1;
1388 cur_gap = new Gap_Struct;
1389 cur_gap->start = end+1;
1390 cur_gap->end = buffer;
1391 m_gaplist.insert(++it2, cur_gap);
1392 break;
1395 UpdateCompletedInfos();
1396 UpdateDisplayedInfo();
1400 void CPartFile::UpdateCompletedInfos()
1402 uint64 allgaps = 0;
1404 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
1405 for (; it != m_gaplist.end(); ) {
1406 std::list<Gap_Struct*>::iterator it2 = it++;
1407 Gap_Struct* cur_gap = *it2;
1409 if ((cur_gap->end > GetFileSize()) || (cur_gap->start >= GetFileSize())) {
1410 m_gaplist.erase(it2);
1411 } else {
1412 allgaps += cur_gap->end - cur_gap->start + 1;
1416 if ((!m_gaplist.empty()) || (!m_requestedblocks_list.empty())) {
1417 percentcompleted = (1.0f-(double)allgaps/GetFileSize()) * 100;
1418 completedsize = GetFileSize() - allgaps;
1419 } else {
1420 percentcompleted = 100;
1421 completedsize = GetFileSize();
1426 void CPartFile::WritePartStatus(CMemFile* file)
1428 uint16 parts = GetED2KPartCount();
1429 file->WriteUInt16(parts);
1430 uint16 done = 0;
1431 while (done != parts){
1432 uint8 towrite = 0;
1433 for (uint32 i = 0;i != 8;++i) {
1434 if (IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1)) {
1435 towrite |= (1<<i);
1437 ++done;
1438 if (done == parts) {
1439 break;
1442 file->WriteUInt8(towrite);
1446 void CPartFile::WriteCompleteSourcesCount(CMemFile* file)
1448 file->WriteUInt16(m_nCompleteSourcesCount);
1451 uint32 CPartFile::Process(uint32 reducedownload/*in percent*/,uint8 m_icounter)
1453 uint16 old_trans;
1454 uint32 dwCurTick = ::GetTickCount();
1456 // If buffer size exceeds limit, or if not written within time limit, flush data
1457 if ( (m_nTotalBufferData > thePrefs::GetFileBufferSize()) ||
1458 (dwCurTick > (m_nLastBufferFlushTime + BUFFER_TIME_LIMIT))) {
1459 // Avoid flushing while copying preview file
1460 if (!m_bPreviewing) {
1461 FlushBuffer();
1466 // check if we want new sources from server --> MOVED for 16.40 version
1467 old_trans=transferingsrc;
1468 transferingsrc = 0;
1469 kBpsDown = 0.0;
1471 if (m_icounter < 10) {
1472 // Update only downloading sources.
1473 CClientPtrList::iterator it = m_downloadingSourcesList.begin();
1474 for( ; it != m_downloadingSourcesList.end(); ) {
1475 CUpDownClient *cur_src = *it++;
1476 if(cur_src->GetDownloadState() == DS_DOWNLOADING) {
1477 ++transferingsrc;
1478 kBpsDown += cur_src->SetDownloadLimit(reducedownload);
1481 } else {
1482 // Update all sources (including downloading sources)
1483 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ) {
1484 CUpDownClient* cur_src = *it++;
1485 switch (cur_src->GetDownloadState()) {
1486 case DS_DOWNLOADING: {
1487 ++transferingsrc;
1488 kBpsDown += cur_src->SetDownloadLimit(reducedownload);
1489 break;
1491 case DS_BANNED: {
1492 break;
1494 case DS_ERROR: {
1495 break;
1497 case DS_LOWTOLOWIP: {
1498 if ( cur_src->HasLowID() && !theApp->DoCallback( cur_src ) ) {
1499 // If we are almost maxed on sources,
1500 // slowly remove these client to see
1501 // if we can find a better source.
1502 if( ((dwCurTick - lastpurgetime) > 30000) &&
1503 (GetSourceCount() >= (thePrefs::GetMaxSourcePerFile()*.8))) {
1504 RemoveSource( cur_src );
1505 lastpurgetime = dwCurTick;
1506 break;
1508 } else {
1509 cur_src->SetDownloadState(DS_ONQUEUE);
1512 break;
1514 case DS_NONEEDEDPARTS: {
1515 // we try to purge noneeded source, even without reaching the limit
1516 if((dwCurTick - lastpurgetime) > 40000) {
1517 if(!cur_src->SwapToAnotherFile(false , false, false , NULL)) {
1518 //however we only delete them if reaching the limit
1519 if (GetSourceCount() >= (thePrefs::GetMaxSourcePerFile()*.8 )) {
1520 RemoveSource(cur_src);
1521 lastpurgetime = dwCurTick;
1522 break; //Johnny-B - nothing more to do here (good eye!)
1524 } else {
1525 lastpurgetime = dwCurTick;
1526 break;
1529 // doubled reasktime for no needed parts - save connections and traffic
1530 if ( !((!cur_src->GetLastAskedTime()) ||
1531 (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME*2)) {
1532 break;
1534 // Recheck this client to see if still NNP..
1535 // Set to DS_NONE so that we force a TCP reask next time..
1536 cur_src->SetDownloadState(DS_NONE);
1538 break;
1540 case DS_ONQUEUE: {
1541 if( cur_src->IsRemoteQueueFull()) {
1542 if( ((dwCurTick - lastpurgetime) > 60000) &&
1543 (GetSourceCount() >= (thePrefs::GetMaxSourcePerFile()*.8 )) ) {
1544 RemoveSource( cur_src );
1545 lastpurgetime = dwCurTick;
1546 break; //Johnny-B - nothing more to do here (good eye!)
1550 // Give up to 1 min for UDP to respond..
1551 // If we are within on min on TCP, do not try..
1552 if ( theApp->IsConnected() &&
1553 ( (!cur_src->GetLastAskedTime()) ||
1554 (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME-20000)) {
1555 cur_src->UDPReaskForDownload();
1558 // No break here, since the next case takes care of asking for downloads.
1560 case DS_CONNECTING:
1561 case DS_TOOMANYCONNS:
1562 case DS_NONE:
1563 case DS_WAITCALLBACK:
1564 case DS_WAITCALLBACKKAD: {
1565 if ( theApp->IsConnected() &&
1566 ( (!cur_src->GetLastAskedTime()) ||
1567 (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME)) {
1568 if (!cur_src->AskForDownload()) {
1569 // I left this break here just as a reminder
1570 // just in case re rearange things..
1571 break;
1574 break;
1579 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
1580 if (IsA4AFAuto() && ((!m_LastNoNeededCheck) || (dwCurTick - m_LastNoNeededCheck > 900000))) {
1581 m_LastNoNeededCheck = dwCurTick;
1582 for ( SourceSet::iterator it = m_A4AFsrclist.begin(); it != m_A4AFsrclist.end(); ) {
1583 CUpDownClient *cur_source = *it++;
1584 uint8 download_state=cur_source->GetDownloadState();
1585 if( download_state != DS_DOWNLOADING
1586 && cur_source->GetRequestFile()
1587 && ((!cur_source->GetRequestFile()->IsA4AFAuto()) || download_state == DS_NONEEDEDPARTS))
1589 cur_source->SwapToAnotherFile(false, false, false, this);
1593 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
1595 // swap No needed partfiles if possible
1597 if (((old_trans==0) && (transferingsrc>0)) || ((old_trans>0) && (transferingsrc==0))) {
1598 SetPartFileStatus(status);
1601 // Kad source search
1602 if( GetMaxSourcePerFileUDP() > GetSourceCount()){
1603 //Once we can handle lowID users in Kad, we remove the second IsConnected
1604 if (theApp->downloadqueue->DoKademliaFileRequest() && (Kademlia::CKademlia::GetTotalFile() < KADEMLIATOTALFILE) && (dwCurTick > m_LastSearchTimeKad) && Kademlia::CKademlia::IsConnected() && theApp->IsConnected() && !IsStopped()){
1605 //Kademlia
1606 theApp->downloadqueue->SetLastKademliaFileRequest();
1608 if (GetKadFileSearchID()) {
1609 /* This will never happen anyway. We're talking a
1610 1h timespan and searches are at max 45secs */
1611 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), false);
1614 Kademlia::CUInt128 kadFileID(GetFileHash().GetHash());
1615 Kademlia::CSearch* pSearch = Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::FILE, true, kadFileID);
1616 AddDebugLogLineM(false, logKadSearch, CFormat(wxT("Preparing a Kad Search for '%s'")) % GetFileName());
1617 if (pSearch) {
1618 AddDebugLogLineM(false, logKadSearch, CFormat(wxT("Kad lookup started for '%s'")) % GetFileName());
1619 if(m_TotalSearchesKad < 7) {
1620 m_TotalSearchesKad++;
1622 m_LastSearchTimeKad = dwCurTick + (KADEMLIAREASKTIME*m_TotalSearchesKad);
1623 SetKadFileSearchID(pSearch->GetSearchID());
1626 } else {
1627 if(GetKadFileSearchID()) {
1628 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), true);
1632 // check if we want new sources from server
1633 if ( !m_localSrcReqQueued &&
1634 ( (!m_lastsearchtime) ||
1635 (dwCurTick - m_lastsearchtime) > SERVERREASKTIME) &&
1636 theApp->IsConnectedED2K() &&
1637 thePrefs::GetMaxSourcePerFileSoft() > GetSourceCount() &&
1638 !m_stopped ) {
1639 m_localSrcReqQueued = true;
1640 theApp->downloadqueue->SendLocalSrcRequest(this);
1643 // calculate datarate, set limit etc.
1646 ++m_count;
1648 // Kry - does the 3 / 30 difference produce too much flickering or CPU?
1649 if (m_count >= 30) {
1650 m_count = 0;
1651 UpdateAutoDownPriority();
1652 UpdateDisplayedInfo();
1653 if(m_bPercentUpdated == false) {
1654 UpdateCompletedInfos();
1656 m_bPercentUpdated = false;
1657 if (thePrefs::ShowCatTabInfos()) {
1658 Notify_ShowUpdateCatTabTitles();
1662 return (uint32)(kBpsDown*1024.0);
1665 bool CPartFile::CanAddSource(uint32 userid, uint16 port, uint32 serverip, uint16 serverport, uint8* pdebug_lowiddropped, bool ed2kID)
1668 //The incoming ID could have the userid in the Hybrid format..
1669 uint32 hybridID = 0;
1670 if( ed2kID ) {
1671 if (IsLowID(userid)) {
1672 hybridID = userid;
1673 } else {
1674 hybridID = wxUINT32_SWAP_ALWAYS(userid);
1676 } else {
1677 hybridID = userid;
1678 if (!IsLowID(userid)) {
1679 userid = wxUINT32_SWAP_ALWAYS(userid);
1683 // MOD Note: Do not change this part - Merkur
1684 if (theApp->IsConnectedED2K()) {
1685 if(::IsLowID(theApp->GetED2KID())) {
1686 if(theApp->GetED2KID() == userid && theApp->serverconnect->GetCurrentServer()->GetIP() == serverip && theApp->serverconnect->GetCurrentServer()->GetPort() == serverport ) {
1687 return false;
1689 if(theApp->GetPublicIP() == userid) {
1690 return false;
1692 } else {
1693 if(theApp->GetED2KID() == userid && thePrefs::GetPort() == port) {
1694 return false;
1699 if (Kademlia::CKademlia::IsConnected()) {
1700 if(!Kademlia::CKademlia::IsFirewalled()) {
1701 if(Kademlia::CKademlia::GetIPAddress() == hybridID && thePrefs::GetPort() == port) {
1702 return false;
1707 //This allows *.*.*.0 clients to not be removed if Ed2kID == false
1708 if ( IsLowID(hybridID) && theApp->IsFirewalled()) {
1709 if (pdebug_lowiddropped) {
1710 (*pdebug_lowiddropped)++;
1712 return false;
1714 // MOD Note - end
1715 return true;
1718 void CPartFile::AddSources(CMemFile& sources,uint32 serverip, uint16 serverport, unsigned origin, bool bWithObfuscationAndHash)
1720 uint8 count = sources.ReadUInt8();
1721 uint8 debug_lowiddropped = 0;
1722 uint8 debug_possiblesources = 0;
1723 CMD4Hash achUserHash;
1725 if (m_stopped) {
1726 // since we may received multiple search source UDP results we have to "consume" all data of that packet
1727 AddDebugLogLineM(false, logPartFile, wxT("Trying to add sources for a stopped file"));
1728 sources.Seek(count*(4+2), wxFromCurrent);
1729 return;
1732 for (int i = 0;i != count;++i) {
1733 uint32 userid = sources.ReadUInt32();
1734 uint16 port = sources.ReadUInt16();
1736 uint8 byCryptOptions = 0;
1737 if (bWithObfuscationAndHash){
1738 byCryptOptions = sources.ReadUInt8();
1739 if ((byCryptOptions & 0x80) > 0) {
1740 achUserHash = sources.ReadHash();
1743 if ((thePrefs::IsClientCryptLayerRequested() && (byCryptOptions & 0x01/*supported*/) > 0 && (byCryptOptions & 0x80) == 0)
1744 || (thePrefs::IsClientCryptLayerSupported() && (byCryptOptions & 0x02/*requested*/) > 0 && (byCryptOptions & 0x80) == 0)) {
1745 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));
1746 } else if (!thePrefs::IsClientCryptLayerRequested() && (byCryptOptions & 0x02/*requested*/) == 0 && (byCryptOptions & 0x80) != 0) {
1747 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));
1752 // "Filter LAN IPs" and "IPfilter" the received sources IP addresses
1753 if (!IsLowID(userid)) {
1754 // check for 0-IP, localhost and optionally for LAN addresses
1755 if ( !IsGoodIP(userid, thePrefs::FilterLanIPs()) ) {
1756 continue;
1758 if (theApp->ipfilter->IsFiltered(userid)) {
1759 continue;
1763 if (!CanAddSource(userid, port, serverip, serverport, &debug_lowiddropped)) {
1764 continue;
1767 if(thePrefs::GetMaxSourcePerFile() > GetSourceCount()) {
1768 ++debug_possiblesources;
1769 CUpDownClient* newsource = new CUpDownClient(port,userid,serverip,serverport,this, true, true);
1771 newsource->SetSourceFrom((ESourceFrom)origin);
1772 newsource->SetConnectOptions(byCryptOptions, true, false);
1774 if ((byCryptOptions & 0x80) != 0) {
1775 newsource->SetUserHash(achUserHash);
1778 theApp->downloadqueue->CheckAndAddSource(this,newsource);
1779 } else {
1780 AddDebugLogLineM(false, logPartFile, wxT("Consuming a packet because of max sources reached"));
1781 // Since we may receive multiple search source UDP results we have to "consume" all data of that packet
1782 // This '+1' is added because 'i' counts from 0.
1783 sources.Seek((count-(i+1))*(4+2), wxFromCurrent);
1784 if (GetKadFileSearchID()) {
1785 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), false);
1787 break;
1792 void CPartFile::UpdatePartsInfo()
1794 if( !IsPartFile() ) {
1795 CKnownFile::UpdatePartsInfo();
1796 return;
1799 // Cache part count
1800 uint16 partcount = GetPartCount();
1801 bool flag = (time(NULL) - m_nCompleteSourcesTime > 0);
1803 // Ensure the frequency-list is ready
1804 if ( m_SrcpartFrequency.size() != GetPartCount() ) {
1805 m_SrcpartFrequency.clear();
1806 m_SrcpartFrequency.insert(m_SrcpartFrequency.begin(), GetPartCount(), 0);
1809 // Find number of available parts
1810 uint16 availablecounter = 0;
1811 for ( uint16 i = 0; i < partcount; ++i ) {
1812 if ( m_SrcpartFrequency[i] )
1813 ++availablecounter;
1816 if ( ( availablecounter == partcount ) && ( m_availablePartsCount < partcount ) ) {
1817 lastseencomplete = time(NULL);
1820 m_availablePartsCount = availablecounter;
1822 if ( flag ) {
1823 ArrayOfUInts16 count;
1825 count.reserve(GetSourceCount());
1827 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it ) {
1828 if ( !(*it)->GetUpPartStatus().empty() && (*it)->GetUpPartCount() == partcount ) {
1829 count.push_back((*it)->GetUpCompleteSourcesCount());
1833 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo = m_nCompleteSourcesCountHi = 0;
1835 for (uint16 i = 0; i < partcount; ++i) {
1836 if( !i ) {
1837 m_nCompleteSourcesCount = m_SrcpartFrequency[i];
1839 else if( m_nCompleteSourcesCount > m_SrcpartFrequency[i]) {
1840 m_nCompleteSourcesCount = m_SrcpartFrequency[i];
1843 count.push_back(m_nCompleteSourcesCount);
1845 int32 n = count.size();
1846 if (n > 0) {
1847 std::sort(count.begin(), count.end(), std::less<uint16>());
1849 // calculate range
1850 int32 i= n >> 1; // (n / 2)
1851 int32 j= (n * 3) >> 2; // (n * 3) / 4
1852 int32 k= (n * 7) >> 3; // (n * 7) / 8
1854 //When still a part file, adjust your guesses by 20% to what you see..
1857 if (n < 5) {
1858 //Not many sources, so just use what you see..
1859 // welcome to 'plain stupid code'
1860 // m_nCompleteSourcesCount;
1861 m_nCompleteSourcesCountLo= m_nCompleteSourcesCount;
1862 m_nCompleteSourcesCountHi= m_nCompleteSourcesCount;
1863 } else if (n < 20) {
1864 // For low guess and normal guess count
1865 // If we see more sources then the guessed low and normal, use what we see.
1866 // If we see less sources then the guessed low, adjust network accounts for 80%,
1867 // we account for 20% with what we see and make sure we are still above the normal.
1868 // For high guess
1869 // Adjust 80% network and 20% what we see.
1870 if ( count[i] < m_nCompleteSourcesCount ) {
1871 m_nCompleteSourcesCountLo = m_nCompleteSourcesCount;
1872 } else {
1873 m_nCompleteSourcesCountLo =
1874 (uint16)((float)(count[i]*.8) +
1875 (float)(m_nCompleteSourcesCount*.2));
1877 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo;
1878 m_nCompleteSourcesCountHi =
1879 (uint16)((float)(count[j]*.8) +
1880 (float)(m_nCompleteSourcesCount*.2));
1881 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1882 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1884 } else {
1885 // Many sources
1886 // ------------
1887 // For low guess
1888 // Use what we see.
1889 // For normal guess
1890 // Adjust network accounts for 80%, we account for 20% with what
1891 // we see and make sure we are still above the low.
1892 // For high guess
1893 // Adjust network accounts for 80%, we account for 20% with what
1894 // we see and make sure we are still above the normal.
1896 m_nCompleteSourcesCountLo= m_nCompleteSourcesCount;
1897 m_nCompleteSourcesCount= (uint16)((float)(count[j]*.8)+(float)(m_nCompleteSourcesCount*.2));
1898 if( m_nCompleteSourcesCount < m_nCompleteSourcesCountLo ) {
1899 m_nCompleteSourcesCount = m_nCompleteSourcesCountLo;
1901 m_nCompleteSourcesCountHi= (uint16)((float)(count[k]*.8)+(float)(m_nCompleteSourcesCount*.2));
1902 if( m_nCompleteSourcesCountHi < m_nCompleteSourcesCount ) {
1903 m_nCompleteSourcesCountHi = m_nCompleteSourcesCount;
1907 m_nCompleteSourcesTime = time(NULL) + (60);
1909 UpdateDisplayedInfo();
1912 // Kry - Updated to 0.42e + bugfix
1913 // [Maella -Enhanced Chunk Selection- (based on jicxicmic)]
1914 bool CPartFile::GetNextRequestedBlock(CUpDownClient* sender, Requested_Block_Struct** newblocks, uint16* count)
1917 // The purpose of this function is to return a list of blocks (~180KB) to
1918 // download. To avoid a prematurely stop of the downloading, all blocks that
1919 // are requested from the same source must be located within the same
1920 // chunk (=> part ~9MB).
1922 // The selection of the chunk to download is one of the CRITICAL parts of the
1923 // edonkey network. The selection algorithm must insure the best spreading
1924 // of files.
1926 // The selection is based on 4 criteria:
1927 // 1. Frequency of the chunk (availability), very rare chunks must be downloaded
1928 // as quickly as possible to become a new available source.
1929 // 2. Parts used for preview (first + last chunk), preview or check a
1930 // file (e.g. movie, mp3)
1931 // 3. Request state (downloading in process), try to ask each source for another
1932 // chunk. Spread the requests between all sources.
1933 // 4. Completion (shortest-to-complete), partially retrieved chunks should be
1934 // completed before starting to download other one.
1936 // The frequency criterion defines three zones: very rare (<10%), rare (<50%)
1937 // and common (>30%). Inside each zone, the criteria have a specific weight, used
1938 // to calculate the priority of chunks. The chunk(s) with the highest
1939 // priority (highest=0, lowest=0xffff) is/are selected first.
1941 // very rare (preview) rare common
1942 // 0% <---- +0 pt ----> 10% <----- +10000 pt -----> 50% <---- +20000 pt ----> 100%
1943 // 1. <------- frequency: +25*frequency pt ----------->
1944 // 2. <- preview: +1 pt --><-------------- preview: set to 10000 pt ------------->
1945 // 3. <------ request: download in progress +20000 pt ------>
1946 // 4a. <- completion: 0% +100, 25% +75 .. 100% +0 pt --><-- !req => completion --->
1947 // 4b. <--- req => !completion -->
1949 // Unrolled, the priority scale is:
1951 // 0..xxxx unrequested and requested very rare chunks
1952 // 10000..1xxxx unrequested rare chunks + unrequested preview chunks
1953 // 20000..2xxxx unrequested common chunks (priority to the most complete)
1954 // 30000..3xxxx requested rare chunks + requested preview chunks
1955 // 40000..4xxxx requested common chunks (priority to the least complete)
1957 // This algorithm usually selects first the rarest chunk(s). However, partially
1958 // complete chunk(s) that is/are close to completion may overtake the priority
1959 // (priority inversion).
1960 // For the common chuncks, the algorithm tries to spread the dowload between
1961 // the sources
1964 // Check input parameters
1965 if(count == NULL) {
1966 return false;
1968 if ( sender->GetPartStatus().empty() ) {
1969 return false;
1971 // Define and create the list of the chunks to download
1972 const uint16 partCount = GetPartCount();
1973 ChunkList chunksList;
1975 // Main loop
1976 uint16 newBlockCount = 0;
1977 while(newBlockCount != *count) {
1978 // Create a request block stucture if a chunk has been previously selected
1979 if(sender->GetLastPartAsked() != 0xffff) {
1980 Requested_Block_Struct* pBlock = new Requested_Block_Struct;
1981 if(GetNextEmptyBlockInPart(sender->GetLastPartAsked(), pBlock) == true) {
1982 // Keep a track of all pending requested blocks
1983 m_requestedblocks_list.push_back(pBlock);
1984 // Update list of blocks to return
1985 newblocks[newBlockCount++] = pBlock;
1986 // Skip end of loop (=> CPU load)
1987 continue;
1988 } else {
1989 // All blocks for this chunk have been already requested
1990 delete pBlock;
1991 // => Try to select another chunk
1992 sender->SetLastPartAsked(0xffff);
1996 // Check if a new chunk must be selected (e.g. download starting, previous chunk complete)
1997 if(sender->GetLastPartAsked() == 0xffff) {
1998 // Quantify all chunks (create list of chunks to download)
1999 // This is done only one time and only if it is necessary (=> CPU load)
2000 if(chunksList.empty()) {
2001 // Indentify the locally missing part(s) that this source has
2002 for(uint16 i=0; i < partCount; ++i) {
2003 if(sender->IsPartAvailable(i) == true && GetNextEmptyBlockInPart(i, NULL) == true) {
2004 // Create a new entry for this chunk and add it to the list
2005 Chunk newEntry;
2006 newEntry.part = i;
2007 newEntry.frequency = m_SrcpartFrequency[i];
2008 chunksList.push_back(newEntry);
2012 // Check if any bloks(s) could be downloaded
2013 if(chunksList.empty()) {
2014 break; // Exit main loop while()
2017 // Define the bounds of the three zones (very rare, rare)
2018 // more depending on available sources
2019 uint8 modif=10;
2020 if (GetSourceCount()>800) {
2021 modif=2;
2022 } else if (GetSourceCount()>200) {
2023 modif=5;
2025 uint16 limit= modif*GetSourceCount()/ 100;
2026 if (limit==0) {
2027 limit=1;
2029 const uint16 veryRareBound = limit;
2030 const uint16 rareBound = 2*limit;
2032 // Cache Preview state (Criterion 2)
2033 FileType type = GetFiletype(GetFileName());
2034 const bool isPreviewEnable =
2035 thePrefs::GetPreviewPrio() &&
2036 (type == ftArchive || type == ftVideo);
2038 // Collect and calculate criteria for all chunks
2039 for (ChunkList::iterator it = chunksList.begin(); it != chunksList.end(); ++it) {
2040 Chunk& cur_chunk = *it;
2042 // Offsets of chunk
2043 const uint64 uStart = cur_chunk.part * PARTSIZE;
2044 const uint64 uEnd =
2045 ((GetFileSize() - 1) < (uStart + PARTSIZE - 1)) ?
2046 (GetFileSize() - 1) : (uStart + PARTSIZE - 1);
2047 // Criterion 2. Parts used for preview
2048 // Remark: - We need to download the first part and the last part(s).
2049 // - When the last part is very small, it's necessary to
2050 // download the two last parts.
2051 bool critPreview = false;
2052 if(isPreviewEnable == true) {
2053 if(cur_chunk.part == 0) {
2054 critPreview = true; // First chunk
2055 } else if(cur_chunk.part == partCount-1) {
2056 critPreview = true; // Last chunk
2057 } else if(cur_chunk.part == partCount-2) {
2058 // Last chunk - 1 (only if last chunk is too small)
2059 const uint32 sizeOfLastChunk = GetFileSize() - uEnd;
2060 if(sizeOfLastChunk < PARTSIZE/3) {
2061 critPreview = true; // Last chunk - 1
2066 // Criterion 3. Request state (downloading in process from other source(s))
2067 // => CPU load
2068 const bool critRequested =
2069 cur_chunk.frequency > veryRareBound &&
2070 IsAlreadyRequested(uStart, uEnd);
2072 // Criterion 4. Completion
2073 uint64 partSize = PARTSIZE;
2075 std::list<Gap_Struct*>::iterator it2 = m_gaplist.begin();
2076 for (; it2 != m_gaplist.end(); ++it2) {
2077 const Gap_Struct* cur_gap = *it2;
2078 // Check if Gap is into the limit
2079 if(cur_gap->start < uStart) {
2080 if(cur_gap->end > uStart && cur_gap->end < uEnd) {
2081 partSize -= cur_gap->end - uStart + 1;
2082 } else if(cur_gap->end >= uEnd) {
2083 partSize = 0;
2084 break; // exit loop for()
2086 } else if(cur_gap->start <= uEnd) {
2087 if(cur_gap->end < uEnd) {
2088 partSize -= cur_gap->end - cur_gap->start + 1;
2089 } else {
2090 partSize -= uEnd - cur_gap->start + 1;
2094 const uint16 critCompletion = (uint16)(partSize/(PARTSIZE/100)); // in [%]
2096 // Calculate priority with all criteria
2097 if(cur_chunk.frequency <= veryRareBound) {
2098 // 0..xxxx unrequested + requested very rare chunks
2099 cur_chunk.rank = (25 * cur_chunk.frequency) + // Criterion 1
2100 ((critPreview == true) ? 0 : 1) + // Criterion 2
2101 (100 - critCompletion); // Criterion 4
2102 } else if(critPreview == true) {
2103 // 10000..10100 unrequested preview chunks
2104 // 30000..30100 requested preview chunks
2105 cur_chunk.rank = ((critRequested == false) ? 10000 : 30000) + // Criterion 3
2106 (100 - critCompletion); // Criterion 4
2107 } else if(cur_chunk.frequency <= rareBound) {
2108 // 10101..1xxxx unrequested rare chunks
2109 // 30101..3xxxx requested rare chunks
2110 cur_chunk.rank = (25 * cur_chunk.frequency) + // Criterion 1
2111 ((critRequested == false) ? 10101 : 30101) + // Criterion 3
2112 (100 - critCompletion); // Criterion 4
2113 } else {
2114 // common chunk
2115 if(critRequested == false) { // Criterion 3
2116 // 20000..2xxxx unrequested common chunks
2117 cur_chunk.rank = 20000 + // Criterion 3
2118 (100 - critCompletion); // Criterion 4
2119 } else {
2120 // 40000..4xxxx requested common chunks
2121 // Remark: The weight of the completion criterion is inversed
2122 // to spead the requests over the completing chunks.
2123 // Without this, the chunk closest to completion will
2124 // received every new sources.
2125 cur_chunk.rank = 40000 + // Criterion 3
2126 (critCompletion); // Criterion 4
2132 // Select the next chunk to download
2133 if(!chunksList.empty()) {
2134 // Find and count the chunck(s) with the highest priority
2135 uint16 chunkCount = 0; // Number of found chunks with same priority
2136 uint16 rank = 0xffff; // Highest priority found
2138 // Collect and calculate criteria for all chunks
2139 for (ChunkList::iterator it = chunksList.begin(); it != chunksList.end(); ++it) {
2140 const Chunk& cur_chunk = *it;
2141 if(cur_chunk.rank < rank) {
2142 chunkCount = 1;
2143 rank = cur_chunk.rank;
2144 } else if(cur_chunk.rank == rank) {
2145 ++chunkCount;
2149 // Use a random access to avoid that everybody tries to download the
2150 // same chunks at the same time (=> spread the selected chunk among clients)
2151 uint16 randomness = 1 + (int) (((float)(chunkCount-1))*rand()/(RAND_MAX+1.0));
2153 for (ChunkList::iterator it = chunksList.begin(); it != chunksList.end(); ++it) {
2154 const Chunk& cur_chunk = *it;
2155 if(cur_chunk.rank == rank) {
2156 randomness--;
2157 if(randomness == 0) {
2158 // Selection process is over
2159 sender->SetLastPartAsked(cur_chunk.part);
2160 // Remark: this list might be reused up to *count times
2161 chunksList.erase(it);
2162 break; // exit loop for()
2166 } else {
2167 // There is no remaining chunk to download
2168 break; // Exit main loop while()
2172 // Return the number of the blocks
2173 *count = newBlockCount;
2174 // Return
2175 return (newBlockCount > 0);
2177 // Maella end
2178 // Kry EOI
2181 void CPartFile::RemoveBlockFromList(uint64 start,uint64 end)
2183 std::list<Requested_Block_Struct*>::iterator it = m_requestedblocks_list.begin();
2184 while (it != m_requestedblocks_list.end()) {
2185 std::list<Requested_Block_Struct*>::iterator it2 = it++;
2187 if ((*it2)->StartOffset <= start && (*it2)->EndOffset >= end) {
2188 m_requestedblocks_list.erase(it2);
2194 void CPartFile::RemoveAllRequestedBlocks(void)
2196 m_requestedblocks_list.clear();
2200 void CPartFile::CompleteFile(bool bIsHashingDone)
2202 if (GetKadFileSearchID()) {
2203 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), false);
2206 theApp->downloadqueue->RemoveLocalServerRequest(this);
2208 AddDebugLogLineM( false, logPartFile, wxString( wxT("CPartFile::CompleteFile: Hash ") ) + ( bIsHashingDone ? wxT("done") : wxT("not done") ) );
2210 if (!bIsHashingDone) {
2211 SetPartFileStatus(PS_COMPLETING);
2212 kBpsDown = 0.0;
2214 CPath partFile = m_partmetfilename.RemoveExt();
2215 CThreadScheduler::AddTask(new CHashingTask(GetFilePath(), partFile, this));
2216 return;
2217 } else {
2218 StopFile();
2219 m_is_A4AF_auto=false;
2220 SetPartFileStatus(PS_COMPLETING);
2221 // guess I was wrong about not need to spaw a thread ...
2222 // It is if the temp and incoming dirs are on different
2223 // partitions/drives and the file is large...[oz]
2226 PerformFileComplete();
2230 if (thePrefs::ShowCatTabInfos()) {
2231 Notify_ShowUpdateCatTabTitles();
2233 UpdateDisplayedInfo(true);
2237 void CPartFile::CompleteFileEnded(bool errorOccured, const CPath& newname)
2239 if (errorOccured) {
2240 m_paused = true;
2241 SetPartFileStatus(PS_ERROR);
2242 AddLogLineM(true, CFormat( _("Unexpected error while completing %s. File paused") )% GetFileName() );
2243 } else {
2244 m_fullname = newname;
2246 SetFilePath(m_fullname.GetPath());
2247 SetFileName(m_fullname.GetFullName());
2249 SetPartFileStatus(PS_COMPLETE);
2250 m_paused = false;
2251 ClearPriority();
2253 // TODO: What the f*** if it is already known?
2254 theApp->knownfiles->SafeAddKFile(this);
2256 // remove the file from the suspended uploads list
2257 theApp->uploadqueue->ResumeUpload(GetFileHash());
2258 theApp->downloadqueue->RemoveFile(this);
2259 theApp->sharedfiles->SafeAddKFile(this);
2260 UpdateDisplayedInfo(true);
2262 // republish that file to the ed2k-server to update the 'FT_COMPLETE_SOURCES' counter on the server.
2263 theApp->sharedfiles->RepublishFile(this);
2265 // Ensure that completed shows the correct value
2266 completedsize = GetFileSize();
2268 AddLogLineM(true, CFormat( _("Finished downloading: %s") ) % GetFileName() );
2271 theApp->downloadqueue->StartNextFile(this);
2275 void CPartFile::PerformFileComplete()
2277 // add this file to the suspended uploads list
2278 theApp->uploadqueue->SuspendUpload(GetFileHash());
2279 FlushBuffer();
2281 // close permanent handle
2282 if (m_hpartfile.IsOpened()) {
2283 m_hpartfile.Close();
2286 // Schedule task for completion of the file
2287 CThreadScheduler::AddTask(new CCompletionTask(this));
2291 void CPartFile::RemoveAllSources(bool bTryToSwap)
2293 for( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end();) {
2294 CUpDownClient* cur_src = *it++;
2295 if (bTryToSwap) {
2296 if (!cur_src->SwapToAnotherFile(true, true, true, NULL)) {
2297 RemoveSource(cur_src,true,false);
2298 // If it was not swapped, it's not on any file anymore, and should die
2300 } else {
2301 RemoveSource(cur_src,true,false);
2305 UpdatePartsInfo();
2307 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
2308 // remove all links A4AF in sources to this file
2309 if(!m_A4AFsrclist.empty()) {
2310 for( SourceSet::iterator it = m_A4AFsrclist.begin(); it != m_A4AFsrclist.end(); ) {
2311 CUpDownClient* cur_src = *it++;
2312 if ( cur_src->DeleteFileRequest( this ) ) {
2313 Notify_DownloadCtrlRemoveSource(cur_src, this);
2316 m_A4AFsrclist.clear();
2318 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
2319 UpdateFileRatingCommentAvail();
2323 void CPartFile::Delete()
2325 AddLogLineM(false, CFormat(_("Deleting file: %s")) % GetFileName());
2326 // Barry - Need to tell any connected clients to stop sending the file
2327 StopFile(true);
2328 AddDebugLogLineM(false, logPartFile, wxT("\tStopped"));
2330 theApp->sharedfiles->RemoveFile(this);
2331 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved from shared"));
2332 theApp->downloadqueue->RemoveFile(this);
2333 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved from download queue"));
2334 Notify_DownloadCtrlRemoveFile(this);
2335 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved transferwnd"));
2337 // Kry - WTF?
2338 // eMule had same problem with lseek error ... and override with a simple
2339 // check for INVALID_HANDLE_VALUE (that, btw, does not exist on linux)
2340 // So we just guess is < 0 on error and > 2 if ok (0 stdin, 1 stdout, 2 stderr)
2341 if (m_hpartfile.fd() > 2) { // 0 stdin, 1 stdout, 2 stderr
2342 m_hpartfile.Close();
2345 AddDebugLogLineM(false, logPartFile, wxT("\tClosed"));
2347 if (!CPath::RemoveFile(m_fullname)) {
2348 AddDebugLogLineM(true, logPartFile, CFormat(wxT("\tFailed to delete '%s'")) % m_fullname);
2349 } else {
2350 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved .part.met"));
2353 CPath partFile = m_fullname.RemoveExt();
2354 if (!CPath::RemoveFile(partFile)) {
2355 AddDebugLogLineM(true, logPartFile, CFormat(wxT("Failed to delete '%s'")) % partFile);
2356 } else {
2357 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved .part"));
2360 CPath BAKName = m_fullname.AppendExt(PARTMET_BAK_EXT);
2361 if (!CPath::RemoveFile(BAKName)) {
2362 AddDebugLogLineM(true, logPartFile, CFormat(wxT("Failed to delete '%s'")) % BAKName);
2363 } else {
2364 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved .BAK"));
2367 CPath SEEDSName = m_fullname.AppendExt(wxT(".seeds"));
2368 if (SEEDSName.FileExists()) {
2369 if (CPath::RemoveFile(SEEDSName)) {
2370 AddDebugLogLineM(false, logPartFile, wxT("\tRemoved .seeds"));
2371 } else {
2372 AddDebugLogLineM(true, logPartFile, CFormat(wxT("Failed to delete '%s'")) % SEEDSName);
2376 AddDebugLogLineM(false, logPartFile, wxT("Done"));
2378 delete this;
2382 bool CPartFile::HashSinglePart(uint16 partnumber)
2384 if ((GetHashCount() <= partnumber) && (GetPartCount() > 1)) {
2385 AddLogLineM(true,
2386 CFormat( _("WARNING: Unable to hash downloaded part - hashset incomplete for '%s'") )
2387 % GetFileName() );
2388 m_hashsetneeded = true;
2389 return true;
2390 } else if ((GetHashCount() <= partnumber) && GetPartCount() != 1) {
2391 AddLogLineM(true, CFormat( _("ERROR: Unable to hash downloaded part - hashset incomplete (%s). This should never happen")) % GetFileName() );
2392 m_hashsetneeded = true;
2393 return true;
2394 } else {
2395 CMD4Hash hashresult;
2396 uint64 length = PARTSIZE;
2397 const uint64 offset = length * partnumber;
2398 try {
2399 m_hpartfile.Seek(offset, wxFromStart);
2400 if (offset + PARTSIZE > m_hpartfile.GetLength()) {
2401 length = m_hpartfile.GetLength() - offset;
2402 wxASSERT( length <= PARTSIZE );
2404 CreateHashFromFile(&m_hpartfile, length, &hashresult, NULL);
2405 } catch (const CIOFailureException& e) {
2406 AddLogLineM(true, CFormat( wxT("EOF while hashing downloaded part %u with length %u (max %u) of partfile '%s' with length %u: %s"))
2407 % partnumber % length % (offset+length) % GetFileName() % GetFileSize() % e.what());
2408 SetPartFileStatus(PS_ERROR);
2409 return false;
2410 } catch (const CEOFException& e) {
2411 AddLogLineM(true, CFormat( wxT("EOF while hashing downloaded part %u with length %u (max %u) of partfile '%s' with length %u: %s"))
2412 % partnumber % length % (offset+length) % GetFileName() % GetFileSize() % e.what());
2413 return false;
2416 if (GetPartCount() > 1) {
2417 if (hashresult != GetPartHash(partnumber)) {
2418 AddDebugLogLineM(false, logPartFile, CFormat( wxT("%s: Expected part-hash: %s")) % GetFileName() % GetPartHash(partnumber).Encode() );
2419 AddDebugLogLineM(false, logPartFile, CFormat( wxT("%s: Actual part-hash: %s")) % GetFileName() % hashresult.Encode() );
2420 return false;
2421 } else {
2422 return true;
2424 } else {
2425 if (hashresult != m_abyFileHash) {
2426 return false;
2427 } else {
2428 return true;
2435 bool CPartFile::IsCorruptedPart(uint16 partnumber)
2437 return std::find(m_corrupted_list.begin(), m_corrupted_list.end(), partnumber)
2438 != m_corrupted_list.end();
2442 void CPartFile::SetDownPriority(uint8 np, bool bSave, bool bRefresh )
2444 if ( m_iDownPriority != np ) {
2445 m_iDownPriority = np;
2446 if ( bRefresh )
2447 UpdateDisplayedInfo(true);
2448 if ( bSave )
2449 SavePartFile();
2454 void CPartFile::StopFile(bool bCancel)
2456 // Kry - Need to set it here to get into SetPartFileStatus(status) correctly
2457 m_stopped = true;
2459 // Barry - Need to tell any connected clients to stop sending the file
2460 PauseFile();
2462 m_LastSearchTimeKad = 0;
2463 m_TotalSearchesKad = 0;
2465 RemoveAllSources(true);
2466 kBpsDown = 0.0;
2467 transferingsrc = 0;
2468 memset(m_anStates,0,sizeof(m_anStates));
2470 if (!bCancel) {
2471 FlushBuffer(true);
2474 UpdateDisplayedInfo(true);
2478 void CPartFile::StopPausedFile()
2480 if (!IsStopped()) {
2481 // Once an hour, remove any sources for files which are no longer active downloads
2482 switch (GetStatus()) {
2483 case PS_PAUSED:
2484 case PS_INSUFFICIENT:
2485 case PS_ERROR:
2486 if (time(NULL) - m_iLastPausePurge > (60*60)) {
2487 m_iLastPausePurge = time(NULL);
2488 StopFile();
2495 void CPartFile::PauseFile(bool bInsufficient)
2497 SetActive(false);
2499 if ( status == PS_COMPLETE || status == PS_COMPLETING ) {
2500 return;
2503 if (GetKadFileSearchID()) {
2504 Kademlia::CSearchManager::StopSearch(GetKadFileSearchID(), true);
2505 // If we were in the middle of searching, reset timer so they can resume searching.
2506 m_LastSearchTimeKad = 0;
2509 m_iLastPausePurge = time(NULL);
2511 theApp->downloadqueue->RemoveLocalServerRequest(this);
2513 CPacket packet( OP_CANCELTRANSFER, 0, OP_EDONKEYPROT );
2514 for( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ) {
2515 CUpDownClient* cur_src = *it++;
2516 if (cur_src->GetDownloadState() == DS_DOWNLOADING) {
2517 if (!cur_src->GetSentCancelTransfer()) {
2518 theStats::AddUpOverheadOther( packet.GetPacketSize() );
2519 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + cur_src->GetFullIP() );
2520 cur_src->SendPacket( &packet, false, true );
2521 cur_src->SetSentCancelTransfer( true );
2523 cur_src->SetDownloadState(DS_ONQUEUE);
2528 m_insufficient = bInsufficient;
2529 m_paused = true;
2532 kBpsDown = 0.0;
2533 transferingsrc = 0;
2534 m_anStates[DS_DOWNLOADING] = 0;
2536 SetStatus(status);
2540 void CPartFile::ResumeFile()
2542 if ( status == PS_COMPLETE || status == PS_COMPLETING ) {
2543 return;
2546 if ( m_insufficient && !CheckFreeDiskSpace() ) {
2547 // Still not enough free discspace
2548 return;
2551 m_paused = false;
2552 m_stopped = false;
2553 m_insufficient = false;
2555 m_lastsearchtime = 0;
2556 SetStatus(status);
2557 SetActive(theApp->IsConnected());
2559 if (m_gaplist.empty() && (GetStatus() == PS_ERROR)) {
2560 // The file has already been hashed at this point
2561 CompleteFile(true);
2564 UpdateDisplayedInfo(true);
2568 bool CPartFile::CheckFreeDiskSpace( uint64 neededSpace )
2570 uint64 free = CPath::GetFreeSpaceAt(GetFilePath());
2571 if (free == static_cast<uint64>(wxInvalidOffset)) {
2572 // If GetFreeSpaceAt() fails, then the path probably does not exist.
2573 return false;
2576 // The very least acceptable diskspace is a single PART
2577 if ( free < PARTSIZE ) {
2578 // Always fail in this case, since we risk losing data if we try to
2579 // write on a full partition.
2580 return false;
2583 // All other checks are only made if the user has enabled them
2584 if ( thePrefs::IsCheckDiskspaceEnabled() ) {
2585 neededSpace += thePrefs::GetMinFreeDiskSpace();
2587 // Due to the the existance of sparse files, we cannot assume that
2588 // writes within the file doesn't cause new blocks to be allocated.
2589 // Therefore, we have to simply stop writing the moment the limit has
2590 // been exceeded.
2591 return free >= neededSpace;
2594 return true;
2598 void CPartFile::SetLastAnsweredTime()
2600 m_ClientSrcAnswered = ::GetTickCount();
2603 void CPartFile::SetLastAnsweredTimeTimeout()
2605 m_ClientSrcAnswered = 2 * CONNECTION_LATENCY + ::GetTickCount() - SOURCECLIENTREASKS;
2608 CPacket *CPartFile::CreateSrcInfoPacket(const CUpDownClient* forClient, uint8 byRequestedVersion, uint16 nRequestedOptions)
2611 if ( m_SrcList.empty() ) {
2612 return NULL;
2615 if(!IsPartFile()) {
2616 return CKnownFile::CreateSrcInfoPacket(forClient, byRequestedVersion, nRequestedOptions);
2619 if (((forClient->GetRequestFile() != this)
2620 && (forClient->GetUploadFile() != this)) || forClient->GetUploadFileID() != GetFileHash()) {
2621 wxString file1 = _("Unknown");
2622 if (forClient->GetRequestFile() && forClient->GetRequestFile()->GetFileName().IsOk()) {
2623 file1 = forClient->GetRequestFile()->GetFileName().GetPrintable();
2624 } else if (forClient->GetUploadFile() && forClient->GetUploadFile()->GetFileName().IsOk()) {
2625 file1 = forClient->GetUploadFile()->GetFileName().GetPrintable();
2627 wxString file2 = _("Unknown");
2628 if (GetFileName().IsOk()) {
2629 file2 = GetFileName().GetPrintable();
2631 AddDebugLogLineM(false, logPartFile, wxT("File mismatch on source packet (P) Sending: ") + file1 + wxT(" From: ") + file2);
2632 return NULL;
2635 if ( !(GetStatus() == PS_READY || GetStatus() == PS_EMPTY)) {
2636 return NULL;
2639 const BitVector& reqstatus = forClient->GetPartStatus();
2640 bool KnowNeededParts = !reqstatus.empty();
2641 //wxASSERT(rcvstatus.size() == GetPartCount()); // Obviously!
2642 if (KnowNeededParts && (reqstatus.size() != GetPartCount())) {
2643 // Yuck. Same file but different part count? Seriously fucked up.
2644 AddDebugLogLineM(false, logPartFile, wxString::Format(wxT("Impossible situation: different partcounts for the same part file: %i (client) and %i (file)"),reqstatus.size(),GetPartCount()));
2645 return NULL;
2648 CMemFile data(1024);
2650 uint8 byUsedVersion;
2651 bool bIsSX2Packet;
2652 if (forClient->SupportsSourceExchange2() && byRequestedVersion > 0){
2653 // the client uses SourceExchange2 and requested the highest version he knows
2654 // and we send the highest version we know, but of course not higher than his request
2655 byUsedVersion = std::min(byRequestedVersion, (uint8)SOURCEEXCHANGE2_VERSION);
2656 bIsSX2Packet = true;
2657 data.WriteUInt8(byUsedVersion);
2659 // we don't support any special SX2 options yet, reserved for later use
2660 if (nRequestedOptions != 0) {
2661 AddDebugLogLineM(false, logKnownFiles, CFormat(wxT("Client requested unknown options for SourceExchange2: %u")) % nRequestedOptions);
2663 } else {
2664 byUsedVersion = forClient->GetSourceExchange1Version();
2665 bIsSX2Packet = false;
2666 if (forClient->SupportsSourceExchange2()) {
2667 AddDebugLogLineM(false, logKnownFiles, wxT("Client which announced to support SX2 sent SX1 packet instead"));
2671 uint16 nCount = 0;
2673 data.WriteHash(m_abyFileHash);
2674 data.WriteUInt16(nCount);
2675 bool bNeeded;
2676 for (SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it ) {
2677 bNeeded = false;
2678 CUpDownClient* cur_src = *it;
2680 int state = cur_src->GetDownloadState();
2681 int valid = ( state == DS_DOWNLOADING ) || ( state == DS_ONQUEUE && !cur_src->IsRemoteQueueFull() );
2683 if ( cur_src->HasLowID() || !valid ) {
2684 continue;
2687 // only send source which have needed parts for this client if possible
2688 const BitVector& srcstatus = cur_src->GetPartStatus();
2689 if ( !srcstatus.empty() ) {
2690 //wxASSERT(srcstatus.size() == GetPartCount()); // Obviously!
2691 if (srcstatus.size() != GetPartCount()) {
2692 continue;
2694 if ( KnowNeededParts ) {
2695 // only send sources which have needed parts for this client
2696 for (int x = 0; x < GetPartCount(); ++x) {
2697 if (srcstatus[x] && !reqstatus[x]) {
2698 bNeeded = true;
2699 break;
2702 } else {
2703 // if we don't know the need parts for this client,
2704 // return any source currently a client sends it's
2705 // file status only after it has at least one complete part
2706 if (srcstatus.size() != GetPartCount()) {
2707 continue;
2709 for (int x = 0; x < GetPartCount(); ++x){
2710 if (srcstatus[x]) {
2711 bNeeded = true;
2712 break;
2717 if(bNeeded) {
2718 ++nCount;
2719 uint32 dwID;
2720 if(forClient->GetSourceExchange1Version() > 2) {
2721 dwID = cur_src->GetUserIDHybrid();
2722 } else {
2723 dwID = wxUINT32_SWAP_ALWAYS(cur_src->GetUserIDHybrid());
2725 data.WriteUInt32(dwID);
2726 data.WriteUInt16(cur_src->GetUserPort());
2727 data.WriteUInt32(cur_src->GetServerIP());
2728 data.WriteUInt16(cur_src->GetServerPort());
2730 if (byUsedVersion >= 2) {
2731 data.WriteHash(cur_src->GetUserHash());
2734 if (byUsedVersion >= 4){
2735 // CryptSettings - SourceExchange V4
2736 // 5 Reserved (!)
2737 // 1 CryptLayer Required
2738 // 1 CryptLayer Requested
2739 // 1 CryptLayer Supported
2740 const uint8 uSupportsCryptLayer = cur_src->SupportsCryptLayer() ? 1 : 0;
2741 const uint8 uRequestsCryptLayer = cur_src->RequestsCryptLayer() ? 1 : 0;
2742 const uint8 uRequiresCryptLayer = cur_src->RequiresCryptLayer() ? 1 : 0;
2743 const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0);
2744 data.WriteUInt8(byCryptOptions);
2747 if (nCount > 500) {
2748 break;
2752 if (!nCount) {
2753 return 0;
2755 data.Seek(bIsSX2Packet ? 17 : 16, wxFromStart);
2756 data.WriteUInt16(nCount);
2758 CPacket* result = new CPacket(data, OP_EMULEPROT, bIsSX2Packet ? OP_ANSWERSOURCES2 : OP_ANSWERSOURCES);
2760 // 16+2+501*(4+2+4+2+16) = 14046 bytes max.
2761 if (result->GetPacketSize() > 354) {
2762 result->PackPacket();
2765 return result;
2768 void CPartFile::AddClientSources(CMemFile* sources, unsigned nSourceFrom, uint8 uClientSXVersion, bool bSourceExchange2, const CUpDownClient* /*pClient*/)
2770 // Kad reviewed
2772 if (m_stopped) {
2773 return;
2776 uint32 nCount = 0;
2777 uint8 uPacketSXVersion = 0;
2778 if (!bSourceExchange2) {
2779 nCount = sources->ReadUInt16();
2781 // Check if the data size matches the 'nCount' for v1 or v2 and eventually correct the source
2782 // exchange version while reading the packet data. Otherwise we could experience a higher
2783 // chance in dealing with wrong source data, userhashs and finally duplicate sources.
2784 uint32 uDataSize = sources->GetLength() - sources->GetPosition();
2786 if ((uint32)(nCount*(4+2+4+2)) == uDataSize) { //Checks if version 1 packet is correct size
2787 if(uClientSXVersion != 1) {
2788 return;
2790 uPacketSXVersion = 1;
2791 } else if ((uint32)(nCount*(4+2+4+2+16)) == uDataSize) { // Checks if version 2&3 packet is correct size
2792 if (uClientSXVersion == 2) {
2793 uPacketSXVersion = 2;
2794 } else if (uClientSXVersion > 2) {
2795 uPacketSXVersion = 3;
2796 } else {
2797 return;
2799 } else if (nCount*(4+2+4+2+16+1) == uDataSize) {
2800 if (uClientSXVersion != 4 ) {
2801 return;
2803 uPacketSXVersion = 4;
2804 } else {
2805 // If v5 inserts additional data (like v2), the above code will correctly filter those packets.
2806 // If v5 appends additional data after <count>(<Sources>)[count], we are in trouble with the
2807 // above code. Though a client which does not understand v5+ should never receive such a packet.
2808 AddDebugLogLineM(false, logClient, CFormat(wxT("Received invalid source exchange packet (v%u) of data size %u for %s")) % uClientSXVersion % uDataSize % GetFileName());
2809 return;
2811 } else {
2812 // for SX2:
2813 // We only check if the version is known by us and do a quick sanitize check on known version
2814 // other then SX1, the packet will be ignored if any error appears, sicne it can't be a "misunderstanding" anymore
2815 if (uClientSXVersion > SOURCEEXCHANGE2_VERSION || uClientSXVersion == 0 ){
2816 AddDebugLogLineM(false, logPartFile, CFormat(wxT("Invalid source exchange type version: %i")) % uClientSXVersion);
2817 return;
2820 // all known versions use the first 2 bytes as count and unknown version are already filtered above
2821 nCount = sources->ReadUInt16();
2822 uint32 uDataSize = (uint32)(sources->GetLength() - sources->GetPosition());
2823 bool bError = false;
2824 switch (uClientSXVersion){
2825 case 1:
2826 bError = nCount*(4+2+4+2) != uDataSize;
2827 break;
2828 case 2:
2829 case 3:
2830 bError = nCount*(4+2+4+2+16) != uDataSize;
2831 break;
2832 case 4:
2833 bError = nCount*(4+2+4+2+16+1) != uDataSize;
2834 break;
2835 default:
2836 wxASSERT( 0 );
2839 if (bError){
2840 wxASSERT( 0 );
2841 AddDebugLogLineM(false, logPartFile, wxT("Invalid source exchange data size."));
2842 return;
2844 uPacketSXVersion = uClientSXVersion;
2847 for (uint16 i = 0;i != nCount;++i) {
2849 uint32 dwID = sources->ReadUInt32();
2850 uint16 nPort = sources->ReadUInt16();
2851 uint32 dwServerIP = sources->ReadUInt32();
2852 uint16 nServerPort = sources->ReadUInt16();
2854 CMD4Hash userHash;
2855 if (uPacketSXVersion > 1) {
2856 userHash = sources->ReadHash();
2859 uint8 byCryptOptions = 0;
2860 if (uPacketSXVersion >= 4) {
2861 byCryptOptions = sources->ReadUInt8();
2864 //Clients send ID's the the Hyrbid format so highID clients with *.*.*.0 won't be falsely switched to a lowID..
2865 uint32 dwIDED2K;
2866 if (uPacketSXVersion >= 3) {
2867 dwIDED2K = wxUINT32_SWAP_ALWAYS(dwID);
2868 } else {
2869 dwIDED2K = dwID;
2872 // check the HighID(IP) - "Filter LAN IPs" and "IPfilter" the received sources IP addresses
2873 if (!IsLowID(dwID)) {
2874 if (!IsGoodIP(dwIDED2K, thePrefs::FilterLanIPs())) {
2875 // check for 0-IP, localhost and optionally for LAN addresses
2876 AddDebugLogLineM(false, logIPFilter, CFormat(wxT("Ignored source (IP=%s) received via %s - bad IP")) % Uint32toStringIP(dwIDED2K) % OriginToText(nSourceFrom));
2877 continue;
2879 if (theApp->ipfilter->IsFiltered(dwIDED2K)) {
2880 AddDebugLogLineM(false, logIPFilter, CFormat(wxT("Ignored source (IP=%s) received via %s - IPFilter")) % Uint32toStringIP(dwIDED2K) % OriginToText(nSourceFrom));
2881 continue;
2883 if (theApp->clientlist->IsBannedClient(dwIDED2K)){
2884 continue;
2888 // additionally check for LowID and own IP
2889 if (!CanAddSource(dwID, nPort, dwServerIP, nServerPort, NULL, false)) {
2890 AddDebugLogLineM(false, logIPFilter, CFormat(wxT("Ignored source (IP=%s) received via source exchange")) % Uint32toStringIP(dwIDED2K));
2891 continue;
2894 if(thePrefs::GetMaxSourcePerFile() > GetSourceCount()) {
2895 CUpDownClient* newsource = new CUpDownClient(nPort,dwID,dwServerIP,nServerPort,this, (uPacketSXVersion < 3), true);
2896 if (uPacketSXVersion > 1) {
2897 newsource->SetUserHash(userHash);
2900 if (uPacketSXVersion >= 4) {
2901 newsource->SetConnectOptions(byCryptOptions, true, false);
2904 newsource->SetSourceFrom((ESourceFrom)nSourceFrom);
2905 theApp->downloadqueue->CheckAndAddSource(this,newsource);
2907 } else {
2908 break;
2913 void CPartFile::UpdateAutoDownPriority()
2915 if (!IsAutoDownPriority()) {
2916 return;
2918 if (GetSourceCount() <= RARE_FILE) {
2919 if ( GetDownPriority() != PR_HIGH )
2920 SetDownPriority(PR_HIGH, false, false);
2921 } else if (GetSourceCount() < 100) {
2922 if ( GetDownPriority() != PR_NORMAL )
2923 SetDownPriority(PR_NORMAL, false, false);
2924 } else {
2925 if ( GetDownPriority() != PR_LOW )
2926 SetDownPriority(PR_LOW, false, false);
2930 // making this function return a higher when more sources have the extended
2931 // protocol will force you to ask a larger variety of people for sources
2933 int CPartFile::GetCommonFilePenalty()
2935 //TODO: implement, but never return less than MINCOMMONPENALTY!
2936 return MINCOMMONPENALTY;
2939 /* Barry - Replaces BlockReceived()
2941 Originally this only wrote to disk when a full 180k block
2942 had been received from a client, and only asked for data in
2943 180k blocks.
2945 This meant that on average 90k was lost for every connection
2946 to a client data source. That is a lot of wasted data.
2948 To reduce the lost data, packets are now written to a buffer
2949 and flushed to disk regularly regardless of size downloaded.
2950 This includes compressed packets.
2952 Data is also requested only where gaps are, not in 180k blocks.
2953 The requests will still not exceed 180k, but may be smaller to
2954 fill a gap.
2957 // Kry - transize is 32bits, no packet can be more than that (this is
2958 // compressed size). Even 32bits is too much imho.As for the return size,
2959 // look at the lenData below.
2960 uint32 CPartFile::WriteToBuffer(uint32 transize, byte* data, uint64 start, uint64 end, Requested_Block_Struct *block)
2962 // Increment transferred bytes counter for this file
2963 transferred += transize;
2965 // This is needed a few times
2966 // Kry - should not need a uint64 here - no block is larger than
2967 // 2GB even after uncompressed.
2968 uint32 lenData = (uint32) (end - start + 1);
2970 if(lenData > transize) {
2971 m_iGainDueToCompression += lenData-transize;
2974 // Occasionally packets are duplicated, no point writing it twice
2975 if (IsComplete(start, end)) {
2976 AddDebugLogLineM(false, logPartFile,
2977 CFormat(wxT("File '%s' has already been written from %u to %u"))
2978 % GetFileName() % start % end);
2979 return 0;
2982 // security sanitize check to make sure we do not write anything into an already hashed complete chunk
2983 const uint64 nStartChunk = start / PARTSIZE;
2984 const uint64 nEndChunk = end / PARTSIZE;
2985 if (IsComplete(PARTSIZE * (uint64)nStartChunk, (PARTSIZE * (uint64)(nStartChunk + 1)) - 1)) {
2986 AddDebugLogLineM(false, logPartFile, CFormat(wxT("Received data touches already hashed chunk - ignored (start): %u-%u; File=%s")) % start % end % GetFileName());
2987 return 0;
2988 } else if (nStartChunk != nEndChunk) {
2989 if (IsComplete(PARTSIZE * (uint64)nEndChunk, (PARTSIZE * (uint64)(nEndChunk + 1)) - 1)) {
2990 AddDebugLogLineM(false, logPartFile, CFormat(wxT("Received data touches already hashed chunk - ignored (end): %u-%u; File=%s")) % start % end % GetFileName());
2991 return 0;
2993 #ifdef __DEBUG__
2994 else {
2995 AddDebugLogLineM(false, logPartFile, CFormat(wxT("Received data crosses chunk boundaries: %u-%u; File=%s")) % start % end % GetFileName());
2997 #endif
3000 // Create copy of data as new buffer
3001 byte *buffer = new byte[lenData];
3002 memcpy(buffer, data, lenData);
3004 // Create a new buffered queue entry
3005 PartFileBufferedData *item = new PartFileBufferedData;
3006 item->data = buffer;
3007 item->start = start;
3008 item->end = end;
3009 item->block = block;
3011 // Add to the queue in the correct position (most likely the end)
3012 bool added = false;
3014 std::list<PartFileBufferedData*>::iterator it = m_BufferedData_list.begin();
3015 for (; it != m_BufferedData_list.end(); ++it) {
3016 PartFileBufferedData* queueItem = *it;
3018 if (item->end <= queueItem->end) {
3019 if (it != m_BufferedData_list.begin()) {
3020 added = true;
3022 m_BufferedData_list.insert(--it, item);
3025 break;
3029 if (!added) {
3030 m_BufferedData_list.push_front(item);
3033 // Increment buffer size marker
3034 m_nTotalBufferData += lenData;
3036 // Mark this small section of the file as filled
3037 FillGap(item->start, item->end);
3039 // Update the flushed mark on the requested block
3040 // The loop here is unfortunate but necessary to detect deleted blocks.
3042 std::list<Requested_Block_Struct*>::iterator it2 = m_requestedblocks_list.begin();
3043 for (; it2 != m_requestedblocks_list.end(); ++it2) {
3044 if (*it2 == item->block) {
3045 item->block->transferred += lenData;
3049 if (m_gaplist.empty()) {
3050 FlushBuffer(true);
3053 // Return the length of data written to the buffer
3054 return lenData;
3057 void CPartFile::FlushBuffer(bool /*forcewait*/, bool bForceICH, bool bNoAICH)
3059 m_nLastBufferFlushTime = GetTickCount();
3061 if (m_BufferedData_list.empty()) {
3062 return;
3066 uint32 partCount = GetPartCount();
3067 std::vector<bool> changedPart(partCount);
3069 // Remember which parts need to be checked at the end of the flush
3070 for ( uint32 i = 0; i < partCount; ++i ) {
3071 changedPart[ i ] = false;
3075 // Ensure file is big enough to write data to (the last item will be the furthest from the start)
3076 uint32 newData = 0;
3078 std::list<PartFileBufferedData*>::iterator it = m_BufferedData_list.begin();
3079 for (; it != m_BufferedData_list.end(); ++it) {
3080 PartFileBufferedData* item = *it;
3081 wxASSERT((item->end - item->start) < 0xFFFFFFFF);
3082 newData += (uint32) (item->end - item->start + 1);
3085 if ( !CheckFreeDiskSpace( newData ) ) {
3086 // Not enough free space to write the last item, bail
3087 AddLogLineM(true, CFormat( _("WARNING: Not enough free disk-space! Pausing file: %s") ) % GetFileName());
3089 PauseFile( true );
3090 return;
3093 // Loop through queue
3094 while ( !m_BufferedData_list.empty() ) {
3095 // Get top item and remove it from the queue
3096 PartFileBufferedData* item = m_BufferedData_list.front();
3097 m_BufferedData_list.pop_front();
3099 // This is needed a few times
3100 wxASSERT((item->end - item->start) < 0xFFFFFFFF);
3101 uint32 lenData = (uint32)(item->end - item->start + 1);
3103 // SLUGFILLER: SafeHash - could be more than one part
3104 for (uint32 curpart = (item->start/PARTSIZE); curpart <= (item->end/PARTSIZE); ++curpart) {
3105 wxASSERT(curpart < partCount);
3106 changedPart[curpart] = true;
3108 // SLUGFILLER: SafeHash
3110 // Go to the correct position in file and write block of data
3111 try {
3112 m_hpartfile.Seek(item->start);
3113 m_hpartfile.Write(item->data, lenData);
3114 } catch (const CIOFailureException& e) {
3115 AddDebugLogLineM(true, logPartFile, wxT("Error while saving part-file: ") + e.what());
3116 SetPartFileStatus(PS_ERROR);
3119 // Decrease buffer size
3120 m_nTotalBufferData -= lenData;
3122 // Release memory used by this item
3123 delete [] item->data;
3124 delete item;
3128 // Update last-changed date
3129 m_lastDateChanged = wxDateTime::GetTimeNow();
3131 try {
3132 // Partfile should never be too large
3133 if (m_hpartfile.GetLength() > GetFileSize()) {
3134 // it's "last chance" correction. the real bugfix has to be applied 'somewhere' else
3135 m_hpartfile.SetLength(GetFileSize());
3137 } catch (const CIOFailureException& e) {
3138 AddDebugLogLineM(true, logPartFile,
3139 CFormat(wxT("Error while truncating part-file (%s): %s"))
3140 % m_fullname.RemoveExt() % e.what());
3141 SetPartFileStatus(PS_ERROR);
3146 // Check each part of the file
3147 uint32 partRange = 0;
3148 try {
3149 uint64 curLength = m_hpartfile.GetLength();
3151 partRange = (uint32)((curLength % PARTSIZE > 0) ? ((curLength % PARTSIZE) - 1) : (PARTSIZE - 1));
3152 } catch (const CIOFailureException& e) {
3153 AddDebugLogLineM(true, logPartFile,
3154 CFormat(wxT("Error while accessing part-file (%s): %s"))
3155 % m_fullname.RemoveExt() % e.what());
3156 SetPartFileStatus(PS_ERROR);
3159 wxASSERT(partRange);
3160 for (int partNumber = partCount-1; partRange && partNumber >= 0; partNumber--) {
3161 if (changedPart[partNumber] == false) {
3162 // Any parts other than last must be full size
3163 partRange = PARTSIZE - 1;
3164 continue;
3167 // Is this 9MB part complete
3168 if (IsComplete(PARTSIZE * partNumber, (PARTSIZE * (partNumber + 1)) - 1)) {
3169 // Is part corrupt
3170 if (!HashSinglePart(partNumber)) {
3171 AddLogLineM(true, CFormat(
3172 _("Downloaded part %i is corrupt in file: %s") ) % partNumber % GetFileName() );
3173 AddGap(PARTSIZE*partNumber, (PARTSIZE*partNumber + partRange));
3174 // add part to corrupted list, if not already there
3175 if (!IsCorruptedPart(partNumber)) {
3176 m_corrupted_list.push_back(partNumber);
3178 // request AICH recovery data
3179 if (!bNoAICH) {
3180 RequestAICHRecovery((uint16)partNumber);
3182 // Reduce transferred amount by corrupt amount
3183 m_iLostDueToCorruption += (partRange + 1);
3184 } else {
3185 if (!m_hashsetneeded) {
3186 AddDebugLogLineM(false, logPartFile, CFormat(
3187 wxT("Finished part %u of '%s'")) % partNumber % GetFileName());
3190 // if this part was successfully completed (although ICH is active), remove from corrupted list
3191 EraseFirstValue(m_corrupted_list, partNumber);
3193 if (status == PS_EMPTY) {
3194 if (theApp->IsRunning()) { // may be called during shutdown!
3195 if (GetHashCount() == GetED2KPartHashCount() && !m_hashsetneeded) {
3196 // Successfully completed part, make it available for sharing
3197 SetStatus(PS_READY);
3198 theApp->sharedfiles->SafeAddKFile(this);
3203 } else if ( IsCorruptedPart(partNumber) && (thePrefs::IsICHEnabled() || bForceICH)) {
3204 // Try to recover with minimal loss
3205 if (HashSinglePart(partNumber)) {
3206 ++m_iTotalPacketsSavedDueToICH;
3208 uint64 uMissingInPart = GetTotalGapSizeInPart(partNumber);
3209 FillGap(PARTSIZE*partNumber,(PARTSIZE*partNumber+partRange));
3210 RemoveBlockFromList(PARTSIZE*partNumber,(PARTSIZE*partNumber + partRange));
3212 // remove from corrupted list
3213 EraseFirstValue(m_corrupted_list, partNumber);
3215 AddLogLineM(true, CFormat( _("ICH: Recovered corrupted part %i for %s -> Saved bytes: %s") )
3216 % partNumber
3217 % GetFileName()
3218 % CastItoXBytes(uMissingInPart));
3220 if (GetHashCount() == GetED2KPartHashCount() && !m_hashsetneeded) {
3221 if (status == PS_EMPTY) {
3222 // Successfully recovered part, make it available for sharing
3223 SetStatus(PS_READY);
3224 if (theApp->IsRunning()) // may be called during shutdown!
3225 theApp->sharedfiles->SafeAddKFile(this);
3230 // Any parts other than last must be full size
3231 partRange = PARTSIZE - 1;
3234 // Update met file
3235 SavePartFile();
3237 if (theApp->IsRunning()) { // may be called during shutdown!
3238 // Is this file finished ?
3239 if (m_gaplist.empty()) {
3240 CompleteFile(false);
3246 void CPartFile::UpdateFileRatingCommentAvail()
3248 bool prevComment = m_hasComment;
3249 int prevRating = m_iUserRating;
3251 m_hasComment = false;
3252 m_iUserRating = 0;
3253 int ratingCount = 0;
3255 SourceSet::iterator it = m_SrcList.begin();
3256 for (; it != m_SrcList.end(); ++it) {
3257 CUpDownClient* cur_src = *it;
3259 if (!cur_src->GetFileComment().IsEmpty()) {
3260 if (thePrefs::IsCommentFiltered(cur_src->GetFileComment())) {
3261 continue;
3263 m_hasComment = true;
3266 uint8 rating = cur_src->GetFileRating();
3267 if (rating) {
3268 wxASSERT(rating <= 5);
3270 ratingCount++;
3271 m_iUserRating += rating;
3275 if (ratingCount) {
3276 m_iUserRating /= ratingCount;
3277 wxASSERT(m_iUserRating > 0 && m_iUserRating <= 5);
3280 if ((prevComment != m_hasComment) || (prevRating != m_iUserRating)) {
3281 UpdateDisplayedInfo();
3286 void CPartFile::SetCategory(uint8 cat)
3288 wxASSERT( cat < theApp->glob_prefs->GetCatCount() );
3290 m_category = cat;
3291 SavePartFile();
3294 bool CPartFile::RemoveSource(CUpDownClient* toremove, bool updatewindow, bool bDoStatsUpdate)
3296 wxASSERT( toremove );
3298 bool result = theApp->downloadqueue->RemoveSource( toremove, updatewindow, bDoStatsUpdate );
3300 // Check if the client should be deleted, but not if the client is already dying
3301 if ( !toremove->GetSocket() && !toremove->HasBeenDeleted() ) {
3302 if ( toremove->Disconnected(wxT("RemoveSource - purged")) ) {
3303 toremove->Safe_Delete();
3307 return result;
3310 void CPartFile::AddDownloadingSource(CUpDownClient* client)
3312 CClientPtrList::iterator it =
3313 std::find(m_downloadingSourcesList.begin(), m_downloadingSourcesList.end(), client);
3314 if (it == m_downloadingSourcesList.end()) {
3315 m_downloadingSourcesList.push_back(client);
3320 void CPartFile::RemoveDownloadingSource(CUpDownClient* client)
3322 CClientPtrList::iterator it =
3323 std::find(m_downloadingSourcesList.begin(), m_downloadingSourcesList.end(), client);
3324 if (it != m_downloadingSourcesList.end()) {
3325 m_downloadingSourcesList.erase(it);
3330 void CPartFile::SetPartFileStatus(uint8 newstatus)
3332 status=newstatus;
3334 if (thePrefs::GetAllcatType()) {
3335 Notify_DownloadCtrlUpdateItem(this);
3338 Notify_DownloadCtrlSort();
3342 uint64 CPartFile::GetNeededSpace()
3344 try {
3345 uint64 length = m_hpartfile.GetLength();
3347 if (length > GetFileSize()) {
3348 return 0; // Shouldn't happen, but just in case
3351 return GetFileSize() - length;
3352 } catch (const CIOFailureException& e) {
3353 AddDebugLogLineM(true, logPartFile,
3354 CFormat(wxT("Error while retrieving file-length (%s): %s"))
3355 % m_fullname.RemoveExt() % e.what());
3356 SetPartFileStatus(PS_ERROR);
3357 return 0;
3361 void CPartFile::SetStatus(uint8 in)
3363 wxASSERT( in != PS_PAUSED && in != PS_INSUFFICIENT );
3365 status = in;
3367 if (theApp->IsRunning()) {
3368 UpdateDisplayedInfo( true );
3370 if ( thePrefs::ShowCatTabInfos() ) {
3371 Notify_ShowUpdateCatTabTitles();
3377 uint64 CPartFile::GetTotalGapSizeInRange(uint64 uRangeStart, uint64 uRangeEnd) const
3379 uint64 uTotalGapSize = 0;
3381 if (uRangeEnd >= GetFileSize()) {
3382 uRangeEnd = GetFileSize() - 1;
3385 std::list<Gap_Struct*>::const_iterator it = m_gaplist.begin();
3386 for (; it != m_gaplist.end(); ++it) {
3387 const Gap_Struct* pGap = *it;
3389 if (pGap->start < uRangeStart && pGap->end > uRangeEnd) {
3390 uTotalGapSize += uRangeEnd - uRangeStart + 1;
3391 break;
3394 if (pGap->start >= uRangeStart && pGap->start <= uRangeEnd) {
3395 uint64 uEnd = (pGap->end > uRangeEnd) ? uRangeEnd : pGap->end;
3396 uTotalGapSize += uEnd - pGap->start + 1;
3397 } else if (pGap->end >= uRangeStart && pGap->end <= uRangeEnd) {
3398 uTotalGapSize += pGap->end - uRangeStart + 1;
3402 wxASSERT( uTotalGapSize <= uRangeEnd - uRangeStart + 1 );
3404 return uTotalGapSize;
3407 uint64 CPartFile::GetTotalGapSizeInPart(uint32 uPart) const
3409 uint64 uRangeStart = uPart * PARTSIZE;
3410 uint64 uRangeEnd = uRangeStart + PARTSIZE - 1;
3411 if (uRangeEnd >= GetFileSize()) {
3412 uRangeEnd = GetFileSize();
3414 return GetTotalGapSizeInRange(uRangeStart, uRangeEnd);
3418 void CPartFile::RequestAICHRecovery(uint16 nPart)
3421 if ( !m_pAICHHashSet->HasValidMasterHash() ||
3422 (m_pAICHHashSet->GetStatus() != AICH_TRUSTED && m_pAICHHashSet->GetStatus() != AICH_VERIFIED)){
3423 AddDebugLogLineM( false, logAICHRecovery, wxT("Unable to request AICH Recoverydata because we have no trusted Masterhash") );
3424 return;
3426 if (GetFileSize() <= EMBLOCKSIZE || GetFileSize() - PARTSIZE*nPart <= EMBLOCKSIZE)
3427 return;
3428 if (CAICHHashSet::IsClientRequestPending(this, nPart)){
3429 AddDebugLogLineM( false, logAICHRecovery, wxT("RequestAICHRecovery: Already a request for this part pending"));
3430 return;
3433 // first check if we have already the recoverydata, no need to rerequest it then
3434 if (m_pAICHHashSet->IsPartDataAvailable(nPart*PARTSIZE)){
3435 AddDebugLogLineM( false, logAICHRecovery, wxT("Found PartRecoveryData in memory"));
3436 AICHRecoveryDataAvailable(nPart);
3437 return;
3440 wxASSERT( nPart < GetPartCount() );
3441 // find some random client which support AICH to ask for the blocks
3442 // first lets see how many we have at all, we prefer high id very much
3443 uint32 cAICHClients = 0;
3444 uint32 cAICHLowIDClients = 0;
3445 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it) {
3446 CUpDownClient* pCurClient = *(it);
3447 if ( pCurClient->IsSupportingAICH() &&
3448 pCurClient->GetReqFileAICHHash() != NULL &&
3449 !pCurClient->IsAICHReqPending()
3450 && (*pCurClient->GetReqFileAICHHash()) == m_pAICHHashSet->GetMasterHash())
3452 if (pCurClient->HasLowID()) {
3453 ++cAICHLowIDClients;
3454 } else {
3455 ++cAICHClients;
3459 if ((cAICHClients | cAICHLowIDClients) == 0){
3460 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"));
3461 return;
3463 uint32 nSeclectedClient;
3464 if (cAICHClients > 0) {
3465 nSeclectedClient = (rand() % cAICHClients) + 1;
3466 } else {
3467 nSeclectedClient = (rand() % cAICHLowIDClients) + 1;
3469 CUpDownClient* pClient = NULL;
3470 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it) {
3471 CUpDownClient* pCurClient = *(it);
3472 if (pCurClient->IsSupportingAICH() && pCurClient->GetReqFileAICHHash() != NULL && !pCurClient->IsAICHReqPending()
3473 && (*pCurClient->GetReqFileAICHHash()) == m_pAICHHashSet->GetMasterHash())
3475 if (cAICHClients > 0){
3476 if (!pCurClient->HasLowID())
3477 nSeclectedClient--;
3479 else{
3480 wxASSERT( pCurClient->HasLowID());
3481 nSeclectedClient--;
3483 if (nSeclectedClient == 0){
3484 pClient = pCurClient;
3485 break;
3489 if (pClient == NULL){
3490 wxASSERT( false );
3491 return;
3494 AddDebugLogLineM( false, logAICHRecovery, CFormat( wxT("Requesting AICH Hash (%s) form client %s") ) % ( cAICHClients ? wxT("HighId") : wxT("LowID") ) % pClient->GetClientFullInfo() );
3495 pClient->SendAICHRequest(this, nPart);
3500 void CPartFile::AICHRecoveryDataAvailable(uint16 nPart)
3502 if (GetPartCount() < nPart){
3503 wxASSERT( false );
3504 return;
3507 FlushBuffer(true, true, true);
3509 uint64 length = PARTSIZE;
3511 try {
3512 if ((unsigned)(PARTSIZE * (nPart + 1)) > m_hpartfile.GetLength()){
3513 length = (m_hpartfile.GetLength() - (PARTSIZE * nPart));
3514 wxASSERT( length <= PARTSIZE );
3516 } catch (const CIOFailureException& e) {
3517 AddDebugLogLineM(true, logPartFile,
3518 CFormat(wxT("Error while retrieving file-length (%s): %s"))
3519 % m_fullname.RemoveExt() % e.what());
3520 SetPartFileStatus(PS_ERROR);
3521 return;
3524 // if the part was already ok, it would now be complete
3525 if (IsComplete(nPart*PARTSIZE, ((nPart*PARTSIZE)+length)-1)){
3526 AddDebugLogLineM( false, logAICHRecovery,
3527 wxString::Format( wxT("Processing AICH Recovery data: The part (%u) is already complete, canceling"), nPart ) );
3528 return;
3533 CAICHHashTree* pVerifiedHash = m_pAICHHashSet->m_pHashTree.FindHash(nPart*PARTSIZE, length);
3534 if (pVerifiedHash == NULL || !pVerifiedHash->GetHashValid()){
3535 AddDebugLogLineM( true, logAICHRecovery, wxT("Processing AICH Recovery data: Unable to get verified hash from hashset (should never happen)") );
3536 wxASSERT( false );
3537 return;
3539 CAICHHashTree htOurHash(pVerifiedHash->GetNDataSize(), pVerifiedHash->GetIsLeftBranch(), pVerifiedHash->GetNBaseSize());
3540 try {
3541 m_hpartfile.Seek(PARTSIZE * nPart,wxFromStart);
3542 CreateHashFromFile(&m_hpartfile,length, NULL, &htOurHash);
3543 } catch (const CIOFailureException& e) {
3544 AddDebugLogLineM(true, logAICHRecovery,
3545 CFormat(wxT("IO failure while hashing part-file '%s': %s"))
3546 % m_hpartfile.GetFilePath() % e.what());
3547 SetPartFileStatus(PS_ERROR);
3548 return;
3551 if (!htOurHash.GetHashValid()){
3552 AddDebugLogLineM( false, logAICHRecovery, wxT("Processing AICH Recovery data: Failed to retrieve AICH Hashset of corrupt part") );
3553 wxASSERT( false );
3554 return;
3557 // now compare the hash we just did, to the verified hash and readd all blocks which are ok
3558 uint32 nRecovered = 0;
3559 for (uint32 pos = 0; pos < length; pos += EMBLOCKSIZE){
3560 const uint32 nBlockSize = min<uint32>(EMBLOCKSIZE, length - pos);
3561 CAICHHashTree* pVerifiedBlock = pVerifiedHash->FindHash(pos, nBlockSize);
3562 CAICHHashTree* pOurBlock = htOurHash.FindHash(pos, nBlockSize);
3563 if ( pVerifiedBlock == NULL || pOurBlock == NULL || !pVerifiedBlock->GetHashValid() || !pOurBlock->GetHashValid()){
3564 wxASSERT( false );
3565 continue;
3567 if (pOurBlock->GetHash() == pVerifiedBlock->GetHash()){
3568 FillGap(PARTSIZE*nPart+pos, PARTSIZE*nPart + pos + (nBlockSize-1));
3569 RemoveBlockFromList(PARTSIZE*nPart, PARTSIZE*nPart + (nBlockSize-1));
3570 nRecovered += nBlockSize;
3574 // ok now some sanity checks
3575 if (IsComplete(nPart*PARTSIZE, ((nPart*PARTSIZE)+length)-1)){
3576 // this is a bad, but it could probably happen under some rare circumstances
3577 // make sure that MD4 agrres to this fact too
3578 if (!HashSinglePart(nPart)){
3579 AddDebugLogLineM( false, logAICHRecovery,
3580 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));
3581 // now we are fu... unhappy
3582 m_pAICHHashSet->SetStatus(AICH_ERROR);
3583 AddGap(PARTSIZE*nPart, ((nPart*PARTSIZE)+length)-1);
3584 wxASSERT( false );
3585 return;
3587 else{
3588 AddDebugLogLineM( false, logAICHRecovery, wxString::Format(
3589 wxT("Processing AICH Recovery data: The part (%u) got completed while recovering and MD4 agrees"), nPart) );
3590 // alrighty not so bad
3591 EraseFirstValue(m_corrupted_list, nPart);
3592 if (status == PS_EMPTY && theApp->IsRunning()){
3593 if (GetHashCount() == GetED2KPartHashCount() && !m_hashsetneeded){
3594 // Successfully recovered part, make it available for sharing
3595 SetStatus(PS_READY);
3596 theApp->sharedfiles->SafeAddKFile(this);
3600 if (theApp->IsRunning()){
3601 // Is this file finished?
3602 if (m_gaplist.empty()) {
3603 CompleteFile(false);
3607 } // end sanity check
3608 // Update met file
3609 SavePartFile();
3611 // make sure the user appreciates our great recovering work :P
3612 AddDebugLogLineM( true, logAICHRecovery, CFormat(
3613 wxT("AICH successfully recovered %s of %s from part %u for %s") )
3614 % CastItoXBytes(nRecovered)
3615 % CastItoXBytes(length)
3616 % nPart
3617 % GetFileName() );
3621 void CPartFile::ClientStateChanged( int oldState, int newState )
3623 if ( oldState == newState )
3624 return;
3626 // If the state is -1, then it's an entirely new item
3627 if ( oldState != -1 ) {
3628 // Was the old state a valid state?
3629 if ( oldState == DS_ONQUEUE || oldState == DS_DOWNLOADING ) {
3630 m_validSources--;
3631 } else {
3632 if ( oldState == DS_CONNECTED /* || oldState == DS_REMOTEQUEUEFULL */ ) {
3633 m_validSources--;
3636 m_notCurrentSources--;
3640 // If the state is -1, then the source is being removed
3641 if ( newState != -1 ) {
3642 // Was the old state a valid state?
3643 if ( newState == DS_ONQUEUE || newState == DS_DOWNLOADING ) {
3644 ++m_validSources;
3645 } else {
3646 if ( newState == DS_CONNECTED /* || newState == DS_REMOTEQUEUEFULL */ ) {
3647 ++m_validSources;
3650 ++m_notCurrentSources;
3656 bool CPartFile::AddSource( CUpDownClient* client )
3658 if (m_SrcList.insert( client ).second) {
3659 theStats::AddFoundSource();
3660 theStats::AddSourceOrigin(client->GetSourceFrom());
3661 return true;
3662 } else {
3663 return false;
3668 bool CPartFile::DelSource( CUpDownClient* client )
3670 if (m_SrcList.erase( client )) {
3671 theStats::RemoveSourceOrigin(client->GetSourceFrom());
3672 theStats::RemoveFoundSource();
3673 return true;
3674 } else {
3675 return false;
3680 void CPartFile::UpdatePartsFrequency( CUpDownClient* client, bool increment )
3682 const BitVector& freq = client->GetPartStatus();
3684 if ( m_SrcpartFrequency.size() != GetPartCount() ) {
3685 m_SrcpartFrequency.clear();
3686 m_SrcpartFrequency.insert(m_SrcpartFrequency.begin(), GetPartCount(), 0);
3688 if ( !increment ) {
3689 return;
3693 unsigned int size = freq.size();
3694 if ( size != m_SrcpartFrequency.size() ) {
3695 return;
3698 if ( increment ) {
3699 for ( unsigned int i = 0; i < size; i++ ) {
3700 if ( freq[i] ) {
3701 m_SrcpartFrequency[i]++;
3704 } else {
3705 for ( unsigned int i = 0; i < size; i++ ) {
3706 if ( freq[i] ) {
3707 m_SrcpartFrequency[i]--;
3713 const FileRatingList &CPartFile::GetRatingAndComments()
3715 m_FileRatingList.clear();
3716 // This can be pre-processed, but is it worth the CPU?
3717 CPartFile::SourceSet::iterator it = m_SrcList.begin();
3718 for ( ; it != m_SrcList.end(); ++it ) {
3719 CUpDownClient *cur_src = *it;
3720 if (cur_src->GetFileComment().Length()>0 || cur_src->GetFileRating()>0) {
3721 // AddDebugLogLineM(false, logPartFile, wxString(wxT("found a comment for ")) << GetFileName());
3722 m_FileRatingList.push_back(SFileRating(*cur_src));
3726 return m_FileRatingList;
3729 #else // CLIENT_GUI
3731 CPartFile::CPartFile(CEC_PartFile_Tag *tag)
3733 Init();
3735 SetFileName(CPath(tag->FileName()));
3736 m_abyFileHash = tag->ID();
3737 SetFileSize(tag->SizeFull());
3738 m_partmetfilename = CPath(tag->PartMetName());
3739 transferred = tag->SizeXfer();
3740 percentcompleted = (100.0*completedsize) / GetFileSize();
3741 completedsize = tag->SizeDone();
3743 m_category = tag->FileCat();
3745 m_iPartCount = ((uint64)GetFileSize() + (PARTSIZE - 1)) / PARTSIZE;
3746 m_SrcpartFrequency.insert(m_SrcpartFrequency.end(), m_iPartCount, 0);
3747 m_iDownPriority = tag->Prio();
3748 if ( m_iDownPriority >= 10 ) {
3749 m_iDownPriority-= 10;
3750 m_bAutoDownPriority = true;
3751 } else {
3752 m_bAutoDownPriority = false;
3754 // FIXME: !
3755 m_category = 0;
3757 m_source_count = 0;
3758 m_a4af_source_count = 0;
3762 * Remote gui specific code
3764 CPartFile::~CPartFile()
3768 const FileRatingList &CPartFile::GetRatingAndComments()
3770 return m_FileRatingList;
3772 #endif // !CLIENT_GUI
3775 void CPartFile::UpdateDisplayedInfo(bool force)
3777 uint32 curTick = ::GetTickCount();
3778 m_CommentUpdated = true;
3780 // Wait 1.5s between each redraw
3781 if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE ) {
3782 Notify_DownloadCtrlUpdateItem(this);
3783 m_lastRefreshedDLDisplay = curTick;
3789 void CPartFile::Init()
3791 m_showSources = false;
3792 m_lastsearchtime = 0;
3793 lastpurgetime = ::GetTickCount();
3794 m_paused = false;
3795 m_stopped = false;
3796 m_insufficient = false;
3798 status = PS_EMPTY;
3800 transferred = 0;
3801 m_iLastPausePurge = time(NULL);
3803 if(thePrefs::GetNewAutoDown()) {
3804 m_iDownPriority = PR_HIGH;
3805 m_bAutoDownPriority = true;
3806 } else {
3807 m_iDownPriority = PR_NORMAL;
3808 m_bAutoDownPriority = false;
3811 memset(m_anStates,0,sizeof(m_anStates));
3813 transferingsrc = 0; // new
3815 kBpsDown = 0.0;
3817 m_CommentUpdated = false;
3818 m_hashsetneeded = true;
3819 m_count = 0;
3820 percentcompleted = 0;
3821 completedsize=0;
3822 m_bPreviewing = false;
3823 lastseencomplete = 0;
3824 m_availablePartsCount=0;
3825 m_ClientSrcAnswered = 0;
3826 m_LastNoNeededCheck = 0;
3827 m_iRating = 0;
3828 m_nTotalBufferData = 0;
3829 m_nLastBufferFlushTime = 0;
3830 m_bPercentUpdated = false;
3831 m_bRecoveringArchive = false;
3832 m_iGainDueToCompression = 0;
3833 m_iLostDueToCorruption = 0;
3834 m_iTotalPacketsSavedDueToICH = 0;
3835 m_category = 0;
3836 m_lastRefreshedDLDisplay = 0;
3837 m_nDlActiveTime = 0;
3838 m_tActivated = 0;
3839 m_is_A4AF_auto = false;
3840 m_localSrcReqQueued = false;
3841 m_nCompleteSourcesTime = time(NULL);
3842 m_nCompleteSourcesCount = 0;
3843 m_nCompleteSourcesCountLo = 0;
3844 m_nCompleteSourcesCountHi = 0;
3846 m_validSources = 0;
3847 m_notCurrentSources = 0;
3849 // Kad
3850 m_LastSearchTimeKad = 0;
3851 m_TotalSearchesKad = 0;
3855 wxString CPartFile::getPartfileStatus() const
3858 wxString mybuffer;
3860 if ((status == PS_HASHING) || (status == PS_WAITINGFORHASH)) {
3861 mybuffer=_("Hashing");
3862 } else if (status == PS_ALLOCATING) {
3863 mybuffer = _("Allocating");
3864 } else {
3865 switch (GetStatus()) {
3866 case PS_COMPLETING:
3867 mybuffer=_("Completing");
3868 break;
3869 case PS_COMPLETE:
3870 mybuffer=_("Complete");
3871 break;
3872 case PS_PAUSED:
3873 mybuffer=_("Paused");
3874 break;
3875 case PS_ERROR:
3876 mybuffer=_("Erroneous");
3877 break;
3878 case PS_INSUFFICIENT:
3879 mybuffer = _("Insufficient disk space");
3880 break;
3881 default:
3882 if (GetTransferingSrcCount()>0) {
3883 mybuffer=_("Downloading");
3884 } else {
3885 mybuffer=_("Waiting");
3887 break;
3889 if (m_stopped && (GetStatus()!=PS_COMPLETE)) {
3890 mybuffer=_("Stopped");
3894 return mybuffer;
3897 int CPartFile::getPartfileStatusRang() const
3900 int tempstatus=0;
3901 if (GetTransferingSrcCount()==0) tempstatus=1;
3902 switch (GetStatus()) {
3903 case PS_HASHING:
3904 case PS_WAITINGFORHASH:
3905 tempstatus=3;
3906 break;
3907 case PS_COMPLETING:
3908 tempstatus=4;
3909 break;
3910 case PS_COMPLETE:
3911 tempstatus=5;
3912 break;
3913 case PS_PAUSED:
3914 tempstatus=2;
3915 break;
3916 case PS_ERROR:
3917 tempstatus=6;
3918 break;
3920 return tempstatus;
3924 wxString CPartFile::GetFeedback() const
3926 wxString retval = CKnownFile::GetFeedback();
3927 if (GetStatus() != PS_COMPLETE) {
3928 retval += wxString(_("Downloaded")) + wxT(": ") + CastItoXBytes(GetCompletedSize()) + wxString::Format(wxT(" (%.2f%%)\n"), GetPercentCompleted())
3929 + _("Sources") + CFormat(wxT(": %u\n")) % GetSourceCount();
3931 return retval + _("Status") + wxT(": ") + getPartfileStatus() + wxT("\n");
3935 sint32 CPartFile::getTimeRemaining() const
3937 if (GetKBpsDown() < 0.001)
3938 return -1;
3939 else
3940 return((GetFileSize()-GetCompletedSize()) / ((int)(GetKBpsDown()*1024.0)));
3943 bool CPartFile::PreviewAvailable()
3945 FileType type = GetFiletype(GetFileName());
3947 return (((type == ftVideo) || (type == ftAudio)) && IsComplete(0, 256*1024));
3950 bool CPartFile::CheckShowItemInGivenCat(int inCategory)
3952 // easy normal cases
3953 bool IsInCat;
3954 bool IsNotFiltered = true;
3956 IsInCat = ((inCategory==0) || (inCategory>0 && inCategory==GetCategory()));
3958 switch (thePrefs::GetAllcatType()) {
3959 case 1:
3960 IsNotFiltered = GetCategory() == 0 || inCategory > 0;
3961 break;
3962 case 2:
3963 IsNotFiltered = IsPartFile();
3964 break;
3965 case 3:
3966 IsNotFiltered = !IsPartFile();
3967 break;
3968 case 4:
3969 IsNotFiltered =
3970 (GetStatus() == PS_READY || GetStatus() == PS_EMPTY) &&
3971 GetTransferingSrcCount() == 0;
3972 break;
3973 case 5:
3974 IsNotFiltered =
3975 (GetStatus() == PS_READY || GetStatus()==PS_EMPTY) &&
3976 GetTransferingSrcCount() > 0;
3977 break;
3978 case 6:
3979 IsNotFiltered = GetStatus() == PS_ERROR;
3980 break;
3981 case 7:
3982 IsNotFiltered = GetStatus() == PS_PAUSED && !IsStopped();
3983 break;
3984 case 8:
3985 IsNotFiltered = IsStopped();
3986 break;
3987 case 9:
3988 IsNotFiltered = GetFiletype(GetFileName()) == ftVideo;
3989 break;
3990 case 10:
3991 IsNotFiltered = GetFiletype(GetFileName()) == ftAudio;
3992 break;
3993 case 11:
3994 IsNotFiltered = GetFiletype(GetFileName()) == ftArchive;
3995 break;
3996 case 12:
3997 IsNotFiltered = GetFiletype(GetFileName()) == ftCDImage;
3998 break;
3999 case 13:
4000 IsNotFiltered = GetFiletype(GetFileName()) == ftPicture;
4001 break;
4002 case 14:
4003 IsNotFiltered = GetFiletype(GetFileName()) == ftText;
4004 break;
4005 case 15:
4006 IsNotFiltered = !IsStopped() && GetStatus() != PS_PAUSED;
4007 break;
4010 return IsNotFiltered && IsInCat;
4013 bool CPartFile::IsComplete(uint64 start, uint64 end)
4015 if (end >= GetFileSize()) {
4016 end = GetFileSize()-1;
4019 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
4020 for (; it != m_gaplist.end(); ++it) {
4021 Gap_Struct* cur_gap = *it;
4022 if ((cur_gap->start >= start && cur_gap->end <= end)||(cur_gap->start >= start
4023 && cur_gap->start <= end)||(cur_gap->end <= end && cur_gap->end >= start)
4024 ||(start >= cur_gap->start && end <= cur_gap->end)) {
4025 return false;
4028 return true;
4032 void CPartFile::SetActive(bool bActive)
4034 time_t tNow = time(NULL);
4035 if (bActive) {
4036 if (theApp->IsConnected()) {
4037 if (m_tActivated == 0) {
4038 m_tActivated = tNow;
4041 } else {
4042 if (m_tActivated != 0) {
4043 m_nDlActiveTime += tNow - m_tActivated;
4044 m_tActivated = 0;
4050 uint32 CPartFile::GetDlActiveTime() const
4052 uint32 nDlActiveTime = m_nDlActiveTime;
4053 if (m_tActivated != 0) {
4054 nDlActiveTime += time(NULL) - m_tActivated;
4056 return nDlActiveTime;
4059 #ifdef CLIENT_GUI
4061 // remote Gui has no adjustment factor transferred yet, so use raw rate
4062 float CPartFile::GetKBpsDown() const
4064 return kBpsDown;
4067 #else
4069 float CPartFile::GetKBpsDown() const
4071 return kBpsDown * theStats::GetDownloadRateAdjust();
4075 uint8 CPartFile::GetStatus(bool ignorepause) const
4077 if ( (!m_paused && !m_insufficient) ||
4078 status == PS_ERROR ||
4079 status == PS_COMPLETING ||
4080 status == PS_COMPLETE ||
4081 ignorepause) {
4082 return status;
4083 } else if ( m_insufficient ) {
4084 return PS_INSUFFICIENT;
4085 } else {
4086 return PS_PAUSED;
4090 void CPartFile::AddDeadSource(const CUpDownClient* client)
4092 m_deadSources.AddDeadSource( client );
4096 bool CPartFile::IsDeadSource(const CUpDownClient* client)
4098 return m_deadSources.IsDeadSource( client );
4101 void CPartFile::SetFileName(const CPath& fileName)
4103 CKnownFile* pFile = theApp->sharedfiles->GetFileByID(GetFileHash());
4105 bool is_shared = (pFile && pFile == this);
4107 if (is_shared) {
4108 // The file is shared, we must clear the search keywords so we don't
4109 // publish the old name anymore.
4110 theApp->sharedfiles->RemoveKeywords(this);
4113 CKnownFile::SetFileName(fileName);
4115 if (is_shared) {
4116 // And of course, we must advertise the new name if the file is shared.
4117 theApp->sharedfiles->AddKeywords(this);
4120 UpdateDisplayedInfo(true);
4124 uint16 CPartFile::GetMaxSources() const
4126 // This is just like this, while we don't import the private max sources per file
4127 return thePrefs::GetMaxSourcePerFile();
4131 uint16 CPartFile::GetMaxSourcePerFileSoft() const
4133 unsigned int temp = ((unsigned int)GetMaxSources() * 9L) / 10;
4134 if (temp > MAX_SOURCES_FILE_SOFT) {
4135 return MAX_SOURCES_FILE_SOFT;
4137 return temp;
4140 uint16 CPartFile::GetMaxSourcePerFileUDP() const
4142 unsigned int temp = ((unsigned int)GetMaxSources() * 3L) / 4;
4143 if (temp > MAX_SOURCES_FILE_UDP) {
4144 return MAX_SOURCES_FILE_UDP;
4146 return temp;
4149 #define DROP_FACTOR 2
4151 CUpDownClient* CPartFile::GetSlowerDownloadingClient(uint32 speed, CUpDownClient* caller) {
4152 // printf("Start slower source calculation\n");
4153 for( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ) {
4154 CUpDownClient* cur_src = *it++;
4155 if ((cur_src->GetDownloadState() == DS_DOWNLOADING) && (cur_src != caller)) {
4156 uint32 factored_bytes_per_second = static_cast<uint32>(
4157 (cur_src->GetKBpsDown() * 1024) * DROP_FACTOR);
4158 if ( factored_bytes_per_second< speed) {
4159 // printf("Selecting source %p to drop: %d < %d\n", cur_src, factored_bytes_per_second, speed);
4160 // printf("End slower source calculation\n");
4161 return cur_src;
4162 } else {
4163 // printf("Not selecting source %p to drop: %d > %d\n", cur_src, factored_bytes_per_second, speed);
4167 // printf("End slower source calculation\n");
4168 return NULL;
4171 void CPartFile::AllocationFinished()
4173 if (!m_hpartfile.Open(GetFullName().RemoveExt(), CFile::read_write)) {
4174 AddLogLineM(false, CFormat(_("ERROR: Failed to open partfile '%s'")) % GetFullName());
4175 SetPartFileStatus(PS_ERROR);
4179 #endif
4180 // File_checked_for_headers