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