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-2008 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
26 #include "SharedFileList.h" // Interface declarations // Do_not_auto_remove
28 #include <protocol/Protocols.h>
29 #include <protocol/ed2k/ClientSoftware.h>
30 #include <protocol/kad/Constants.h>
31 #include <tags/FileTags.h>
35 #include "Packet.h" // Needed for CPacket
36 #include "MemFile.h" // Needed for CMemFile
37 #include "ServerConnect.h" // Needed for CServerConnect
38 #include "KnownFileList.h" // Needed for CKnownFileList
39 #include "ThreadTasks.h" // Needed for CThreadScheduler and CHasherTask
40 #include "Preferences.h" // Needed for thePrefs
41 #include "DownloadQueue.h" // Needed for CDownloadQueue
42 #include "amule.h" // Needed for theApp
43 #include "PartFile.h" // Needed for PartFile
44 #include "Server.h" // Needed for CServer
45 #include "updownclient.h"
46 #include "Statistics.h" // Needed for theStats
48 #include <common/Format.h>
49 #include <common/FileFunctions.h>
50 #include "GuiEvents.h" // Needed for Notify_*
51 #include "SHAHashSet.h" // Needed for CAICHHash
54 #include "kademlia/kademlia/Kademlia.h"
55 #include "kademlia/kademlia/Search.h"
56 #include "ClientList.h"
58 typedef std::deque
<CKnownFile
*> KnownFileArray
;
60 ///////////////////////////////////////////////////////////////////////////////
66 CPublishKeyword(const wxString
& rstrKeyword
)
68 m_strKeyword
= rstrKeyword
;
69 // min. keyword char is allowed to be < 3 in some cases (see also 'CSearchManager::getWords')
70 //ASSERT( rstrKeyword.GetLength() >= 3 );
71 wxASSERT( !rstrKeyword
.IsEmpty() );
72 KadGetKeywordHash(rstrKeyword
, &m_nKadID
);
73 SetNextPublishTime(0);
77 const Kademlia::CUInt128
& GetKadID() const { return m_nKadID
; }
78 const wxString
& GetKeyword() const { return m_strKeyword
; }
79 int GetRefCount() const { return m_aFiles
.size(); }
80 const KnownFileArray
& GetReferences() const { return m_aFiles
; }
82 uint32
GetNextPublishTime() const { return m_tNextPublishTime
; }
83 void SetNextPublishTime(uint32 tNextPublishTime
) { m_tNextPublishTime
= tNextPublishTime
; }
85 uint32
GetPublishedCount() const { return m_uPublishedCount
; }
86 void SetPublishedCount(uint32 uPublishedCount
) { m_uPublishedCount
= uPublishedCount
; }
87 void IncPublishedCount() { m_uPublishedCount
++; }
89 bool AddRef(CKnownFile
* pFile
) {
90 if (std::find(m_aFiles
.begin(), m_aFiles
.end(), pFile
) != m_aFiles
.end()) {
94 m_aFiles
.push_back(pFile
);
98 int RemoveRef(CKnownFile
* pFile
) {
99 KnownFileArray::iterator it
= std::find(m_aFiles
.begin(), m_aFiles
.end(), pFile
);
100 if (it
!= m_aFiles
.end()) {
103 return m_aFiles
.size();
106 void RemoveAllReferences() {
110 void RotateReferences(unsigned iRotateSize
) {
111 wxCHECK_RET(m_aFiles
.size(), wxT("RotateReferences: Rotating empty array"));
113 unsigned shift
= (iRotateSize
% m_aFiles
.size());
114 std::rotate(m_aFiles
.begin(), m_aFiles
.begin() + shift
, m_aFiles
.end());
118 wxString m_strKeyword
;
119 Kademlia::CUInt128 m_nKadID
;
120 uint32 m_tNextPublishTime
;
121 uint32 m_uPublishedCount
;
122 KnownFileArray m_aFiles
;
126 ///////////////////////////////////////////////////////////////////////////////
127 // CPublishKeywordList
129 class CPublishKeywordList
132 CPublishKeywordList();
133 ~CPublishKeywordList();
135 void AddKeywords(CKnownFile
* pFile
);
136 void RemoveKeywords(CKnownFile
* pFile
);
137 void RemoveAllKeywords();
139 void RemoveAllKeywordReferences();
140 void PurgeUnreferencedKeywords();
142 int GetCount() const { return m_lstKeywords
.size(); }
144 CPublishKeyword
* GetNextKeyword();
145 void ResetNextKeyword();
147 uint32
GetNextPublishTime() const { return m_tNextPublishKeywordTime
; }
148 void SetNextPublishTime(uint32 tNextPublishKeywordTime
) { m_tNextPublishKeywordTime
= tNextPublishKeywordTime
; }
151 // can't use a CMap - too many disadvantages in processing the 'list'
152 //CTypedPtrMap<CMapStringToPtr, CString, CPublishKeyword*> m_lstKeywords;
153 typedef std::list
<CPublishKeyword
*> CKeyWordList
;
154 CKeyWordList m_lstKeywords
;
155 CKeyWordList::iterator m_posNextKeyword
;
156 uint32 m_tNextPublishKeywordTime
;
158 CPublishKeyword
* FindKeyword(const wxString
& rstrKeyword
, CKeyWordList::iterator
* ppos
= NULL
);
161 CPublishKeywordList::CPublishKeywordList()
164 SetNextPublishTime(0);
167 CPublishKeywordList::~CPublishKeywordList()
172 CPublishKeyword
* CPublishKeywordList::GetNextKeyword()
174 if (m_posNextKeyword
== m_lstKeywords
.end()) {
175 m_posNextKeyword
= m_lstKeywords
.begin();
176 if (m_posNextKeyword
== m_lstKeywords
.end()) {
180 return *m_posNextKeyword
++;
183 void CPublishKeywordList::ResetNextKeyword()
185 m_posNextKeyword
= m_lstKeywords
.begin();
188 CPublishKeyword
* CPublishKeywordList::FindKeyword(const wxString
& rstrKeyword
, CKeyWordList::iterator
* ppos
)
190 CKeyWordList::iterator it
= m_lstKeywords
.begin();
191 for (; it
!= m_lstKeywords
.end(); ++it
) {
192 CPublishKeyword
* pPubKw
= *it
;
193 if (pPubKw
->GetKeyword() == rstrKeyword
) {
205 void CPublishKeywordList::AddKeywords(CKnownFile
* pFile
)
207 const Kademlia::WordList
& wordlist
= pFile
->GetKadKeywords();
209 Kademlia::WordList::const_iterator it
;
210 for (it
= wordlist
.begin(); it
!= wordlist
.end(); ++it
) {
211 const wxString
& strKeyword
= *it
;
212 CPublishKeyword
* pPubKw
= FindKeyword(strKeyword
);
213 if (pPubKw
== NULL
) {
214 pPubKw
= new CPublishKeyword(strKeyword
);
215 m_lstKeywords
.push_back(pPubKw
);
216 SetNextPublishTime(0);
218 pPubKw
->AddRef(pFile
);
222 void CPublishKeywordList::RemoveKeywords(CKnownFile
* pFile
)
224 const Kademlia::WordList
& wordlist
= pFile
->GetKadKeywords();
225 Kademlia::WordList::const_iterator it
;
226 for (it
= wordlist
.begin(); it
!= wordlist
.end(); ++it
) {
227 const wxString
& strKeyword
= *it
;
228 CKeyWordList::iterator pos
;
229 CPublishKeyword
* pPubKw
= FindKeyword(strKeyword
, &pos
);
230 if (pPubKw
!= NULL
) {
231 if (pPubKw
->RemoveRef(pFile
) == 0) {
232 if (pos
== m_posNextKeyword
) {
235 m_lstKeywords
.erase(pos
);
237 SetNextPublishTime(0);
244 void CPublishKeywordList::RemoveAllKeywords()
246 DeleteContents(m_lstKeywords
);
248 SetNextPublishTime(0);
252 void CPublishKeywordList::RemoveAllKeywordReferences()
254 CKeyWordList::iterator it
= m_lstKeywords
.begin();
255 for (; it
!= m_lstKeywords
.end(); ++it
) {
256 (*it
)->RemoveAllReferences();
261 void CPublishKeywordList::PurgeUnreferencedKeywords()
263 CKeyWordList::iterator it
= m_lstKeywords
.begin();
264 while (it
!= m_lstKeywords
.end()) {
265 CPublishKeyword
* pPubKw
= *it
;
266 if (pPubKw
->GetRefCount() == 0) {
267 if (it
== m_posNextKeyword
) {
270 m_lstKeywords
.erase(it
++);
272 SetNextPublishTime(0);
280 CSharedFileList::CSharedFileList(CKnownFileList
* in_filelist
){
281 filelist
= in_filelist
;
283 m_lastPublishED2K
= 0;
284 m_lastPublishED2KFlag
= true;
286 m_keywords
= new CPublishKeywordList
;
289 m_lastPublishKadSrc
= 0;
290 m_lastPublishKadNotes
= 0;
295 CSharedFileList::~CSharedFileList()
301 void CSharedFileList::FindSharedFiles()
303 /* Abort loading if we are shutting down. */
304 if(theApp
->IsOnShutDown()) {
309 theStats::ClearSharedFilesInfo();
311 // Reload shareddir.dat
312 theApp
->glob_prefs
->ReloadSharedFolders();
315 wxMutexLocker
lock(list_mut
);
319 // All part files are automatically shared.
320 for ( uint32 i
= 0; i
< theApp
->downloadqueue
->GetFileCount(); ++i
) {
321 CPartFile
* file
= theApp
->downloadqueue
->GetFileByIndex( i
);
323 if ( file
->GetStatus(true) == PS_READY
) {
324 AddLogLineNS(CFormat(_("Adding file %s to shares"))
325 % file
->GetFullName().GetPrintable());
330 // Create a list of all shared paths and weed out duplicates.
331 std::list
<CPath
> sharedPaths
;
333 // Global incoming dir and all category incoming directories are automatically shared.
334 sharedPaths
.push_back(thePrefs::GetIncomingDir());
335 for (unsigned int i
= 1;i
< theApp
->glob_prefs
->GetCatCount(); ++i
) {
336 sharedPaths
.push_back(theApp
->glob_prefs
->GetCatPath(i
));
339 const thePrefs::PathList
& shared
= theApp
->glob_prefs
->shareddir_list
;
340 sharedPaths
.insert(sharedPaths
.end(), shared
.begin(), shared
.end());
343 sharedPaths
.unique();
345 unsigned addedFiles
= 0;
346 std::list
<CPath
>::iterator it
= sharedPaths
.begin();
347 for (; it
!= sharedPaths
.end(); ++it
) {
348 addedFiles
+= AddFilesFromDirectory(*it
);
351 if (addedFiles
== 0) {
352 AddLogLineM(false, wxString::Format(wxPLURAL("Found %i known shared file", "Found %i known shared files", GetCount()), GetCount()));
354 // Make sure the AICH-hashes are up to date.
355 CThreadScheduler::AddTask(new CAICHSyncTask());
357 // New files, AICH thread will be run at the end of the hashing thread.
358 AddLogLineM(false, wxString::Format(wxPLURAL("Found %i known shared file, %i unknown", "Found %i known shared files, %i unknown", GetCount()), GetCount(), addedFiles
));
363 // Checks if the dir a is the same as b. If they are, then logs the message and returns true.
364 bool CheckDirectory(const wxString
& a
, const CPath
& b
)
366 if (CPath(a
).IsSameDir(b
)) {
367 AddLogLineM(true, CFormat( _("ERROR: Attempted to share %s") ) % a
);
376 unsigned CSharedFileList::AddFilesFromDirectory(const CPath
& directory
)
378 // Do not allow these folders to be shared:
379 // - The .aMule folder
381 // - The users home-dir
382 if (CheckDirectory(wxGetHomeDir(), directory
)) {
384 } else if (CheckDirectory(theApp
->ConfigDir
, directory
)) {
386 } else if (CheckDirectory(thePrefs::GetTempDir().GetRaw(), directory
)) {
390 if (!directory
.DirExists()) {
391 AddLogLineNS(CFormat(_("Shared directory not found, skipping: %s"))
392 % directory
.GetPrintable());
397 CDirIterator::FileType searchFor
= CDirIterator::FileNoHidden
;
398 if (thePrefs::ShareHiddenFiles()) {
399 searchFor
= CDirIterator::File
;
402 unsigned knownFiles
= 0;
403 unsigned addedFiles
= 0;
405 CDirIterator
SharedDir(directory
);
407 CPath fname
= SharedDir
.GetFirstFile(searchFor
);
408 while (fname
.IsOk()) {
409 CPath fullPath
= directory
.JoinPaths(fname
);
411 if (!fullPath
.FileExists()) {
412 AddDebugLogLineM(false, logKnownFiles
,
413 CFormat(wxT("Shared file does not exist (possibly a broken link): %s")) % fullPath
);
415 fname
= SharedDir
.GetNextFile();
419 AddDebugLogLineM(false, logKnownFiles
,
420 CFormat(wxT("Found shared file: %s")) % fullPath
);
422 time_t fdate
= CPath::GetModificationTime(fullPath
);
423 sint64 fsize
= fullPath
.GetFileSize();
425 // This will also catch files with too strict permissions.
426 if ((fdate
== (time_t)-1) || (fsize
== wxInvalidOffset
)) {
427 AddDebugLogLineM(false, logKnownFiles
,
428 CFormat(wxT("Failed to retrive modification time or size for '%s', skipping.")) % fullPath
);
430 fname
= SharedDir
.GetNextFile();
435 CKnownFile
* toadd
= filelist
->FindKnownFile(fname
, fdate
, fsize
);
438 if (AddFile(toadd
)) {
439 AddDebugLogLineM(false, logKnownFiles
,
440 CFormat(wxT("Added known file '%s' to shares"))
443 toadd
->SetFilePath(directory
);
445 AddDebugLogLineM(false, logKnownFiles
,
446 CFormat(wxT("File already shared, skipping: %s"))
450 //not in knownfilelist - start adding thread to hash file
451 AddDebugLogLineM(false, logKnownFiles
,
452 CFormat(wxT("Hashing new unknown shared file '%s'")) % fname
);
454 if (CThreadScheduler::AddTask(new CHashingTask(directory
, fname
))) {
459 fname
= SharedDir
.GetNextFile();
462 if ((addedFiles
== 0) && (knownFiles
== 0)) {
463 AddLogLineNS(CFormat(_("No shareable files found in directory: %s"))
464 % directory
.GetPrintable());
471 bool CSharedFileList::AddFile(CKnownFile
* pFile
)
473 wxASSERT(pFile
->GetHashCount() == pFile
->GetED2KPartHashCount());
475 wxMutexLocker
lock(list_mut
);
477 CKnownFileMap::value_type
entry(pFile
->GetFileHash(), pFile
);
478 if (m_Files_map
.insert(entry
).second
) {
479 /* Keywords to publish on Kad */
480 m_keywords
->AddKeywords(pFile
);
481 theStats::AddSharedFile(pFile
->GetFileSize());
488 void CSharedFileList::SafeAddKFile(CKnownFile
* toadd
, bool bOnlyAdd
)
490 // TODO: Check if the file is already known - only with another date
492 if (AddFile(toadd
)) {
493 Notify_SharedFilesShowFile(toadd
);
496 if (!bOnlyAdd
&& theApp
->IsConnectedED2K()) {
497 // Publishing of files is not anymore handled here.
498 // Instead, the timer does it by itself.
499 m_lastPublishED2KFlag
= true;
504 // removes first occurrence of 'toremove' in 'list'
505 void CSharedFileList::RemoveFile(CKnownFile
* toremove
){
506 Notify_SharedFilesRemoveFile(toremove
);
507 wxMutexLocker
lock(list_mut
);
508 if (m_Files_map
.erase(toremove
->GetFileHash()) > 0) {
509 theStats::RemoveSharedFile(toremove
->GetFileSize());
511 /* This file keywords must not be published to kad anymore */
512 m_keywords
->RemoveKeywords(toremove
);
516 void CSharedFileList::Reload()
518 // Madcat - Disable reloading if reloading already in progress.
519 // Kry - Fixed to let non-english language users use the 'Reload' button :P
520 // deltaHF - removed the old ugly button and changed the code to use the new small one
521 // Kry - bah, let's use a var.
524 Notify_SharedFilesRemoveAllItems();
526 /* All Kad keywords must be removed */
527 m_keywords
->RemoveAllKeywordReferences();
531 /* And now the unreferenced keywords must be removed also */
532 m_keywords
->PurgeUnreferencedKeywords();
534 Notify_SharedFilesShowFileList();
541 const CKnownFile
*CSharedFileList::GetFileByIndex(unsigned int index
) const
543 wxMutexLocker
lock(list_mut
);
544 if ( index
>= m_Files_map
.size() ) {
547 CKnownFileMap::const_iterator pos
= m_Files_map
.begin();
548 std::advance(pos
, index
);
553 CKnownFile
* CSharedFileList::GetFileByID(const CMD4Hash
& filehash
)
555 wxMutexLocker
lock(list_mut
);
556 CKnownFileMap::iterator it
= m_Files_map
.find(filehash
);
558 if ( it
!= m_Files_map
.end() ) {
565 short CSharedFileList::GetFilePriorityByID(const CMD4Hash
& filehash
)
567 CKnownFile
* tocheck
= GetFileByID(filehash
);
569 return tocheck
->GetUpPriority();
571 return -10; // file doesn't exist
575 void CSharedFileList::CopyFileList(std::vector
<CKnownFile
*>& out_list
) const
577 wxMutexLocker
lock(list_mut
);
579 out_list
.reserve(m_Files_map
.size());
581 CKnownFileMap::const_iterator it
= m_Files_map
.begin();
582 it
!= m_Files_map
.end();
585 out_list
.push_back(it
->second
);
591 void CSharedFileList::UpdateItem(CKnownFile
* toupdate
)
593 Notify_SharedFilesUpdateItem(toupdate
);
597 void CSharedFileList::GetSharedFilesByDirectory(const wxString
& directory
,
598 CKnownFilePtrList
& list
)
600 wxMutexLocker
lock(list_mut
);
602 const CPath dir
= CPath(directory
);
603 for (CKnownFileMap::iterator pos
= m_Files_map
.begin();
604 pos
!= m_Files_map
.end(); ++pos
) {
605 CKnownFile
*cur_file
= pos
->second
;
607 if (dir
.IsSameDir(cur_file
->GetFilePath())) {
611 list
.push_back(cur_file
);
615 /* ---------------- Network ----------------- */
617 void CSharedFileList::ClearED2KPublishInfo(){
618 CKnownFile
* cur_file
;
619 m_lastPublishED2KFlag
= true;
620 wxMutexLocker
lock(list_mut
);
621 for (CKnownFileMap::iterator pos
= m_Files_map
.begin(); pos
!= m_Files_map
.end(); ++pos
) {
622 cur_file
= pos
->second
;
623 cur_file
->SetPublishedED2K(false);
627 void CSharedFileList::ClearKadSourcePublishInfo()
629 wxMutexLocker
lock(list_mut
);
630 CKnownFile
* cur_file
;
631 for (CKnownFileMap::iterator pos
= m_Files_map
.begin(); pos
!= m_Files_map
.end(); ++pos
) {
632 cur_file
= pos
->second
;
633 cur_file
->SetLastPublishTimeKadSrc(0,0);
637 void CSharedFileList::RepublishFile(CKnownFile
* pFile
)
639 CServer
* server
= theApp
->serverconnect
->GetCurrentServer();
640 if (server
&& (server
->GetTCPFlags() & SRV_TCPFLG_COMPRESSION
)) {
641 m_lastPublishED2KFlag
= true;
642 pFile
->SetPublishedED2K(false); // FIXME: this creates a wrong 'No' for the ed2k shared info in the listview until the file is shared again.
646 uint8
GetRealPrio(uint8 in
)
658 bool SortFunc( const CKnownFile
* fileA
, const CKnownFile
* fileB
)
660 return GetRealPrio(fileA
->GetUpPriority()) < GetRealPrio(fileB
->GetUpPriority());
663 void CSharedFileList::SendListToServer(){
664 std::vector
<CKnownFile
*> SortedList
;
667 wxMutexLocker
lock(list_mut
);
669 if (m_Files_map
.empty() || !theApp
->IsConnectedED2K() ) {
673 // Getting a sorted list of the non-published files.
674 SortedList
.reserve( m_Files_map
.size() );
676 CKnownFileMap::iterator it
= m_Files_map
.begin();
677 for ( ; it
!= m_Files_map
.end(); ++it
) {
678 if (!it
->second
->GetPublishedED2K()) {
679 SortedList
.push_back( it
->second
);
684 std::sort( SortedList
.begin(), SortedList
.end(), SortFunc
);
686 // Limits for the server.
688 CServer
* server
= theApp
->serverconnect
->GetCurrentServer();
690 uint32 limit
= server
? server
->GetSoftFiles() : 0;
691 if( limit
== 0 || limit
> 200 ) {
695 if( (uint32
)SortedList
.size() < limit
) {
696 limit
= SortedList
.size();
698 m_lastPublishED2KFlag
= false;
706 files
.WriteUInt32(limit
);
710 std::vector
<CKnownFile
*>::iterator sorted_it
= SortedList
.begin();
711 for ( ; (sorted_it
!= SortedList
.end()) && (count
< limit
); ++sorted_it
) {
712 CKnownFile
* file
= *sorted_it
;
713 if (!file
->IsLargeFile() || (server
&& server
->SupportsLargeFilesTCP())) {
714 CreateOfferedFilePacket(file
, &files
, server
, NULL
);
716 file
->SetPublishedED2K(true);
720 wxASSERT(count
== limit
);
722 CPacket
* packet
= new CPacket(files
, OP_EDONKEYPROT
, OP_OFFERFILES
);
724 // - this kind of data is highly compressable (N * (1 MD4 and at least 3 string meta data tags and 1 integer meta data tag))
725 // - the min. amount of data needed for one published file is ~100 bytes
726 // - this function is called once when connecting to a server and when a file becomes shareable - so, it's called rarely.
727 // - if the compressed size is still >= the original size, we send the uncompressed packet
728 // therefor we always try to compress the packet
729 if (server
->GetTCPFlags() & SRV_TCPFLG_COMPRESSION
){
730 packet
->PackPacket();
733 theStats::AddUpOverheadServer(packet
->GetPacketSize());
734 theApp
->serverconnect
->SendPacket(packet
,true);
738 void CSharedFileList::CreateOfferedFilePacket(
739 CKnownFile
*cur_file
,
742 CUpDownClient
*pClient
) {
744 // This function is used for offering files to the local server and for sending
745 // shared files to some other client. In each case we send our IP+Port only, if
748 wxASSERT(!(pClient
&& pServer
));
750 cur_file
->SetPublishedED2K(true);
751 files
->WriteHash(cur_file
->GetFileHash());
753 uint32 nClientID
= 0;
754 uint16 nClientPort
= 0;
757 if (pServer
->GetTCPFlags() & SRV_TCPFLG_COMPRESSION
) {
758 #define FILE_COMPLETE_ID 0xfbfbfbfb
759 #define FILE_COMPLETE_PORT 0xfbfb
760 #define FILE_INCOMPLETE_ID 0xfcfcfcfc
761 #define FILE_INCOMPLETE_PORT 0xfcfc
762 // complete file: ip 251.251.251 (0xfbfbfbfb) port 0xfbfb
763 // incomplete file: op 252.252.252 (0xfcfcfcfc) port 0xfcfc
764 if (cur_file
->GetStatus() == PS_COMPLETE
) {
765 nClientID
= FILE_COMPLETE_ID
;
766 nClientPort
= FILE_COMPLETE_PORT
;
768 nClientID
= FILE_INCOMPLETE_ID
;
769 nClientPort
= FILE_INCOMPLETE_PORT
;
772 if (theApp
->IsConnectedED2K() && !::IsLowID(theApp
->GetED2KID())){
773 nClientID
= theApp
->GetID();
774 nClientPort
= thePrefs::GetPort();
778 // Do not merge this with the above case - this one
779 // also checks Kad status.
780 if (theApp
->IsConnected() && !theApp
->IsFirewalled()) {
781 nClientID
= theApp
->GetID();
782 nClientPort
= thePrefs::GetPort();
786 files
->WriteUInt32(nClientID
);
787 files
->WriteUInt16(nClientPort
);
791 // The printable filename is used because it's destined for another user.
792 tags
.push_back(new CTagString(FT_FILENAME
, cur_file
->GetFileName().GetPrintable()));
794 if (pClient
&& pClient
->GetVBTTags()) {
795 tags
.push_back(new CTagVarInt(FT_FILESIZE
, cur_file
->GetFileSize()));
797 if (!cur_file
->IsLargeFile()){
798 tags
.push_back(new CTagInt32(FT_FILESIZE
, cur_file
->GetFileSize()));
801 // we send 2*32 bit tags to servers, but a real 64 bit tag to other clients.
803 if (!pServer
->SupportsLargeFilesTCP()){
805 tags
.push_back(new CTagInt32(FT_FILESIZE
, 0));
807 tags
.push_back(new CTagInt32(FT_FILESIZE
, (uint32
)cur_file
->GetFileSize()));
808 tags
.push_back(new CTagInt32(FT_FILESIZE_HI
, (uint32
)(cur_file
->GetFileSize() >> 32)));
811 if (!pClient
->SupportsLargeFiles()) {
813 tags
.push_back(new CTagInt32(FT_FILESIZE
, 0));
815 tags
.push_back(new CTagInt64(FT_FILESIZE
, cur_file
->GetFileSize()));
821 if (cur_file
->GetFileRating()) {
822 tags
.push_back(new CTagVarInt(FT_FILERATING
, cur_file
->GetFileRating(), (pClient
&& pClient
->GetVBTTags()) ? 0 : 32));
825 // NOTE: Archives and CD-Images are published+searched with file type "Pro"
826 bool bAddedFileType
= false;
827 if (pServer
&& (pServer
->GetTCPFlags() & SRV_TCPFLG_TYPETAGINTEGER
)) {
828 // Send integer file type tags to newer servers
829 EED2KFileType eFileType
= GetED2KFileTypeSearchID(GetED2KFileTypeID(cur_file
->GetFileName()));
830 if (eFileType
>= ED2KFT_AUDIO
&& eFileType
<= ED2KFT_CDIMAGE
) {
831 tags
.push_back(new CTagInt32(FT_FILETYPE
, eFileType
));
832 bAddedFileType
= true;
835 if (!bAddedFileType
) {
836 // Send string file type tags to:
837 // - newer servers, in case there is no integer type available for the file type (e.g. emulecollection)
840 wxString
strED2KFileType(GetED2KFileTypeSearchTerm(GetED2KFileTypeID(cur_file
->GetFileName())));
841 if (!strED2KFileType
.IsEmpty()) {
842 tags
.push_back(new CTagString(FT_FILETYPE
, strED2KFileType
));
846 // There, we could add MetaData info, if we ever get to have that.
850 bool unicode_support
=
851 // eservers that support UNICODE.
852 (pServer
&& (pServer
->GetUnicodeSupport()))
854 // clients that support unicode
855 (pClient
&& pClient
->GetUnicodeSupport());
856 eStrEncode
= unicode_support
? utf8strRaw
: utf8strNone
;
858 files
->WriteUInt32(tags
.size());
860 // Sadly, eMule doesn't use a MISCOPTIONS flag on hello packet for this, so we
861 // have to identify the support for new tags by version.
863 // eMule client > 0.42f
864 (pClient
&& pClient
->IsEmuleClient() && pClient
->GetVersion() >= MAKE_CLIENT_VERSION(0,42,7))
866 // aMule >= 2.0.0rc8. Sadly, there's no way to check the rcN number, so I checked
867 // the rc8 changelog. On rc8 OSInfo was introduced, so...
868 (pClient
&& pClient
->GetClientSoft() == SO_AMULE
&& !pClient
->GetClientOSInfo().IsEmpty())
870 // eservers use a flag for this, at least.
871 (pServer
&& (pServer
->GetTCPFlags() & SRV_TCPFLG_NEWTAGS
));
873 for (TagPtrList::iterator it
= tags
.begin(); it
!= tags
.end(); ++it
) {
876 pTag
->WriteNewEd2kTag(files
, eStrEncode
);
878 pTag
->WriteTagToFile(files
, eStrEncode
);
885 void CSharedFileList::Process()
888 if( !m_lastPublishED2KFlag
|| ( ::GetTickCount() - m_lastPublishED2K
< ED2KREPUBLISHTIME
) ) {
892 m_lastPublishED2K
= ::GetTickCount();
895 void CSharedFileList::Publish()
897 // Variables to save cpu.
898 unsigned int tNow
= time(NULL
);
899 bool IsFirewalled
= theApp
->IsFirewalled();
901 if( Kademlia::CKademlia::IsConnected() && ( !IsFirewalled
|| ( IsFirewalled
&& theApp
->clientlist
->GetBuddyStatus() == Connected
)) && GetCount() && Kademlia::CKademlia::GetPublish()) {
902 //We are connected to Kad. We are either open or have a buddy. And Kad is ready to start publishing.
904 if( Kademlia::CKademlia::GetTotalStoreKey() < KADEMLIATOTALSTOREKEY
) {
906 //We are not at the max simultaneous keyword publishes
907 if (tNow
>= m_keywords
->GetNextPublishTime()) {
909 //Enough time has passed since last keyword publish
911 //Get the next keyword which has to be (re)-published
912 CPublishKeyword
* pPubKw
= m_keywords
->GetNextKeyword();
915 //We have the next keyword to check if it can be published
917 //Debug check to make sure things are going well.
918 wxASSERT( pPubKw
->GetRefCount() != 0 );
920 if (tNow
>= pPubKw
->GetNextPublishTime()) {
921 //This keyword can be published.
922 Kademlia::CSearch
* pSearch
= Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::STOREKEYWORD
, false, pPubKw
->GetKadID());
924 //pSearch was created. Which means no search was already being done with this HashID.
925 //This also means that it was checked to see if network load wasn't a factor.
927 //This sets the filename into the search object so we can show it in the gui.
928 pSearch
->SetFileName(pPubKw
->GetKeyword());
930 //Add all file IDs which relate to the current keyword to be published
931 const KnownFileArray
& aFiles
= pPubKw
->GetReferences();
933 for (unsigned int f
= 0; f
< aFiles
.size(); ++f
) {
935 //Only publish complete files as someone else should have the full file to publish these keywords.
936 //As a side effect, this may help reduce people finding incomplete files in the network.
937 if( !aFiles
[f
]->IsPartFile() ) {
939 pSearch
->AddFileID(Kademlia::CUInt128(aFiles
[f
]->GetFileHash().GetHash()));
941 //We only publish up to 150 files per keyword publish then rotate the list.
942 pPubKw
->RotateReferences(f
);
949 //Start our keyword publish
950 pPubKw
->SetNextPublishTime(tNow
+(KADEMLIAREPUBLISHTIMEK
));
951 pPubKw
->IncPublishedCount();
952 Kademlia::CSearchManager::StartSearch(pSearch
);
954 //There were no valid files to publish with this keyword.
960 m_keywords
->SetNextPublishTime(KADEMLIAPUBLISHTIME
+tNow
);
964 if( Kademlia::CKademlia::GetTotalStoreSrc() < KADEMLIATOTALSTORESRC
) {
965 if(tNow
>= m_lastPublishKadSrc
) {
966 if(m_currFileSrc
> GetCount()) {
969 CKnownFile
* pCurKnownFile
= const_cast<CKnownFile
*>(GetFileByIndex(m_currFileSrc
));
971 if(pCurKnownFile
->PublishSrc()) {
972 Kademlia::CUInt128 kadFileID
;
973 kadFileID
.SetValueBE(pCurKnownFile
->GetFileHash().GetHash());
974 if(Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::STOREFILE
, true, kadFileID
)==NULL
) {
975 pCurKnownFile
->SetLastPublishTimeKadSrc(0,0);
981 // even if we did not publish a source, reset the timer so that this list is processed
982 // only every KADEMLIAPUBLISHTIME seconds.
983 m_lastPublishKadSrc
= KADEMLIAPUBLISHTIME
+tNow
;
987 if( Kademlia::CKademlia::GetTotalStoreNotes() < KADEMLIATOTALSTORENOTES
) {
988 if(tNow
>= m_lastPublishKadNotes
) {
989 if(m_currFileNotes
> GetCount()) {
992 CKnownFile
* pCurKnownFile
= const_cast<CKnownFile
*>(GetFileByIndex(m_currFileNotes
));
994 if(pCurKnownFile
->PublishNotes()) {
995 Kademlia::CUInt128 kadFileID
;
996 kadFileID
.SetValueBE(pCurKnownFile
->GetFileHash().GetHash());
997 if(Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::STORENOTES
, true, kadFileID
)==NULL
)
998 pCurKnownFile
->SetLastPublishTimeKadNotes(0);
1003 // even if we did not publish a source, reset the timer so that this list is processed
1004 // only every KADEMLIAPUBLISHTIME seconds.
1005 m_lastPublishKadNotes
= KADEMLIAPUBLISHTIME
+tNow
;
1012 void CSharedFileList::AddKeywords(CKnownFile
* pFile
)
1014 m_keywords
->AddKeywords(pFile
);
1018 void CSharedFileList::RemoveKeywords(CKnownFile
* pFile
)
1020 m_keywords
->RemoveKeywords(pFile
);
1024 bool CSharedFileList::RenameFile(CKnownFile
* file
, const CPath
& newName
)
1026 if (file
->IsPartFile()) {
1027 CPartFile
* pfile
= dynamic_cast<CPartFile
*>(file
);
1029 if (file
->GetStatus() != PS_COMPLETING
) {
1030 pfile
->SetFileName(newName
);
1031 pfile
->SavePartFile();
1033 Notify_SharedFilesUpdateItem(file
);
1034 Notify_DownloadCtrlUpdateItem(file
);
1039 //#warning Renaming of completed files causes problems on kad. Enable when reviewed.
1041 wxString oldPath
= JoinPaths(file
->GetFilePath(), file
->GetFileName());
1042 wxString newPath
= JoinPaths(file
->GetFilePath(), newName
);
1044 if (UTF8_MoveFile(oldPath
, newPath
)) {
1045 RemoveKeywords(file
);
1046 file
->SetFileName(newName
);
1048 theApp
->knownfiles
->Save();
1050 RepublishFile(file
);
1052 Notify_DownloadCtrlUpdateItem(file
);
1053 Notify_SharedFilesUpdateItem(file
);
1064 void CSharedFileList::CheckAICHHashes(const std::list
<CAICHHash
>& hashes
)
1066 wxMutexLocker
locker(list_mut
);
1068 // Now we check that all files which are in the sharedfilelist have a
1069 // corresponding hash in our list. Those how don't are queued for hashing.
1070 CKnownFileMap::iterator it
= m_Files_map
.begin();
1071 for (; it
!= m_Files_map
.end(); ++it
) {
1072 const CKnownFile
* file
= it
->second
;
1074 if (file
->IsPartFile() == false) {
1075 CAICHHashSet
* hashset
= file
->GetAICHHashset();
1077 if (hashset
->GetStatus() == AICH_HASHSETCOMPLETE
) {
1078 if (std::find(hashes
.begin(), hashes
.end(), hashset
->GetMasterHash()) != hashes
.end()) {
1083 hashset
->SetStatus(AICH_ERROR
);
1085 CThreadScheduler::AddTask(new CHashingTask(file
));
1091 // File_checked_for_headers