2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 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/kad/Constants.h>
30 #include <tags/FileTags.h>
34 #include "Packet.h" // Needed for CPacket
35 #include "MemFile.h" // Needed for CMemFile
36 #include "ServerConnect.h" // Needed for CServerConnect
37 #include "KnownFileList.h" // Needed for CKnownFileList
38 #include "ThreadTasks.h" // Needed for CThreadScheduler and CHasherTask
39 #include "Preferences.h" // Needed for thePrefs
40 #include "DownloadQueue.h" // Needed for CDownloadQueue
41 #include "amule.h" // Needed for theApp
42 #include "PartFile.h" // Needed for PartFile
43 #include "Server.h" // Needed for CServer
44 #include "Statistics.h" // Needed for theStats
46 #include <common/Format.h>
47 #include <common/FileFunctions.h>
48 #include "GuiEvents.h" // Needed for Notify_*
49 #include "SHAHashSet.h" // Needed for CAICHHash
52 #include "kademlia/kademlia/Kademlia.h"
53 #include "kademlia/kademlia/Search.h"
54 #include "ClientList.h"
56 typedef std::deque
<CKnownFile
*> KnownFileArray
;
58 ///////////////////////////////////////////////////////////////////////////////
64 CPublishKeyword(const wxString
& rstrKeyword
)
66 m_strKeyword
= rstrKeyword
;
67 // min. keyword char is allowed to be < 3 in some cases (see also 'CSearchManager::getWords')
68 //ASSERT( rstrKeyword.GetLength() >= 3 );
69 wxASSERT( !rstrKeyword
.IsEmpty() );
70 KadGetKeywordHash(rstrKeyword
, &m_nKadID
);
71 SetNextPublishTime(0);
75 const Kademlia::CUInt128
& GetKadID() const { return m_nKadID
; }
76 const wxString
& GetKeyword() const { return m_strKeyword
; }
77 int GetRefCount() const { return m_aFiles
.size(); }
78 const KnownFileArray
& GetReferences() const { return m_aFiles
; }
80 uint32
GetNextPublishTime() const { return m_tNextPublishTime
; }
81 void SetNextPublishTime(uint32 tNextPublishTime
) { m_tNextPublishTime
= tNextPublishTime
; }
83 uint32
GetPublishedCount() const { return m_uPublishedCount
; }
84 void SetPublishedCount(uint32 uPublishedCount
) { m_uPublishedCount
= uPublishedCount
; }
85 void IncPublishedCount() { m_uPublishedCount
++; }
87 bool AddRef(CKnownFile
* pFile
) {
88 if (std::find(m_aFiles
.begin(), m_aFiles
.end(), pFile
) != m_aFiles
.end()) {
92 m_aFiles
.push_back(pFile
);
96 int RemoveRef(CKnownFile
* pFile
) {
97 KnownFileArray::iterator it
= std::find(m_aFiles
.begin(), m_aFiles
.end(), pFile
);
98 if (it
!= m_aFiles
.end()) {
101 return m_aFiles
.size();
104 void RemoveAllReferences() {
108 void RotateReferences(unsigned iRotateSize
) {
109 wxCHECK_RET(m_aFiles
.size(), wxT("RotateReferences: Rotating empty array"));
111 unsigned shift
= (iRotateSize
% m_aFiles
.size());
112 std::rotate(m_aFiles
.begin(), m_aFiles
.begin() + shift
, m_aFiles
.end());
116 wxString m_strKeyword
;
117 Kademlia::CUInt128 m_nKadID
;
118 uint32 m_tNextPublishTime
;
119 uint32 m_uPublishedCount
;
120 KnownFileArray m_aFiles
;
124 ///////////////////////////////////////////////////////////////////////////////
125 // CPublishKeywordList
127 class CPublishKeywordList
130 CPublishKeywordList();
131 ~CPublishKeywordList();
133 void AddKeyword(const wxString
& keyword
, CKnownFile
*file
);
134 void AddKeywords(CKnownFile
* pFile
);
135 void RemoveKeyword(const wxString
& keyword
, CKnownFile
*file
);
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::AddKeyword(const wxString
& keyword
, CKnownFile
*file
)
207 CPublishKeyword
* pubKw
= FindKeyword(keyword
);
209 pubKw
= new CPublishKeyword(keyword
);
210 m_lstKeywords
.push_back(pubKw
);
211 SetNextPublishTime(0);
216 void CPublishKeywordList::AddKeywords(CKnownFile
* pFile
)
218 const Kademlia::WordList
& wordlist
= pFile
->GetKadKeywords();
220 Kademlia::WordList::const_iterator it
;
221 for (it
= wordlist
.begin(); it
!= wordlist
.end(); ++it
) {
222 AddKeyword(*it
, pFile
);
226 void CPublishKeywordList::RemoveKeyword(const wxString
& keyword
, CKnownFile
*file
)
228 CKeyWordList::iterator pos
;
229 CPublishKeyword
* pubKw
= FindKeyword(keyword
, &pos
);
231 if (pubKw
->RemoveRef(file
) == 0) {
232 if (pos
== m_posNextKeyword
) {
235 m_lstKeywords
.erase(pos
);
237 SetNextPublishTime(0);
242 void CPublishKeywordList::RemoveKeywords(CKnownFile
* pFile
)
244 const Kademlia::WordList
& wordlist
= pFile
->GetKadKeywords();
245 Kademlia::WordList::const_iterator it
;
246 for (it
= wordlist
.begin(); it
!= wordlist
.end(); ++it
) {
247 RemoveKeyword(*it
, pFile
);
252 void CPublishKeywordList::RemoveAllKeywords()
254 DeleteContents(m_lstKeywords
);
256 SetNextPublishTime(0);
260 void CPublishKeywordList::RemoveAllKeywordReferences()
262 CKeyWordList::iterator it
= m_lstKeywords
.begin();
263 for (; it
!= m_lstKeywords
.end(); ++it
) {
264 (*it
)->RemoveAllReferences();
269 void CPublishKeywordList::PurgeUnreferencedKeywords()
271 CKeyWordList::iterator it
= m_lstKeywords
.begin();
272 while (it
!= m_lstKeywords
.end()) {
273 CPublishKeyword
* pPubKw
= *it
;
274 if (pPubKw
->GetRefCount() == 0) {
275 if (it
== m_posNextKeyword
) {
278 m_lstKeywords
.erase(it
++);
280 SetNextPublishTime(0);
288 CSharedFileList::CSharedFileList(CKnownFileList
* in_filelist
){
289 filelist
= in_filelist
;
291 m_lastPublishED2K
= 0;
292 m_lastPublishED2KFlag
= true;
294 m_keywords
= new CPublishKeywordList
;
297 m_lastPublishKadSrc
= 0;
298 m_lastPublishKadNotes
= 0;
303 CSharedFileList::~CSharedFileList()
309 void CSharedFileList::FindSharedFiles()
311 /* Abort loading if we are shutting down. */
312 if(theApp
->IsOnShutDown()) {
317 theStats::ClearSharedFilesInfo();
319 // Reload shareddir.dat
320 theApp
->glob_prefs
->ReloadSharedFolders();
323 wxMutexLocker
lock(list_mut
);
327 // All part files are automatically shared.
328 for ( uint32 i
= 0; i
< theApp
->downloadqueue
->GetFileCount(); ++i
) {
329 CPartFile
* file
= theApp
->downloadqueue
->GetFileByIndex( i
);
331 if ( file
->GetStatus(true) == PS_READY
) {
332 AddLogLineNS(CFormat(_("Adding file %s to shares"))
333 % file
->GetFullName().GetPrintable());
338 // Create a list of all shared paths and weed out duplicates.
339 std::list
<CPath
> sharedPaths
;
341 // Global incoming dir and all category incoming directories are automatically shared.
342 sharedPaths
.push_back(thePrefs::GetIncomingDir());
343 for (unsigned int i
= 1;i
< theApp
->glob_prefs
->GetCatCount(); ++i
) {
344 sharedPaths
.push_back(theApp
->glob_prefs
->GetCatPath(i
));
347 const thePrefs::PathList
& shared
= theApp
->glob_prefs
->shareddir_list
;
348 sharedPaths
.insert(sharedPaths
.end(), shared
.begin(), shared
.end());
351 sharedPaths
.unique();
353 filelist
->PrepareIndex();
354 // Gathering is done in the foreground and can be slowed down severely by parallel background hashing.
355 // So just store the hashing tasks for now.
357 for (std::list
<CPath
>::iterator it
= sharedPaths
.begin(); it
!= sharedPaths
.end(); ++it
) {
358 AddFilesFromDirectory(*it
, hashTasks
);
360 filelist
->ReleaseIndex();
362 // Now that the shared files are gathered feed the hashing tasks to the scheduler to start hashing.
363 unsigned addedFiles
= 0;
364 for (TaskList::iterator it
= hashTasks
.begin(); it
!= hashTasks
.end(); ++it
) {
365 if (CThreadScheduler::AddTask(*it
)) {
370 if (addedFiles
== 0) {
371 AddLogLineN(CFormat(wxPLURAL("Found %i known shared file", "Found %i known shared files", GetCount())) % GetCount());
373 // Make sure the AICH-hashes are up to date.
374 CThreadScheduler::AddTask(new CAICHSyncTask());
376 // New files, AICH thread will be run at the end of the hashing thread.
377 AddLogLineN(CFormat(wxPLURAL("Found %i known shared file, %i unknown", "Found %i known shared files, %i unknown", GetCount())) % GetCount() % addedFiles
);
382 // Checks if the dir a is the same as b. If they are, then logs the message and returns true.
383 bool CheckDirectory(const wxString
& a
, const CPath
& b
)
385 if (CPath(a
).IsSameDir(b
)) {
386 AddLogLineC(CFormat( _("ERROR: Attempted to share %s") ) % a
);
395 unsigned CSharedFileList::AddFilesFromDirectory(const CPath
& directory
, TaskList
& hashTasks
)
397 // Do not allow these folders to be shared:
398 // - The .aMule folder
400 // - The users home-dir
401 if (CheckDirectory(wxGetHomeDir(), directory
)) {
403 } else if (CheckDirectory(theApp
->ConfigDir
, directory
)) {
405 } else if (CheckDirectory(thePrefs::GetTempDir().GetRaw(), directory
)) {
409 if (!directory
.DirExists()) {
410 AddLogLineNS(CFormat(_("Shared directory not found, skipping: %s"))
411 % directory
.GetPrintable());
416 CDirIterator::FileType searchFor
= CDirIterator::FileNoHidden
;
417 if (thePrefs::ShareHiddenFiles()) {
418 searchFor
= CDirIterator::File
;
421 unsigned knownFiles
= 0;
422 unsigned addedFiles
= 0;
424 CDirIterator
SharedDir(directory
);
426 for (CPath fname
= SharedDir
.GetFirstFile(searchFor
); fname
.IsOk(); fname
= SharedDir
.GetNextFile()) {
427 CPath fullPath
= directory
.JoinPaths(fname
);
429 if (!fullPath
.FileExists()) {
430 AddDebugLogLineN(logKnownFiles
,
431 CFormat(wxT("Shared file does not exist (possibly a broken link): %s")) % fullPath
);
435 AddDebugLogLineN(logKnownFiles
,
436 CFormat(wxT("Found shared file: %s")) % fullPath
);
438 time_t fdate
= CPath::GetModificationTime(fullPath
);
439 sint64 fsize
= fullPath
.GetFileSize();
441 // This will also catch files with too strict permissions.
442 if ((fdate
== (time_t)-1) || (fsize
== wxInvalidOffset
)) {
443 AddDebugLogLineN(logKnownFiles
,
444 CFormat(wxT("Failed to retrieve modification time or size for '%s', skipping.")) % fullPath
);
449 AddDebugLogLineN(logKnownFiles
,
450 CFormat(wxT("Skip zero size file '%s'")) % fullPath
);
455 CKnownFile
* toadd
= filelist
->FindKnownFile(fname
, fdate
, fsize
);
458 if (AddFile(toadd
)) {
459 AddDebugLogLineN(logKnownFiles
,
460 CFormat(wxT("Added known file '%s' to shares"))
463 toadd
->SetFilePath(directory
);
465 AddDebugLogLineN(logKnownFiles
,
466 CFormat(wxT("File already shared, skipping: %s"))
470 //not in knownfilelist - start adding thread to hash file
471 AddDebugLogLineN(logKnownFiles
,
472 CFormat(wxT("Hashing new unknown shared file '%s'")) % fname
);
474 hashTasks
.push_back(new CHashingTask(directory
, fname
));
479 if ((addedFiles
== 0) && (knownFiles
== 0)) {
480 AddLogLineN(CFormat(_("No shareable files found in directory: %s"))
481 % directory
.GetPrintable());
488 bool CSharedFileList::AddFile(CKnownFile
* pFile
)
490 wxASSERT(pFile
->GetHashCount() == pFile
->GetED2KPartHashCount());
492 wxMutexLocker
lock(list_mut
);
494 CKnownFileMap::value_type
entry(pFile
->GetFileHash(), pFile
);
495 if (m_Files_map
.insert(entry
).second
) {
496 /* Keywords to publish on Kad */
497 m_keywords
->AddKeywords(pFile
);
498 theStats::AddSharedFile(pFile
->GetFileSize());
505 void CSharedFileList::SafeAddKFile(CKnownFile
* toadd
, bool bOnlyAdd
)
507 // TODO: Check if the file is already known - only with another date
509 if (AddFile(toadd
)) {
510 Notify_SharedFilesShowFile(toadd
);
513 if (!bOnlyAdd
&& theApp
->IsConnectedED2K()) {
514 // Publishing of files is not anymore handled here.
515 // Instead, the timer does it by itself.
516 m_lastPublishED2KFlag
= true;
521 // removes first occurrence of 'toremove' in 'list'
522 void CSharedFileList::RemoveFile(CKnownFile
* toremove
){
523 Notify_SharedFilesRemoveFile(toremove
);
524 wxMutexLocker
lock(list_mut
);
525 if (m_Files_map
.erase(toremove
->GetFileHash()) > 0) {
526 theStats::RemoveSharedFile(toremove
->GetFileSize());
528 /* This file keywords must not be published to kad anymore */
529 m_keywords
->RemoveKeywords(toremove
);
533 void CSharedFileList::Reload()
535 // Madcat - Disable reloading if reloading already in progress.
536 // Kry - Fixed to let non-english language users use the 'Reload' button :P
537 // deltaHF - removed the old ugly button and changed the code to use the new small one
538 // Kry - bah, let's use a var.
540 AddDebugLogLineN(logKnownFiles
, wxT("Reload shared files"));
542 Notify_SharedFilesRemoveAllItems();
544 /* All Kad keywords must be removed */
545 m_keywords
->RemoveAllKeywordReferences();
547 /* Public identifiers must be erased as they might be invalid now */
548 m_PublicSharedDirNames
.clear();
552 /* And now the unreferenced keywords must be removed also */
553 m_keywords
->PurgeUnreferencedKeywords();
555 Notify_SharedFilesShowFileList();
562 const CKnownFile
*CSharedFileList::GetFileByIndex(unsigned int index
) const
564 wxMutexLocker
lock(list_mut
);
565 if ( index
>= m_Files_map
.size() ) {
568 CKnownFileMap::const_iterator pos
= m_Files_map
.begin();
569 std::advance(pos
, index
);
574 CKnownFile
* CSharedFileList::GetFileByID(const CMD4Hash
& filehash
)
576 wxMutexLocker
lock(list_mut
);
577 CKnownFileMap::iterator it
= m_Files_map
.find(filehash
);
579 if ( it
!= m_Files_map
.end() ) {
586 short CSharedFileList::GetFilePriorityByID(const CMD4Hash
& filehash
)
588 CKnownFile
* tocheck
= GetFileByID(filehash
);
590 return tocheck
->GetUpPriority();
592 return -10; // file doesn't exist
596 void CSharedFileList::CopyFileList(std::vector
<CKnownFile
*>& out_list
) const
598 wxMutexLocker
lock(list_mut
);
600 out_list
.reserve(m_Files_map
.size());
602 CKnownFileMap::const_iterator it
= m_Files_map
.begin();
603 it
!= m_Files_map
.end();
606 out_list
.push_back(it
->second
);
612 void CSharedFileList::UpdateItem(CKnownFile
* toupdate
)
614 Notify_SharedFilesUpdateItem(toupdate
);
618 void CSharedFileList::GetSharedFilesByDirectory(const wxString
& directory
,
619 CKnownFilePtrList
& list
)
621 wxMutexLocker
lock(list_mut
);
623 const CPath dir
= CPath(directory
);
624 for (CKnownFileMap::iterator pos
= m_Files_map
.begin();
625 pos
!= m_Files_map
.end(); ++pos
) {
626 CKnownFile
*cur_file
= pos
->second
;
628 if (dir
.IsSameDir(cur_file
->GetFilePath())) {
632 list
.push_back(cur_file
);
636 /* ---------------- Network ----------------- */
638 void CSharedFileList::ClearED2KPublishInfo(){
639 CKnownFile
* cur_file
;
640 m_lastPublishED2KFlag
= true;
641 wxMutexLocker
lock(list_mut
);
642 for (CKnownFileMap::iterator pos
= m_Files_map
.begin(); pos
!= m_Files_map
.end(); ++pos
) {
643 cur_file
= pos
->second
;
644 cur_file
->SetPublishedED2K(false);
648 void CSharedFileList::ClearKadSourcePublishInfo()
650 wxMutexLocker
lock(list_mut
);
651 CKnownFile
* cur_file
;
652 for (CKnownFileMap::iterator pos
= m_Files_map
.begin(); pos
!= m_Files_map
.end(); ++pos
) {
653 cur_file
= pos
->second
;
654 cur_file
->SetLastPublishTimeKadSrc(0,0);
658 void CSharedFileList::RepublishFile(CKnownFile
* pFile
)
660 CServer
* server
= theApp
->serverconnect
->GetCurrentServer();
661 if (server
&& (server
->GetTCPFlags() & SRV_TCPFLG_COMPRESSION
)) {
662 m_lastPublishED2KFlag
= true;
663 pFile
->SetPublishedED2K(false); // FIXME: this creates a wrong 'No' for the ed2k shared info in the listview until the file is shared again.
667 uint8
GetRealPrio(uint8 in
)
679 bool SortFunc( const CKnownFile
* fileA
, const CKnownFile
* fileB
)
681 return GetRealPrio(fileA
->GetUpPriority()) < GetRealPrio(fileB
->GetUpPriority());
684 void CSharedFileList::SendListToServer(){
685 std::vector
<CKnownFile
*> SortedList
;
688 wxMutexLocker
lock(list_mut
);
690 if (m_Files_map
.empty() || !theApp
->IsConnectedED2K() ) {
694 // Getting a sorted list of the non-published files.
695 SortedList
.reserve( m_Files_map
.size() );
697 CKnownFileMap::iterator it
= m_Files_map
.begin();
698 for ( ; it
!= m_Files_map
.end(); ++it
) {
699 if (!it
->second
->GetPublishedED2K()) {
700 SortedList
.push_back( it
->second
);
705 std::sort( SortedList
.begin(), SortedList
.end(), SortFunc
);
707 // Limits for the server.
709 CServer
* server
= theApp
->serverconnect
->GetCurrentServer();
711 uint32 limit
= server
? server
->GetSoftFiles() : 0;
712 if( limit
== 0 || limit
> 200 ) {
716 if( (uint32
)SortedList
.size() < limit
) {
717 limit
= SortedList
.size();
719 m_lastPublishED2KFlag
= false;
727 files
.WriteUInt32(limit
);
731 std::vector
<CKnownFile
*>::iterator sorted_it
= SortedList
.begin();
732 for ( ; (sorted_it
!= SortedList
.end()) && (count
< limit
); ++sorted_it
) {
733 CKnownFile
* file
= *sorted_it
;
734 if (!file
->IsLargeFile() || (server
&& server
->SupportsLargeFilesTCP())) {
735 file
->CreateOfferedFilePacket(&files
, server
, NULL
);
737 file
->SetPublishedED2K(true);
741 wxASSERT(count
== limit
);
743 CPacket
* packet
= new CPacket(files
, OP_EDONKEYPROT
, OP_OFFERFILES
);
745 // - this kind of data is highly compressable (N * (1 MD4 and at least 3 string meta data tags and 1 integer meta data tag))
746 // - the min. amount of data needed for one published file is ~100 bytes
747 // - this function is called once when connecting to a server and when a file becomes shareable - so, it's called rarely.
748 // - if the compressed size is still >= the original size, we send the uncompressed packet
749 // therefor we always try to compress the packet
750 if (server
->GetTCPFlags() & SRV_TCPFLG_COMPRESSION
){
751 packet
->PackPacket();
754 theStats::AddUpOverheadServer(packet
->GetPacketSize());
755 theApp
->serverconnect
->SendPacket(packet
,true);
759 void CSharedFileList::Process()
762 if( !m_lastPublishED2KFlag
|| ( ::GetTickCount() - m_lastPublishED2K
< ED2KREPUBLISHTIME
) ) {
766 m_lastPublishED2K
= ::GetTickCount();
769 void CSharedFileList::Publish()
771 // Variables to save cpu.
772 unsigned int tNow
= time(NULL
);
773 bool IsFirewalled
= theApp
->IsFirewalled();
775 if( Kademlia::CKademlia::IsConnected() && ( !IsFirewalled
|| ( IsFirewalled
&& theApp
->clientlist
->GetBuddyStatus() == Connected
)) && GetCount() && Kademlia::CKademlia::GetPublish()) {
776 //We are connected to Kad. We are either open or have a buddy. And Kad is ready to start publishing.
778 if( Kademlia::CKademlia::GetTotalStoreKey() < KADEMLIATOTALSTOREKEY
) {
780 //We are not at the max simultaneous keyword publishes
781 if (tNow
>= m_keywords
->GetNextPublishTime()) {
783 //Enough time has passed since last keyword publish
785 //Get the next keyword which has to be (re)-published
786 CPublishKeyword
* pPubKw
= m_keywords
->GetNextKeyword();
789 //We have the next keyword to check if it can be published
791 //Debug check to make sure things are going well.
792 wxASSERT( pPubKw
->GetRefCount() != 0 );
794 if (tNow
>= pPubKw
->GetNextPublishTime()) {
795 //This keyword can be published.
796 Kademlia::CSearch
* pSearch
= Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::STOREKEYWORD
, false, pPubKw
->GetKadID());
798 //pSearch was created. Which means no search was already being done with this HashID.
799 //This also means that it was checked to see if network load wasn't a factor.
801 //This sets the filename into the search object so we can show it in the gui.
802 pSearch
->SetFileName(pPubKw
->GetKeyword());
804 //Add all file IDs which relate to the current keyword to be published
805 const KnownFileArray
& aFiles
= pPubKw
->GetReferences();
807 for (unsigned int f
= 0; f
< aFiles
.size(); ++f
) {
809 //Only publish complete files as someone else should have the full file to publish these keywords.
810 //As a side effect, this may help reduce people finding incomplete files in the network.
811 if( !aFiles
[f
]->IsPartFile() ) {
813 pSearch
->AddFileID(Kademlia::CUInt128(aFiles
[f
]->GetFileHash().GetHash()));
815 //We only publish up to 150 files per keyword publish then rotate the list.
816 pPubKw
->RotateReferences(f
);
823 //Start our keyword publish
824 pPubKw
->SetNextPublishTime(tNow
+(KADEMLIAREPUBLISHTIMEK
));
825 pPubKw
->IncPublishedCount();
826 Kademlia::CSearchManager::StartSearch(pSearch
);
828 //There were no valid files to publish with this keyword.
834 m_keywords
->SetNextPublishTime(KADEMLIAPUBLISHTIME
+tNow
);
838 if( Kademlia::CKademlia::GetTotalStoreSrc() < KADEMLIATOTALSTORESRC
) {
839 if(tNow
>= m_lastPublishKadSrc
) {
840 if(m_currFileSrc
> GetCount()) {
843 CKnownFile
* pCurKnownFile
= const_cast<CKnownFile
*>(GetFileByIndex(m_currFileSrc
));
845 if(pCurKnownFile
->PublishSrc()) {
846 Kademlia::CUInt128 kadFileID
;
847 kadFileID
.SetValueBE(pCurKnownFile
->GetFileHash().GetHash());
848 if(Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::STOREFILE
, true, kadFileID
)==NULL
) {
849 pCurKnownFile
->SetLastPublishTimeKadSrc(0,0);
855 // even if we did not publish a source, reset the timer so that this list is processed
856 // only every KADEMLIAPUBLISHTIME seconds.
857 m_lastPublishKadSrc
= KADEMLIAPUBLISHTIME
+tNow
;
861 if( Kademlia::CKademlia::GetTotalStoreNotes() < KADEMLIATOTALSTORENOTES
) {
862 if(tNow
>= m_lastPublishKadNotes
) {
863 if(m_currFileNotes
> GetCount()) {
866 CKnownFile
* pCurKnownFile
= const_cast<CKnownFile
*>(GetFileByIndex(m_currFileNotes
));
868 if(pCurKnownFile
->PublishNotes()) {
869 Kademlia::CUInt128 kadFileID
;
870 kadFileID
.SetValueBE(pCurKnownFile
->GetFileHash().GetHash());
871 if(Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::STORENOTES
, true, kadFileID
)==NULL
)
872 pCurKnownFile
->SetLastPublishTimeKadNotes(0);
877 // even if we did not publish a source, reset the timer so that this list is processed
878 // only every KADEMLIAPUBLISHTIME seconds.
879 m_lastPublishKadNotes
= KADEMLIAPUBLISHTIME
+tNow
;
886 void CSharedFileList::AddKeywords(CKnownFile
* pFile
)
888 m_keywords
->AddKeywords(pFile
);
892 void CSharedFileList::RemoveKeywords(CKnownFile
* pFile
)
894 m_keywords
->RemoveKeywords(pFile
);
898 bool CSharedFileList::RenameFile(CKnownFile
* file
, const CPath
& newName
)
900 if (file
->IsPartFile()) {
901 CPartFile
* pfile
= dynamic_cast<CPartFile
*>(file
);
903 if (file
->GetStatus() != PS_COMPLETING
) {
904 pfile
->SetFileName(newName
);
905 pfile
->SavePartFile();
907 Notify_SharedFilesUpdateItem(file
);
908 Notify_DownloadCtrlUpdateItem(file
);
913 CPath oldPath
= file
->GetFilePath().JoinPaths(file
->GetFileName());
914 CPath newPath
= file
->GetFilePath().JoinPaths(newName
);
916 if (CPath::RenameFile(oldPath
, newPath
)) {
917 // Must create a copy of the word list because:
918 // 1) it will be reset on SetFileName()
919 // 2) we will want to edit it
920 Kademlia::WordList oldwords
= file
->GetKadKeywords();
921 file
->SetFileName(newName
);
922 theApp
->knownfiles
->Save();
926 const Kademlia::WordList
& newwords
= file
->GetKadKeywords();
927 Kademlia::WordList::iterator itold
;
928 Kademlia::WordList::const_iterator itnew
;
929 // compare keywords in old and new names
930 for (itnew
= newwords
.begin(); itnew
!= newwords
.end(); ++itnew
) {
931 for (itold
= oldwords
.begin(); itold
!= oldwords
.end(); ++itold
) {
932 if (*itold
== *itnew
) {
936 if (itold
!= oldwords
.end()) {
937 // Remove keyword from old name which also exist in new name
938 oldwords
.erase(itold
);
940 // This is a new keyword not present in the old name
941 m_keywords
->AddKeyword(*itnew
, file
);
944 // Remove all remaining old keywords not present in the new name
945 for (itold
= oldwords
.begin(); itold
!= oldwords
.end(); ++itold
) {
946 m_keywords
->RemoveKeyword(*itold
, file
);
949 Notify_DownloadCtrlUpdateItem(file
);
950 Notify_SharedFilesUpdateItem(file
);
960 const CPath
* CSharedFileList::GetDirForPublicSharedDirName(const wxString
& strSharedDir
) const
962 StringPathMap::const_iterator it
= m_PublicSharedDirNames
.find(strSharedDir
);
964 if (it
!= m_PublicSharedDirNames
.end()) {
965 return &(it
->second
);
972 wxString
CSharedFileList::GetPublicSharedDirName(const CPath
& dir
)
974 // safety check: is the directory supposed to be shared after all?
975 if (!IsShared(dir
)) {
979 // check if the public name for the directory is cached in our Map
980 StringPathMap::const_iterator it
;
981 for (it
= m_PublicSharedDirNames
.begin(); it
!= m_PublicSharedDirNames
.end(); ++it
) {
982 if (it
->second
.IsSameDir(dir
)) {
983 // public name for directory was determined earlier
988 // we store the path separator (forward or back slash) for quick access
989 wxChar cPathSepa
= wxFileName::GetPathSeparator();
991 // determine and cache the public name for "dir" ...
992 // We need to use the 'raw' filename, so the receiving client can recognize it.
993 wxString strDirectoryTmp
= dir
.GetRaw();
994 if (strDirectoryTmp
.EndsWith(&cPathSepa
)) {
995 strDirectoryTmp
.RemoveLast();
998 wxString strPublicName
;
1000 // check all the subdirectories in the path for being shared
1001 // the public name will consist of these concatenated
1002 while ((iPos
= strDirectoryTmp
.Find( cPathSepa
, true )) != wxNOT_FOUND
) {
1003 strPublicName
= strDirectoryTmp
.Right(strDirectoryTmp
.Length() - iPos
) + strPublicName
;
1004 strDirectoryTmp
.Truncate(iPos
);
1005 if (!IsShared(CPath(strDirectoryTmp
)))
1008 if (!strPublicName
.IsEmpty()) {
1009 // remove first path separator ???
1010 wxASSERT( strPublicName
.GetChar(0) == cPathSepa
);
1011 strPublicName
= strPublicName
.Right(strPublicName
.Length() - 1);
1013 // must be a rootdirectory on Windos
1014 wxASSERT( strDirectoryTmp
.Length() == 2 );
1015 strPublicName
= strDirectoryTmp
;
1017 // we have the name, make sure it is unique by appending an index if necessary
1018 if (m_PublicSharedDirNames
.find(strPublicName
) != m_PublicSharedDirNames
.end()) {
1019 wxString strUniquePublicName
;
1020 for (iPos
= 2; ; ++iPos
) {
1021 strUniquePublicName
= CFormat(wxT("%s_%i")) % strPublicName
% iPos
;
1023 if (m_PublicSharedDirNames
.find(strUniquePublicName
) == m_PublicSharedDirNames
.end()) {
1024 AddDebugLogLineN(logClient
, CFormat(wxT("Using public name '%s' for directory '%s'"))
1025 % strUniquePublicName
1026 % dir
.GetPrintable());
1027 m_PublicSharedDirNames
.insert(std::pair
<wxString
, CPath
> (strUniquePublicName
, dir
));
1028 return strUniquePublicName
;
1030 // This is from eMule and it checks if there are more than 200 shared folders with the same public name.
1031 // The condition can be true if many shared subfolders with the same name exist in folders that are not
1032 // shared. So they get the names of each shared subfolders concatenated. But those might all be the same!
1033 // It's here for safety reasons so we should not run out of memory.
1034 else if (iPos
> 200) // Only 200 identical names are indexed.
1041 AddDebugLogLineN(logClient
, CFormat(wxT("Using public name '%s' for directory '%s'")) % strPublicName
% dir
.GetPrintable());
1042 m_PublicSharedDirNames
.insert(std::pair
<wxString
, CPath
> (strPublicName
, dir
));
1043 return strPublicName
;
1048 bool CSharedFileList::IsShared(const CPath
& path
) const
1050 if( path
.IsDir(CPath::exists
) ) {
1051 // check if it's a shared folder
1052 const unsigned folderCount
= theApp
->glob_prefs
->shareddir_list
.size();
1053 for (unsigned i
= 0; i
< folderCount
; ++i
) {
1054 if (path
.IsSameDir(theApp
->glob_prefs
->shareddir_list
[i
])) {
1059 // check if it's one of the categories folders (category 0 = incoming)
1060 for (unsigned i
= 0; i
< theApp
->glob_prefs
->GetCatCount(); ++i
) {
1061 if (path
.IsSameDir(theApp
->glob_prefs
->GetCategory(i
)->path
)) {
1071 void CSharedFileList::CheckAICHHashes(const std::list
<CAICHHash
>& hashes
)
1073 wxMutexLocker
locker(list_mut
);
1075 // Now we check that all files which are in the sharedfilelist have a
1076 // corresponding hash in our list. Those how don't are queued for hashing.
1077 CKnownFileMap::iterator it
= m_Files_map
.begin();
1078 for (; it
!= m_Files_map
.end(); ++it
) {
1079 const CKnownFile
* file
= it
->second
;
1081 if (file
->IsPartFile() == false) {
1082 CAICHHashSet
* hashset
= file
->GetAICHHashset();
1084 if (hashset
->GetStatus() == AICH_HASHSETCOMPLETE
) {
1085 if (std::find(hashes
.begin(), hashes
.end(), hashset
->GetMasterHash()) != hashes
.end()) {
1090 hashset
->SetStatus(AICH_ERROR
);
1092 CThreadScheduler::AddTask(new CHashingTask(file
));
1098 // File_checked_for_headers