Upstream tarball 20080603
[amule.git] / src / PartFile.cpp
blob15ca91841c84c5d65a6d9d134b0a1bd06902bd41
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <wx/wx.h>
28 #include "PartFile.h" // Interface declarations.
30 #ifdef HAVE_CONFIG_H
31 #include "config.h" // Needed for VERSION
32 #endif
34 #include <protocol/kad/Constants.h>
35 #include <protocol/ed2k/Client2Client/TCP.h>
36 #include <protocol/Protocols.h>
37 #include <common/DataFileVersion.h>
38 #include <common/Constants.h>
39 #include <tags/FileTags.h>
41 #include <wx/utils.h>
42 #include <wx/tokenzr.h> // Needed for wxStringTokenizer
44 #include "KnownFileList.h" // Needed for CKnownFileList
45 #include "UploadQueue.h" // Needed for CFileHash
46 #include "IPFilter.h" // Needed for CIPFilter
47 #include "Server.h" // Needed for CServer
48 #include "ServerConnect.h" // Needed for CServerConnect
49 #include "updownclient.h" // Needed for CUpDownClient
50 #include "MemFile.h" // Needed for CMemFile
51 #include "Preferences.h" // Needed for CPreferences
52 #include "DownloadQueue.h" // Needed for CDownloadQueue
53 #include "amule.h" // Needed for theApp
54 #include "ED2KLink.h" // Needed for CED2KLink
55 #include "Packet.h" // Needed for CTag
56 #include "SearchList.h" // Needed for CSearchFile
57 #include "ClientList.h" // Needed for clientlist
58 #include "Statistics.h" // Needed for theStats
59 #include "Logger.h"
60 #include <common/Format.h> // Needed for CFormat
61 #include <common/FileFunctions.h> // Needed for GetLastModificationTime
62 #include "ThreadTasks.h" // Needed for CHashingTask/CCompletionTask/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 fileversion: %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 file 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( uint32 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 (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 // Create copy of data as new buffer
2983 byte *buffer = new byte[lenData];
2984 memcpy(buffer, data, lenData);
2986 // Create a new buffered queue entry
2987 PartFileBufferedData *item = new PartFileBufferedData;
2988 item->data = buffer;
2989 item->start = start;
2990 item->end = end;
2991 item->block = block;
2993 // Add to the queue in the correct position (most likely the end)
2994 bool added = false;
2996 std::list<PartFileBufferedData*>::iterator it = m_BufferedData_list.begin();
2997 for (; it != m_BufferedData_list.end(); ++it) {
2998 PartFileBufferedData* queueItem = *it;
3000 if (item->end <= queueItem->end) {
3001 if (it != m_BufferedData_list.begin()) {
3002 added = true;
3004 m_BufferedData_list.insert(--it, item);
3007 break;
3011 if (!added) {
3012 m_BufferedData_list.push_front(item);
3015 // Increment buffer size marker
3016 m_nTotalBufferData += lenData;
3018 // Mark this small section of the file as filled
3019 FillGap(item->start, item->end);
3021 // Update the flushed mark on the requested block
3022 // The loop here is unfortunate but necessary to detect deleted blocks.
3024 std::list<Requested_Block_Struct*>::iterator it2 = m_requestedblocks_list.begin();
3025 for (; it2 != m_requestedblocks_list.end(); ++it2) {
3026 if (*it2 == item->block) {
3027 item->block->transferred += lenData;
3031 if (m_gaplist.empty()) {
3032 FlushBuffer(true);
3035 // Return the length of data written to the buffer
3036 return lenData;
3039 void CPartFile::FlushBuffer(bool /*forcewait*/, bool bForceICH, bool bNoAICH)
3041 m_nLastBufferFlushTime = GetTickCount();
3043 if (m_BufferedData_list.empty()) {
3044 return;
3048 uint32 partCount = GetPartCount();
3049 std::vector<bool> changedPart(partCount);
3051 // Remember which parts need to be checked at the end of the flush
3052 for ( uint32 i = 0; i < partCount; ++i ) {
3053 changedPart[ i ] = false;
3057 // Ensure file is big enough to write data to (the last item will be the furthest from the start)
3058 uint32 newData = 0;
3060 std::list<PartFileBufferedData*>::iterator it = m_BufferedData_list.begin();
3061 for (; it != m_BufferedData_list.end(); ++it) {
3062 PartFileBufferedData* item = *it;
3063 wxASSERT((item->end - item->start) < 0xFFFFFFFF);
3064 newData += (uint32) (item->end - item->start + 1);
3067 if ( !CheckFreeDiskSpace( newData ) ) {
3068 // Not enough free space to write the last item, bail
3069 AddLogLineM(true, CFormat( _("WARNING: Not enough free disk-space! Pausing file: %s") ) % GetFileName());
3071 PauseFile( true );
3072 return;
3075 // Loop through queue
3076 while ( !m_BufferedData_list.empty() ) {
3077 // Get top item and remove it from the queue
3078 PartFileBufferedData* item = m_BufferedData_list.front();
3079 m_BufferedData_list.pop_front();
3081 // This is needed a few times
3082 wxASSERT((item->end - item->start) < 0xFFFFFFFF);
3083 uint32 lenData = (uint32)(item->end - item->start + 1);
3085 // SLUGFILLER: SafeHash - could be more than one part
3086 for (uint32 curpart = (item->start/PARTSIZE); curpart <= (item->end/PARTSIZE); ++curpart) {
3087 wxASSERT(curpart < partCount);
3088 changedPart[curpart] = true;
3090 // SLUGFILLER: SafeHash
3092 // Go to the correct position in file and write block of data
3093 try {
3094 m_hpartfile.Seek(item->start);
3095 m_hpartfile.Write(item->data, lenData);
3096 } catch (const CIOFailureException& e) {
3097 AddDebugLogLineM(true, logPartFile, wxT("Error while saving part-file: ") + e.what());
3098 SetPartFileStatus(PS_ERROR);
3101 // Decrease buffer size
3102 m_nTotalBufferData -= lenData;
3104 // Release memory used by this item
3105 delete [] item->data;
3106 delete item;
3110 // Update last-changed date
3111 m_lastDateChanged = wxDateTime::GetTimeNow();
3113 try {
3114 // Partfile should never be too large
3115 if (m_hpartfile.GetLength() > GetFileSize()) {
3116 // it's "last chance" correction. the real bugfix has to be applied 'somewhere' else
3117 m_hpartfile.SetLength(GetFileSize());
3119 } catch (const CIOFailureException& e) {
3120 AddDebugLogLineM(true, logPartFile,
3121 CFormat(wxT("Error while truncating part-file (%s): %s"))
3122 % m_fullname.RemoveExt() % e.what());
3123 SetPartFileStatus(PS_ERROR);
3128 // Check each part of the file
3129 uint32 partRange = 0;
3130 try {
3131 uint64 curLength = m_hpartfile.GetLength();
3133 partRange = (uint32)((curLength % PARTSIZE > 0) ? ((curLength % PARTSIZE) - 1) : (PARTSIZE - 1));
3134 } catch (const CIOFailureException& e) {
3135 AddDebugLogLineM(true, logPartFile,
3136 CFormat(wxT("Error while accessing part-file (%s): %s"))
3137 % m_fullname.RemoveExt() % e.what());
3138 SetPartFileStatus(PS_ERROR);
3141 wxASSERT(partRange);
3142 for (int partNumber = partCount-1; partRange && partNumber >= 0; partNumber--) {
3143 if (changedPart[partNumber] == false) {
3144 // Any parts other than last must be full size
3145 partRange = PARTSIZE - 1;
3146 continue;
3149 // Is this 9MB part complete
3150 if (IsComplete(PARTSIZE * partNumber, (PARTSIZE * (partNumber + 1)) - 1)) {
3151 // Is part corrupt
3152 if (!HashSinglePart(partNumber)) {
3153 AddLogLineM(true, CFormat(
3154 _("Downloaded part %i is corrupt in file: %s") ) % partNumber % GetFileName() );
3155 AddGap(PARTSIZE*partNumber, (PARTSIZE*partNumber + partRange));
3156 // add part to corrupted list, if not already there
3157 if (!IsCorruptedPart(partNumber)) {
3158 m_corrupted_list.push_back(partNumber);
3160 // request AICH recovery data
3161 if (!bNoAICH) {
3162 RequestAICHRecovery((uint16)partNumber);
3164 // Reduce transferred amount by corrupt amount
3165 m_iLostDueToCorruption += (partRange + 1);
3166 } else {
3167 if (!m_hashsetneeded) {
3168 AddDebugLogLineM(false, logPartFile, CFormat(
3169 wxT("Finished part %u of '%s'")) % partNumber % GetFileName());
3172 // if this part was successfully completed (although ICH is active), remove from corrupted list
3173 EraseFirstValue(m_corrupted_list, partNumber);
3175 if (status == PS_EMPTY) {
3176 if (theApp->IsRunning()) { // may be called during shutdown!
3177 if (GetHashCount() == GetED2KPartHashCount() && !m_hashsetneeded) {
3178 // Successfully completed part, make it available for sharing
3179 SetStatus(PS_READY);
3180 theApp->sharedfiles->SafeAddKFile(this);
3185 } else if ( IsCorruptedPart(partNumber) && (thePrefs::IsICHEnabled() || bForceICH)) {
3186 // Try to recover with minimal loss
3187 if (HashSinglePart(partNumber)) {
3188 ++m_iTotalPacketsSavedDueToICH;
3190 uint64 uMissingInPart = GetTotalGapSizeInPart(partNumber);
3191 FillGap(PARTSIZE*partNumber,(PARTSIZE*partNumber+partRange));
3192 RemoveBlockFromList(PARTSIZE*partNumber,(PARTSIZE*partNumber + partRange));
3194 // remove from corrupted list
3195 EraseFirstValue(m_corrupted_list, partNumber);
3197 AddLogLineM(true, CFormat( _("ICH: Recovered corrupted part %i for %s -> Saved bytes: %s") )
3198 % partNumber
3199 % GetFileName()
3200 % CastItoXBytes(uMissingInPart));
3202 if (GetHashCount() == GetED2KPartHashCount() && !m_hashsetneeded) {
3203 if (status == PS_EMPTY) {
3204 // Successfully recovered part, make it available for sharing
3205 SetStatus(PS_READY);
3206 if (theApp->IsRunning()) // may be called during shutdown!
3207 theApp->sharedfiles->SafeAddKFile(this);
3212 // Any parts other than last must be full size
3213 partRange = PARTSIZE - 1;
3216 // Update met file
3217 SavePartFile();
3219 if (theApp->IsRunning()) { // may be called during shutdown!
3220 // Is this file finished ?
3221 if (m_gaplist.empty()) {
3222 CompleteFile(false);
3228 void CPartFile::UpdateFileRatingCommentAvail()
3230 bool prevComment = m_hasComment;
3231 int prevRating = m_iUserRating;
3233 m_hasComment = false;
3234 m_iUserRating = 0;
3235 int ratingCount = 0;
3237 SourceSet::iterator it = m_SrcList.begin();
3238 for (; it != m_SrcList.end(); ++it) {
3239 CUpDownClient* cur_src = *it;
3241 if (!cur_src->GetFileComment().IsEmpty()) {
3242 if (thePrefs::IsCommentFiltered(cur_src->GetFileComment())) {
3243 continue;
3245 m_hasComment = true;
3248 uint8 rating = cur_src->GetFileRating();
3249 if (rating) {
3250 wxASSERT(rating <= 5);
3252 ratingCount++;
3253 m_iUserRating += rating;
3257 if (ratingCount) {
3258 m_iUserRating /= ratingCount;
3259 wxASSERT(m_iUserRating > 0 && m_iUserRating <= 5);
3262 if ((prevComment != m_hasComment) || (prevRating != m_iUserRating)) {
3263 UpdateDisplayedInfo();
3268 void CPartFile::SetCategory(uint8 cat)
3270 wxASSERT( cat < theApp->glob_prefs->GetCatCount() );
3272 m_category = cat;
3273 SavePartFile();
3276 bool CPartFile::RemoveSource(CUpDownClient* toremove, bool updatewindow, bool bDoStatsUpdate)
3278 wxASSERT( toremove );
3280 bool result = theApp->downloadqueue->RemoveSource( toremove, updatewindow, bDoStatsUpdate );
3282 // Check if the client should be deleted, but not if the client is already dying
3283 if ( !toremove->GetSocket() && !toremove->HasBeenDeleted() ) {
3284 if ( toremove->Disconnected(wxT("RemoveSource - purged")) ) {
3285 toremove->Safe_Delete();
3289 return result;
3292 void CPartFile::AddDownloadingSource(CUpDownClient* client)
3294 CClientPtrList::iterator it =
3295 std::find(m_downloadingSourcesList.begin(), m_downloadingSourcesList.end(), client);
3296 if (it == m_downloadingSourcesList.end()) {
3297 m_downloadingSourcesList.push_back(client);
3302 void CPartFile::RemoveDownloadingSource(CUpDownClient* client)
3304 CClientPtrList::iterator it =
3305 std::find(m_downloadingSourcesList.begin(), m_downloadingSourcesList.end(), client);
3306 if (it != m_downloadingSourcesList.end()) {
3307 m_downloadingSourcesList.erase(it);
3312 void CPartFile::SetPartFileStatus(uint8 newstatus)
3314 status=newstatus;
3316 if (thePrefs::GetAllcatType()) {
3317 Notify_DownloadCtrlUpdateItem(this);
3320 Notify_DownloadCtrlSort();
3324 uint64 CPartFile::GetNeededSpace()
3326 try {
3327 uint64 length = m_hpartfile.GetLength();
3329 if (length > GetFileSize()) {
3330 return 0; // Shouldn't happen, but just in case
3333 return GetFileSize() - length;
3334 } catch (const CIOFailureException& e) {
3335 AddDebugLogLineM(true, logPartFile,
3336 CFormat(wxT("Error while retrieving file-length (%s): %s"))
3337 % m_fullname.RemoveExt() % e.what());
3338 SetPartFileStatus(PS_ERROR);
3339 return 0;
3343 void CPartFile::SetStatus(uint8 in)
3345 wxASSERT( in != PS_PAUSED && in != PS_INSUFFICIENT );
3347 status = in;
3349 if (theApp->IsRunning()) {
3350 UpdateDisplayedInfo( true );
3352 if ( thePrefs::ShowCatTabInfos() ) {
3353 Notify_ShowUpdateCatTabTitles();
3359 uint64 CPartFile::GetTotalGapSizeInRange(uint64 uRangeStart, uint64 uRangeEnd) const
3361 uint64 uTotalGapSize = 0;
3363 if (uRangeEnd >= GetFileSize()) {
3364 uRangeEnd = GetFileSize() - 1;
3367 std::list<Gap_Struct*>::const_iterator it = m_gaplist.begin();
3368 for (; it != m_gaplist.end(); ++it) {
3369 const Gap_Struct* pGap = *it;
3371 if (pGap->start < uRangeStart && pGap->end > uRangeEnd) {
3372 uTotalGapSize += uRangeEnd - uRangeStart + 1;
3373 break;
3376 if (pGap->start >= uRangeStart && pGap->start <= uRangeEnd) {
3377 uint64 uEnd = (pGap->end > uRangeEnd) ? uRangeEnd : pGap->end;
3378 uTotalGapSize += uEnd - pGap->start + 1;
3379 } else if (pGap->end >= uRangeStart && pGap->end <= uRangeEnd) {
3380 uTotalGapSize += pGap->end - uRangeStart + 1;
3384 wxASSERT( uTotalGapSize <= uRangeEnd - uRangeStart + 1 );
3386 return uTotalGapSize;
3389 uint64 CPartFile::GetTotalGapSizeInPart(uint32 uPart) const
3391 uint64 uRangeStart = uPart * PARTSIZE;
3392 uint64 uRangeEnd = uRangeStart + PARTSIZE - 1;
3393 if (uRangeEnd >= GetFileSize()) {
3394 uRangeEnd = GetFileSize();
3396 return GetTotalGapSizeInRange(uRangeStart, uRangeEnd);
3400 void CPartFile::RequestAICHRecovery(uint16 nPart)
3403 if ( !m_pAICHHashSet->HasValidMasterHash() ||
3404 (m_pAICHHashSet->GetStatus() != AICH_TRUSTED && m_pAICHHashSet->GetStatus() != AICH_VERIFIED)){
3405 AddDebugLogLineM( false, logAICHRecovery, wxT("Unable to request AICH Recoverydata because we have no trusted Masterhash") );
3406 return;
3408 if (GetFileSize() <= EMBLOCKSIZE || GetFileSize() - PARTSIZE*nPart <= EMBLOCKSIZE)
3409 return;
3410 if (CAICHHashSet::IsClientRequestPending(this, nPart)){
3411 AddDebugLogLineM( false, logAICHRecovery, wxT("RequestAICHRecovery: Already a request for this part pending"));
3412 return;
3415 // first check if we have already the recoverydata, no need to rerequest it then
3416 if (m_pAICHHashSet->IsPartDataAvailable(nPart*PARTSIZE)){
3417 AddDebugLogLineM( false, logAICHRecovery, wxT("Found PartRecoveryData in memory"));
3418 AICHRecoveryDataAvailable(nPart);
3419 return;
3422 wxASSERT( nPart < GetPartCount() );
3423 // find some random client which support AICH to ask for the blocks
3424 // first lets see how many we have at all, we prefer high id very much
3425 uint32 cAICHClients = 0;
3426 uint32 cAICHLowIDClients = 0;
3427 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it) {
3428 CUpDownClient* pCurClient = *(it);
3429 if ( pCurClient->IsSupportingAICH() &&
3430 pCurClient->GetReqFileAICHHash() != NULL &&
3431 !pCurClient->IsAICHReqPending()
3432 && (*pCurClient->GetReqFileAICHHash()) == m_pAICHHashSet->GetMasterHash())
3434 if (pCurClient->HasLowID()) {
3435 ++cAICHLowIDClients;
3436 } else {
3437 ++cAICHClients;
3441 if ((cAICHClients | cAICHLowIDClients) == 0){
3442 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"));
3443 return;
3445 uint32 nSeclectedClient;
3446 if (cAICHClients > 0) {
3447 nSeclectedClient = (rand() % cAICHClients) + 1;
3448 } else {
3449 nSeclectedClient = (rand() % cAICHLowIDClients) + 1;
3451 CUpDownClient* pClient = NULL;
3452 for ( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ++it) {
3453 CUpDownClient* pCurClient = *(it);
3454 if (pCurClient->IsSupportingAICH() && pCurClient->GetReqFileAICHHash() != NULL && !pCurClient->IsAICHReqPending()
3455 && (*pCurClient->GetReqFileAICHHash()) == m_pAICHHashSet->GetMasterHash())
3457 if (cAICHClients > 0){
3458 if (!pCurClient->HasLowID())
3459 nSeclectedClient--;
3461 else{
3462 wxASSERT( pCurClient->HasLowID());
3463 nSeclectedClient--;
3465 if (nSeclectedClient == 0){
3466 pClient = pCurClient;
3467 break;
3471 if (pClient == NULL){
3472 wxASSERT( false );
3473 return;
3476 AddDebugLogLineM( false, logAICHRecovery, CFormat( wxT("Requesting AICH Hash (%s) form client %s") ) % ( cAICHClients ? wxT("HighId") : wxT("LowID") ) % pClient->GetClientFullInfo() );
3477 pClient->SendAICHRequest(this, nPart);
3482 void CPartFile::AICHRecoveryDataAvailable(uint16 nPart)
3484 if (GetPartCount() < nPart){
3485 wxASSERT( false );
3486 return;
3489 FlushBuffer(true, true, true);
3491 uint64 length = PARTSIZE;
3493 try {
3494 if ((unsigned)(PARTSIZE * (nPart + 1)) > m_hpartfile.GetLength()){
3495 length = (m_hpartfile.GetLength() - (PARTSIZE * nPart));
3496 wxASSERT( length <= PARTSIZE );
3498 } catch (const CIOFailureException& e) {
3499 AddDebugLogLineM(true, logPartFile,
3500 CFormat(wxT("Error while retrieving file-length (%s): %s"))
3501 % m_fullname.RemoveExt() % e.what());
3502 SetPartFileStatus(PS_ERROR);
3503 return;
3506 // if the part was already ok, it would now be complete
3507 if (IsComplete(nPart*PARTSIZE, ((nPart*PARTSIZE)+length)-1)){
3508 AddDebugLogLineM( false, logAICHRecovery,
3509 wxString::Format( wxT("Processing AICH Recovery data: The part (%u) is already complete, canceling"), nPart ) );
3510 return;
3515 CAICHHashTree* pVerifiedHash = m_pAICHHashSet->m_pHashTree.FindHash(nPart*PARTSIZE, length);
3516 if (pVerifiedHash == NULL || !pVerifiedHash->GetHashValid()){
3517 AddDebugLogLineM( true, logAICHRecovery, wxT("Processing AICH Recovery data: Unable to get verified hash from hashset (should never happen)") );
3518 wxASSERT( false );
3519 return;
3521 CAICHHashTree htOurHash(pVerifiedHash->GetNDataSize(), pVerifiedHash->GetIsLeftBranch(), pVerifiedHash->GetNBaseSize());
3522 try {
3523 m_hpartfile.Seek(PARTSIZE * nPart,wxFromStart);
3524 CreateHashFromFile(&m_hpartfile,length, NULL, &htOurHash);
3525 } catch (const CIOFailureException& e) {
3526 AddDebugLogLineM(true, logAICHRecovery,
3527 CFormat(wxT("IO failure while hashing part-file '%s': %s"))
3528 % m_hpartfile.GetFilePath() % e.what());
3529 SetPartFileStatus(PS_ERROR);
3530 return;
3533 if (!htOurHash.GetHashValid()){
3534 AddDebugLogLineM( false, logAICHRecovery, wxT("Processing AICH Recovery data: Failed to retrieve AICH Hashset of corrupt part") );
3535 wxASSERT( false );
3536 return;
3539 // now compare the hash we just did, to the verified hash and readd all blocks which are ok
3540 uint32 nRecovered = 0;
3541 for (uint32 pos = 0; pos < length; pos += EMBLOCKSIZE){
3542 const uint32 nBlockSize = min<uint32>(EMBLOCKSIZE, length - pos);
3543 CAICHHashTree* pVerifiedBlock = pVerifiedHash->FindHash(pos, nBlockSize);
3544 CAICHHashTree* pOurBlock = htOurHash.FindHash(pos, nBlockSize);
3545 if ( pVerifiedBlock == NULL || pOurBlock == NULL || !pVerifiedBlock->GetHashValid() || !pOurBlock->GetHashValid()){
3546 wxASSERT( false );
3547 continue;
3549 if (pOurBlock->GetHash() == pVerifiedBlock->GetHash()){
3550 FillGap(PARTSIZE*nPart+pos, PARTSIZE*nPart + pos + (nBlockSize-1));
3551 RemoveBlockFromList(PARTSIZE*nPart, PARTSIZE*nPart + (nBlockSize-1));
3552 nRecovered += nBlockSize;
3556 // ok now some sanity checks
3557 if (IsComplete(nPart*PARTSIZE, ((nPart*PARTSIZE)+length)-1)){
3558 // this is a bad, but it could probably happen under some rare circumstances
3559 // make sure that MD4 agrres to this fact too
3560 if (!HashSinglePart(nPart)){
3561 AddDebugLogLineM( false, logAICHRecovery,
3562 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));
3563 // now we are fu... unhappy
3564 m_pAICHHashSet->SetStatus(AICH_ERROR);
3565 AddGap(PARTSIZE*nPart, ((nPart*PARTSIZE)+length)-1);
3566 wxASSERT( false );
3567 return;
3569 else{
3570 AddDebugLogLineM( false, logAICHRecovery, wxString::Format(
3571 wxT("Processing AICH Recovery data: The part (%u) got completed while recovering and MD4 agrees"), nPart) );
3572 // alrighty not so bad
3573 EraseFirstValue(m_corrupted_list, nPart);
3574 if (status == PS_EMPTY && theApp->IsRunning()){
3575 if (GetHashCount() == GetED2KPartHashCount() && !m_hashsetneeded){
3576 // Successfully recovered part, make it available for sharing
3577 SetStatus(PS_READY);
3578 theApp->sharedfiles->SafeAddKFile(this);
3582 if (theApp->IsRunning()){
3583 // Is this file finished?
3584 if (m_gaplist.empty()) {
3585 CompleteFile(false);
3589 } // end sanity check
3590 // Update met file
3591 SavePartFile();
3593 // make sure the user appreciates our great recovering work :P
3594 AddDebugLogLineM( true, logAICHRecovery, CFormat(
3595 wxT("AICH successfully recovered %s of %s from part %u for %s") )
3596 % CastItoXBytes(nRecovered)
3597 % CastItoXBytes(length)
3598 % nPart
3599 % GetFileName() );
3603 void CPartFile::ClientStateChanged( int oldState, int newState )
3605 if ( oldState == newState )
3606 return;
3608 // If the state is -1, then it's an entirely new item
3609 if ( oldState != -1 ) {
3610 // Was the old state a valid state?
3611 if ( oldState == DS_ONQUEUE || oldState == DS_DOWNLOADING ) {
3612 m_validSources--;
3613 } else {
3614 if ( oldState == DS_CONNECTED /* || oldState == DS_REMOTEQUEUEFULL */ ) {
3615 m_validSources--;
3618 m_notCurrentSources--;
3622 // If the state is -1, then the source is being removed
3623 if ( newState != -1 ) {
3624 // Was the old state a valid state?
3625 if ( newState == DS_ONQUEUE || newState == DS_DOWNLOADING ) {
3626 ++m_validSources;
3627 } else {
3628 if ( newState == DS_CONNECTED /* || newState == DS_REMOTEQUEUEFULL */ ) {
3629 ++m_validSources;
3632 ++m_notCurrentSources;
3638 bool CPartFile::AddSource( CUpDownClient* client )
3640 if (m_SrcList.insert( client ).second) {
3641 theStats::AddFoundSource();
3642 theStats::AddSourceOrigin(client->GetSourceFrom());
3643 return true;
3644 } else {
3645 return false;
3650 bool CPartFile::DelSource( CUpDownClient* client )
3652 if (m_SrcList.erase( client )) {
3653 theStats::RemoveSourceOrigin(client->GetSourceFrom());
3654 theStats::RemoveFoundSource();
3655 return true;
3656 } else {
3657 return false;
3662 void CPartFile::UpdatePartsFrequency( CUpDownClient* client, bool increment )
3664 const BitVector& freq = client->GetPartStatus();
3666 if ( m_SrcpartFrequency.size() != GetPartCount() ) {
3667 m_SrcpartFrequency.clear();
3668 m_SrcpartFrequency.insert(m_SrcpartFrequency.begin(), GetPartCount(), 0);
3670 if ( !increment ) {
3671 return;
3675 unsigned int size = freq.size();
3676 if ( size != m_SrcpartFrequency.size() ) {
3677 return;
3680 if ( increment ) {
3681 for ( unsigned int i = 0; i < size; i++ ) {
3682 if ( freq[i] ) {
3683 m_SrcpartFrequency[i]++;
3686 } else {
3687 for ( unsigned int i = 0; i < size; i++ ) {
3688 if ( freq[i] ) {
3689 m_SrcpartFrequency[i]--;
3695 const FileRatingList &CPartFile::GetRatingAndComments()
3697 m_FileRatingList.clear();
3698 // This can be pre-processed, but is it worth the CPU?
3699 CPartFile::SourceSet::iterator it = m_SrcList.begin();
3700 for ( ; it != m_SrcList.end(); ++it ) {
3701 CUpDownClient *cur_src = *it;
3702 if (cur_src->GetFileComment().Length()>0 || cur_src->GetFileRating()>0) {
3703 // AddDebugLogLineM(false, logPartFile, wxString(wxT("found a comment for ")) << GetFileName());
3704 m_FileRatingList.push_back(SFileRating(*cur_src));
3708 return m_FileRatingList;
3711 #else // CLIENT_GUI
3713 CPartFile::CPartFile(CEC_PartFile_Tag *tag)
3715 Init();
3717 SetFileName(CPath(tag->FileName()));
3718 m_abyFileHash = tag->ID();
3719 SetFileSize(tag->SizeFull());
3720 m_partmetfilename = CPath(tag->PartMetName());
3721 transferred = tag->SizeXfer();
3722 percentcompleted = (100.0*completedsize) / GetFileSize();
3723 completedsize = tag->SizeDone();
3725 m_category = tag->FileCat();
3727 m_iPartCount = ((uint64)GetFileSize() + (PARTSIZE - 1)) / PARTSIZE;
3728 m_SrcpartFrequency.insert(m_SrcpartFrequency.end(), m_iPartCount, 0);
3729 m_iDownPriority = tag->Prio();
3730 if ( m_iDownPriority >= 10 ) {
3731 m_iDownPriority-= 10;
3732 m_bAutoDownPriority = true;
3733 } else {
3734 m_bAutoDownPriority = false;
3736 // FIXME: !
3737 m_category = 0;
3739 m_source_count = 0;
3740 m_a4af_source_count = 0;
3744 * Remote gui specific code
3746 CPartFile::~CPartFile()
3750 const FileRatingList &CPartFile::GetRatingAndComments()
3752 return m_FileRatingList;
3754 #endif // !CLIENT_GUI
3757 void CPartFile::UpdateDisplayedInfo(bool force)
3759 uint32 curTick = ::GetTickCount();
3760 m_CommentUpdated = true;
3762 // Wait 1.5s between each redraw
3763 if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE ) {
3764 Notify_DownloadCtrlUpdateItem(this);
3765 m_lastRefreshedDLDisplay = curTick;
3771 void CPartFile::Init()
3773 m_showSources = false;
3774 m_lastsearchtime = 0;
3775 lastpurgetime = ::GetTickCount();
3776 m_paused = false;
3777 m_stopped = false;
3778 m_insufficient = false;
3780 status = PS_EMPTY;
3782 transferred = 0;
3783 m_iLastPausePurge = time(NULL);
3785 if(thePrefs::GetNewAutoDown()) {
3786 m_iDownPriority = PR_HIGH;
3787 m_bAutoDownPriority = true;
3788 } else {
3789 m_iDownPriority = PR_NORMAL;
3790 m_bAutoDownPriority = false;
3793 memset(m_anStates,0,sizeof(m_anStates));
3795 transferingsrc = 0; // new
3797 kBpsDown = 0.0;
3799 m_CommentUpdated = false;
3800 m_hashsetneeded = true;
3801 m_count = 0;
3802 percentcompleted = 0;
3803 completedsize=0;
3804 m_bPreviewing = false;
3805 lastseencomplete = 0;
3806 m_availablePartsCount=0;
3807 m_ClientSrcAnswered = 0;
3808 m_LastNoNeededCheck = 0;
3809 m_iRating = 0;
3810 m_nTotalBufferData = 0;
3811 m_nLastBufferFlushTime = 0;
3812 m_bPercentUpdated = false;
3813 m_bRecoveringArchive = false;
3814 m_iGainDueToCompression = 0;
3815 m_iLostDueToCorruption = 0;
3816 m_iTotalPacketsSavedDueToICH = 0;
3817 m_category = 0;
3818 m_lastRefreshedDLDisplay = 0;
3819 m_nDlActiveTime = 0;
3820 m_tActivated = 0;
3821 m_is_A4AF_auto = false;
3822 m_localSrcReqQueued = false;
3823 m_nCompleteSourcesTime = time(NULL);
3824 m_nCompleteSourcesCount = 0;
3825 m_nCompleteSourcesCountLo = 0;
3826 m_nCompleteSourcesCountHi = 0;
3828 m_validSources = 0;
3829 m_notCurrentSources = 0;
3831 // Kad
3832 m_LastSearchTimeKad = 0;
3833 m_TotalSearchesKad = 0;
3837 wxString CPartFile::getPartfileStatus() const
3840 wxString mybuffer;
3842 if ((status == PS_HASHING) || (status == PS_WAITINGFORHASH)) {
3843 mybuffer=_("Hashing");
3844 } else if (status == PS_ALLOCATING) {
3845 mybuffer = _("Allocating");
3846 } else {
3847 switch (GetStatus()) {
3848 case PS_COMPLETING:
3849 mybuffer=_("Completing");
3850 break;
3851 case PS_COMPLETE:
3852 mybuffer=_("Complete");
3853 break;
3854 case PS_PAUSED:
3855 mybuffer=_("Paused");
3856 break;
3857 case PS_ERROR:
3858 mybuffer=_("Erroneous");
3859 break;
3860 case PS_INSUFFICIENT:
3861 mybuffer = _("Insufficient Diskspace");
3862 break;
3863 default:
3864 if (GetTransferingSrcCount()>0) {
3865 mybuffer=_("Downloading");
3866 } else {
3867 mybuffer=_("Waiting");
3869 break;
3871 if (m_stopped && (GetStatus()!=PS_COMPLETE)) {
3872 mybuffer=_("Stopped");
3876 return mybuffer;
3879 int CPartFile::getPartfileStatusRang() const
3882 int tempstatus=0;
3883 if (GetTransferingSrcCount()==0) tempstatus=1;
3884 switch (GetStatus()) {
3885 case PS_HASHING:
3886 case PS_WAITINGFORHASH:
3887 tempstatus=3;
3888 break;
3889 case PS_COMPLETING:
3890 tempstatus=4;
3891 break;
3892 case PS_COMPLETE:
3893 tempstatus=5;
3894 break;
3895 case PS_PAUSED:
3896 tempstatus=2;
3897 break;
3898 case PS_ERROR:
3899 tempstatus=6;
3900 break;
3902 return tempstatus;
3906 wxString CPartFile::GetFeedback() const
3908 wxString retval = CKnownFile::GetFeedback();
3909 if (GetStatus() != PS_COMPLETE) {
3910 retval += wxString(_("Downloaded")) + wxT(": ") + CastItoXBytes(GetCompletedSize()) + wxString::Format(wxT(" (%.2f%%)\n"), GetPercentCompleted())
3911 + _("Sources") + CFormat(wxT(": %u\n")) % GetSourceCount();
3913 return retval + _("Status") + wxT(": ") + getPartfileStatus() + wxT("\n");
3917 sint32 CPartFile::getTimeRemaining() const
3919 if (GetKBpsDown() < 0.001)
3920 return -1;
3921 else
3922 return((GetFileSize()-GetCompletedSize()) / ((int)(GetKBpsDown()*1024.0)));
3925 bool CPartFile::PreviewAvailable()
3927 FileType type = GetFiletype(GetFileName());
3929 return (((type == ftVideo) || (type == ftAudio)) && IsComplete(0, 256*1024));
3932 bool CPartFile::CheckShowItemInGivenCat(int inCategory)
3934 // easy normal cases
3935 bool IsInCat;
3936 bool IsNotFiltered = true;
3938 IsInCat = ((inCategory==0) || (inCategory>0 && inCategory==GetCategory()));
3940 switch (thePrefs::GetAllcatType()) {
3941 case 1:
3942 IsNotFiltered = GetCategory() == 0 || inCategory > 0;
3943 break;
3944 case 2:
3945 IsNotFiltered = IsPartFile();
3946 break;
3947 case 3:
3948 IsNotFiltered = !IsPartFile();
3949 break;
3950 case 4:
3951 IsNotFiltered =
3952 (GetStatus() == PS_READY || GetStatus() == PS_EMPTY) &&
3953 GetTransferingSrcCount() == 0;
3954 break;
3955 case 5:
3956 IsNotFiltered =
3957 (GetStatus() == PS_READY || GetStatus()==PS_EMPTY) &&
3958 GetTransferingSrcCount() > 0;
3959 break;
3960 case 6:
3961 IsNotFiltered = GetStatus() == PS_ERROR;
3962 break;
3963 case 7:
3964 IsNotFiltered = GetStatus() == PS_PAUSED && !IsStopped();
3965 break;
3966 case 8:
3967 IsNotFiltered = IsStopped();
3968 break;
3969 case 9:
3970 IsNotFiltered = GetFiletype(GetFileName()) == ftVideo;
3971 break;
3972 case 10:
3973 IsNotFiltered = GetFiletype(GetFileName()) == ftAudio;
3974 break;
3975 case 11:
3976 IsNotFiltered = GetFiletype(GetFileName()) == ftArchive;
3977 break;
3978 case 12:
3979 IsNotFiltered = GetFiletype(GetFileName()) == ftCDImage;
3980 break;
3981 case 13:
3982 IsNotFiltered = GetFiletype(GetFileName()) == ftPicture;
3983 break;
3984 case 14:
3985 IsNotFiltered = GetFiletype(GetFileName()) == ftText;
3986 break;
3987 case 15:
3988 IsNotFiltered = !IsStopped() && GetStatus() != PS_PAUSED;
3989 break;
3992 return IsNotFiltered && IsInCat;
3995 bool CPartFile::IsComplete(uint64 start, uint64 end)
3997 if (end >= GetFileSize()) {
3998 end = GetFileSize()-1;
4001 std::list<Gap_Struct*>::iterator it = m_gaplist.begin();
4002 for (; it != m_gaplist.end(); ++it) {
4003 Gap_Struct* cur_gap = *it;
4004 if ((cur_gap->start >= start && cur_gap->end <= end)||(cur_gap->start >= start
4005 && cur_gap->start <= end)||(cur_gap->end <= end && cur_gap->end >= start)
4006 ||(start >= cur_gap->start && end <= cur_gap->end)) {
4007 return false;
4010 return true;
4014 void CPartFile::SetActive(bool bActive)
4016 time_t tNow = time(NULL);
4017 if (bActive) {
4018 if (theApp->IsConnected()) {
4019 if (m_tActivated == 0) {
4020 m_tActivated = tNow;
4023 } else {
4024 if (m_tActivated != 0) {
4025 m_nDlActiveTime += tNow - m_tActivated;
4026 m_tActivated = 0;
4032 uint32 CPartFile::GetDlActiveTime() const
4034 uint32 nDlActiveTime = m_nDlActiveTime;
4035 if (m_tActivated != 0) {
4036 nDlActiveTime += time(NULL) - m_tActivated;
4038 return nDlActiveTime;
4042 #ifndef CLIENT_GUI
4044 uint8 CPartFile::GetStatus(bool ignorepause) const
4046 if ( (!m_paused && !m_insufficient) ||
4047 status == PS_ERROR ||
4048 status == PS_COMPLETING ||
4049 status == PS_COMPLETE ||
4050 ignorepause) {
4051 return status;
4052 } else if ( m_insufficient ) {
4053 return PS_INSUFFICIENT;
4054 } else {
4055 return PS_PAUSED;
4059 void CPartFile::AddDeadSource(const CUpDownClient* client)
4061 m_deadSources.AddDeadSource( client );
4065 bool CPartFile::IsDeadSource(const CUpDownClient* client)
4067 return m_deadSources.IsDeadSource( client );
4070 void CPartFile::SetFileName(const CPath& fileName)
4072 CKnownFile* pFile = theApp->sharedfiles->GetFileByID(GetFileHash());
4074 bool is_shared = (pFile && pFile == this);
4076 if (is_shared) {
4077 // The file is shared, we must clear the search keywords so we don't
4078 // publish the old name anymore.
4079 theApp->sharedfiles->RemoveKeywords(this);
4082 CKnownFile::SetFileName(fileName);
4084 if (is_shared) {
4085 // And of course, we must advertise the new name if the file is shared.
4086 theApp->sharedfiles->AddKeywords(this);
4089 UpdateDisplayedInfo(true);
4093 uint16 CPartFile::GetMaxSources() const
4095 // This is just like this, while we don't import the private max sources per file
4096 return thePrefs::GetMaxSourcePerFile();
4100 uint16 CPartFile::GetMaxSourcePerFileSoft() const
4102 unsigned int temp = ((unsigned int)GetMaxSources() * 9L) / 10;
4103 if (temp > MAX_SOURCES_FILE_SOFT) {
4104 return MAX_SOURCES_FILE_SOFT;
4106 return temp;
4109 uint16 CPartFile::GetMaxSourcePerFileUDP() const
4111 unsigned int temp = ((unsigned int)GetMaxSources() * 3L) / 4;
4112 if (temp > MAX_SOURCES_FILE_UDP) {
4113 return MAX_SOURCES_FILE_UDP;
4115 return temp;
4118 #define DROP_FACTOR 2
4120 CUpDownClient* CPartFile::GetSlowerDownloadingClient(uint32 speed, CUpDownClient* caller) {
4121 // printf("Start slower source calculation\n");
4122 for( SourceSet::iterator it = m_SrcList.begin(); it != m_SrcList.end(); ) {
4123 CUpDownClient* cur_src = *it++;
4124 if ((cur_src->GetDownloadState() == DS_DOWNLOADING) && (cur_src != caller)) {
4125 uint32 factored_bytes_per_second = static_cast<uint32>(
4126 (cur_src->GetKBpsDown() * 1024) * DROP_FACTOR);
4127 if ( factored_bytes_per_second< speed) {
4128 // printf("Selecting source %p to drop: %d < %d\n", cur_src, factored_bytes_per_second, speed);
4129 // printf("End slower source calculation\n");
4130 return cur_src;
4131 } else {
4132 // printf("Not selecting source %p to drop: %d > %d\n", cur_src, factored_bytes_per_second, speed);
4136 // printf("End slower source calculation\n");
4137 return NULL;
4140 void CPartFile::AllocationFinished()
4142 if (!m_hpartfile.Open(GetFullName().RemoveExt(), CFile::read_write)) {
4143 AddLogLineM(false, CFormat(_("ERROR: Failed to open partfile '%s'")) % GetFullName());
4144 SetPartFileStatus(PS_ERROR);
4148 #endif
4149 // File_checked_for_headers