Nothing to see here. Really.
[amule.git] / src / SharedFileList.cpp
blob26093a31174f9d5286691bcef855684dd5d995a5
1 //
2 // This file is part of the aMule Project.
3 //
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 )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "SharedFileList.h" // Interface declarations // Do_not_auto_remove
28 #include <protocol/Protocols.h>
29 #include <protocol/kad/Constants.h>
30 #include <tags/FileTags.h>
32 #include <wx/utils.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
45 #include "Logger.h"
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 ///////////////////////////////////////////////////////////////////////////////
59 // CPublishKeyword
61 class CPublishKeyword
63 public:
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);
72 SetPublishedCount(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()) {
89 wxFAIL;
90 return false;
92 m_aFiles.push_back(pFile);
93 return true;
96 int RemoveRef(CKnownFile* pFile) {
97 KnownFileArray::iterator it = std::find(m_aFiles.begin(), m_aFiles.end(), pFile);
98 if (it != m_aFiles.end()) {
99 m_aFiles.erase(it);
101 return m_aFiles.size();
104 void RemoveAllReferences() {
105 m_aFiles.clear();
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());
115 protected:
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
129 public:
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; }
150 protected:
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()
163 ResetNextKeyword();
164 SetNextPublishTime(0);
167 CPublishKeywordList::~CPublishKeywordList()
169 RemoveAllKeywords();
172 CPublishKeyword* CPublishKeywordList::GetNextKeyword()
174 if (m_posNextKeyword == m_lstKeywords.end()) {
175 m_posNextKeyword = m_lstKeywords.begin();
176 if (m_posNextKeyword == m_lstKeywords.end()) {
177 return NULL;
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) {
194 if (ppos) {
195 (*ppos) = it;
198 return pPubKw;
202 return NULL;
205 void CPublishKeywordList::AddKeyword(const wxString& keyword, CKnownFile *file)
207 CPublishKeyword* pubKw = FindKeyword(keyword);
208 if (pubKw == NULL) {
209 pubKw = new CPublishKeyword(keyword);
210 m_lstKeywords.push_back(pubKw);
211 SetNextPublishTime(0);
213 pubKw->AddRef(file);
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);
230 if (pubKw != NULL) {
231 if (pubKw->RemoveRef(file) == 0) {
232 if (pos == m_posNextKeyword) {
233 ++m_posNextKeyword;
235 m_lstKeywords.erase(pos);
236 delete pubKw;
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);
255 ResetNextKeyword();
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) {
276 ++m_posNextKeyword;
278 m_lstKeywords.erase(it++);
279 delete pPubKw;
280 SetNextPublishTime(0);
281 } else {
282 ++it;
288 CSharedFileList::CSharedFileList(CKnownFileList* in_filelist){
289 filelist = in_filelist;
290 reloading = false;
291 m_lastPublishED2K = 0;
292 m_lastPublishED2KFlag = true;
293 /* Kad Stuff */
294 m_keywords = new CPublishKeywordList;
295 m_currFileSrc = 0;
296 m_currFileNotes = 0;
297 m_lastPublishKadSrc = 0;
298 m_lastPublishKadNotes = 0;
299 m_currFileKey = 0;
303 CSharedFileList::~CSharedFileList()
305 delete m_keywords;
309 void CSharedFileList::FindSharedFiles()
311 /* Abort loading if we are shutting down. */
312 if(theApp->IsOnShutDown()) {
313 return;
316 // Clear statistics.
317 theStats::ClearSharedFilesInfo();
319 // Reload shareddir.dat
320 theApp->glob_prefs->ReloadSharedFolders();
323 wxMutexLocker lock(list_mut);
324 m_Files_map.clear();
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());
334 AddFile(file);
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());
350 sharedPaths.sort();
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.
356 TaskList hashTasks;
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)) {
366 addedFiles++;
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());
375 } else {
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);
388 return true;
391 return false;
395 unsigned CSharedFileList::AddFilesFromDirectory(const CPath& directory, TaskList & hashTasks)
397 // Do not allow these folders to be shared:
398 // - The .aMule folder
399 // - The Temp folder
400 // - The users home-dir
401 if (CheckDirectory(wxGetHomeDir(), directory)) {
402 return 0;
403 } else if (CheckDirectory(theApp->ConfigDir, directory)) {
404 return 0;
405 } else if (CheckDirectory(thePrefs::GetTempDir().GetRaw(), directory)) {
406 return 0;
409 if (!directory.DirExists()) {
410 AddLogLineNS(CFormat(_("Shared directory not found, skipping: %s"))
411 % directory.GetPrintable());
413 return 0;
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 CPath fname = SharedDir.GetFirstFile(searchFor);
427 while (fname.IsOk()) {
428 CPath fullPath = directory.JoinPaths(fname);
430 if (!fullPath.FileExists()) {
431 AddDebugLogLineN(logKnownFiles,
432 CFormat(wxT("Shared file does not exist (possibly a broken link): %s")) % fullPath);
434 fname = SharedDir.GetNextFile();
435 continue;
438 AddDebugLogLineN(logKnownFiles,
439 CFormat(wxT("Found shared file: %s")) % fullPath);
441 time_t fdate = CPath::GetModificationTime(fullPath);
442 sint64 fsize = fullPath.GetFileSize();
444 // This will also catch files with too strict permissions.
445 if ((fdate == (time_t)-1) || (fsize == wxInvalidOffset)) {
446 AddDebugLogLineN(logKnownFiles,
447 CFormat(wxT("Failed to retrieve modification time or size for '%s', skipping.")) % fullPath);
449 fname = SharedDir.GetNextFile();
450 continue;
454 CKnownFile* toadd = filelist->FindKnownFile(fname, fdate, fsize);
455 if (toadd) {
456 knownFiles++;
457 if (AddFile(toadd)) {
458 AddDebugLogLineN(logKnownFiles,
459 CFormat(wxT("Added known file '%s' to shares"))
460 % fname);
462 toadd->SetFilePath(directory);
463 } else {
464 AddDebugLogLineN(logKnownFiles,
465 CFormat(wxT("File already shared, skipping: %s"))
466 % fname);
468 } else {
469 //not in knownfilelist - start adding thread to hash file
470 AddDebugLogLineN(logKnownFiles,
471 CFormat(wxT("Hashing new unknown shared file '%s'")) % fname);
473 hashTasks.push_back(new CHashingTask(directory, fname));
474 addedFiles++;
477 fname = SharedDir.GetNextFile();
480 if ((addedFiles == 0) && (knownFiles == 0)) {
481 AddLogLineN(CFormat(_("No shareable files found in directory: %s"))
482 % directory.GetPrintable());
485 return addedFiles;
489 bool CSharedFileList::AddFile(CKnownFile* pFile)
491 wxASSERT(pFile->GetHashCount() == pFile->GetED2KPartHashCount());
493 wxMutexLocker lock(list_mut);
495 CKnownFileMap::value_type entry(pFile->GetFileHash(), pFile);
496 if (m_Files_map.insert(entry).second) {
497 /* Keywords to publish on Kad */
498 m_keywords->AddKeywords(pFile);
499 theStats::AddSharedFile(pFile->GetFileSize());
500 return true;
502 return false;
506 void CSharedFileList::SafeAddKFile(CKnownFile* toadd, bool bOnlyAdd)
508 // TODO: Check if the file is already known - only with another date
510 if (AddFile(toadd)) {
511 Notify_SharedFilesShowFile(toadd);
514 if (!bOnlyAdd && theApp->IsConnectedED2K()) {
515 // Publishing of files is not anymore handled here.
516 // Instead, the timer does it by itself.
517 m_lastPublishED2KFlag = true;
522 // removes first occurrence of 'toremove' in 'list'
523 void CSharedFileList::RemoveFile(CKnownFile* toremove){
524 Notify_SharedFilesRemoveFile(toremove);
525 wxMutexLocker lock(list_mut);
526 if (m_Files_map.erase(toremove->GetFileHash()) > 0) {
527 theStats::RemoveSharedFile(toremove->GetFileSize());
529 /* This file keywords must not be published to kad anymore */
530 m_keywords->RemoveKeywords(toremove);
534 void CSharedFileList::Reload()
536 // Madcat - Disable reloading if reloading already in progress.
537 // Kry - Fixed to let non-english language users use the 'Reload' button :P
538 // deltaHF - removed the old ugly button and changed the code to use the new small one
539 // Kry - bah, let's use a var.
540 if (!reloading) {
541 AddDebugLogLineN(logKnownFiles, wxT("Reload shared files"));
542 reloading = true;
543 Notify_SharedFilesRemoveAllItems();
545 /* All Kad keywords must be removed */
546 m_keywords->RemoveAllKeywordReferences();
548 /* Public identifiers must be erased as they might be invalid now */
549 m_PublicSharedDirNames.clear();
551 FindSharedFiles();
553 /* And now the unreferenced keywords must be removed also */
554 m_keywords->PurgeUnreferencedKeywords();
556 Notify_SharedFilesShowFileList();
558 reloading = false;
563 const CKnownFile *CSharedFileList::GetFileByIndex(unsigned int index) const
565 wxMutexLocker lock(list_mut);
566 if ( index >= m_Files_map.size() ) {
567 return NULL;
569 CKnownFileMap::const_iterator pos = m_Files_map.begin();
570 std::advance(pos, index);
571 return pos->second;
575 CKnownFile* CSharedFileList::GetFileByID(const CMD4Hash& filehash)
577 wxMutexLocker lock(list_mut);
578 CKnownFileMap::iterator it = m_Files_map.find(filehash);
580 if ( it != m_Files_map.end() ) {
581 return it->second;
582 } else {
583 return NULL;
587 short CSharedFileList::GetFilePriorityByID(const CMD4Hash& filehash)
589 CKnownFile* tocheck = GetFileByID(filehash);
590 if (tocheck)
591 return tocheck->GetUpPriority();
592 else
593 return -10; // file doesn't exist
597 void CSharedFileList::CopyFileList(std::vector<CKnownFile*>& out_list) const
599 wxMutexLocker lock(list_mut);
601 out_list.reserve(m_Files_map.size());
602 for (
603 CKnownFileMap::const_iterator it = m_Files_map.begin();
604 it != m_Files_map.end();
605 ++it
607 out_list.push_back(it->second);
613 void CSharedFileList::UpdateItem(CKnownFile* toupdate)
615 Notify_SharedFilesUpdateItem(toupdate);
619 void CSharedFileList::GetSharedFilesByDirectory(const wxString& directory,
620 CKnownFilePtrList& list)
622 wxMutexLocker lock(list_mut);
624 const CPath dir = CPath(directory);
625 for (CKnownFileMap::iterator pos = m_Files_map.begin();
626 pos != m_Files_map.end(); ++pos ) {
627 CKnownFile *cur_file = pos->second;
629 if (dir.IsSameDir(cur_file->GetFilePath())) {
630 continue;
633 list.push_back(cur_file);
637 /* ---------------- Network ----------------- */
639 void CSharedFileList::ClearED2KPublishInfo(){
640 CKnownFile* cur_file;
641 m_lastPublishED2KFlag = true;
642 wxMutexLocker lock(list_mut);
643 for (CKnownFileMap::iterator pos = m_Files_map.begin(); pos != m_Files_map.end(); ++pos ) {
644 cur_file = pos->second;
645 cur_file->SetPublishedED2K(false);
649 void CSharedFileList::ClearKadSourcePublishInfo()
651 wxMutexLocker lock(list_mut);
652 CKnownFile* cur_file;
653 for (CKnownFileMap::iterator pos = m_Files_map.begin(); pos != m_Files_map.end(); ++pos ) {
654 cur_file = pos->second;
655 cur_file->SetLastPublishTimeKadSrc(0,0);
659 void CSharedFileList::RepublishFile(CKnownFile* pFile)
661 CServer* server = theApp->serverconnect->GetCurrentServer();
662 if (server && (server->GetTCPFlags() & SRV_TCPFLG_COMPRESSION)) {
663 m_lastPublishED2KFlag = true;
664 pFile->SetPublishedED2K(false); // FIXME: this creates a wrong 'No' for the ed2k shared info in the listview until the file is shared again.
668 uint8 GetRealPrio(uint8 in)
670 switch(in) {
671 case 4 : return 0;
672 case 0 : return 1;
673 case 1 : return 2;
674 case 2 : return 3;
675 case 3 : return 4;
677 return 0;
680 bool SortFunc( const CKnownFile* fileA, const CKnownFile* fileB )
682 return GetRealPrio(fileA->GetUpPriority()) < GetRealPrio(fileB->GetUpPriority());
685 void CSharedFileList::SendListToServer(){
686 std::vector<CKnownFile*> SortedList;
689 wxMutexLocker lock(list_mut);
691 if (m_Files_map.empty() || !theApp->IsConnectedED2K() ) {
692 return;
695 // Getting a sorted list of the non-published files.
696 SortedList.reserve( m_Files_map.size() );
698 CKnownFileMap::iterator it = m_Files_map.begin();
699 for ( ; it != m_Files_map.end(); ++it ) {
700 if (!it->second->GetPublishedED2K()) {
701 SortedList.push_back( it->second );
706 std::sort( SortedList.begin(), SortedList.end(), SortFunc );
708 // Limits for the server.
710 CServer* server = theApp->serverconnect->GetCurrentServer();
712 uint32 limit = server ? server->GetSoftFiles() : 0;
713 if( limit == 0 || limit > 200 ) {
714 limit = 200;
717 if( (uint32)SortedList.size() < limit ) {
718 limit = SortedList.size();
719 if (limit == 0) {
720 m_lastPublishED2KFlag = false;
721 return;
725 CMemFile files;
727 // Files sent.
728 files.WriteUInt32(limit);
730 uint16 count = 0;
731 // Add to packet
732 std::vector<CKnownFile*>::iterator sorted_it = SortedList.begin();
733 for ( ; (sorted_it != SortedList.end()) && (count < limit); ++sorted_it ) {
734 CKnownFile* file = *sorted_it;
735 if (!file->IsLargeFile() || (server && server->SupportsLargeFilesTCP())) {
736 file->CreateOfferedFilePacket(&files, server, NULL);
738 file->SetPublishedED2K(true);
739 ++count;
742 wxASSERT(count == limit);
744 CPacket* packet = new CPacket(files, OP_EDONKEYPROT, OP_OFFERFILES);
745 // compress packet
746 // - this kind of data is highly compressable (N * (1 MD4 and at least 3 string meta data tags and 1 integer meta data tag))
747 // - the min. amount of data needed for one published file is ~100 bytes
748 // - this function is called once when connecting to a server and when a file becomes shareable - so, it's called rarely.
749 // - if the compressed size is still >= the original size, we send the uncompressed packet
750 // therefor we always try to compress the packet
751 if (server->GetTCPFlags() & SRV_TCPFLG_COMPRESSION){
752 packet->PackPacket();
755 theStats::AddUpOverheadServer(packet->GetPacketSize());
756 theApp->serverconnect->SendPacket(packet,true);
760 void CSharedFileList::Process()
762 Publish();
763 if( !m_lastPublishED2KFlag || ( ::GetTickCount() - m_lastPublishED2K < ED2KREPUBLISHTIME ) ) {
764 return;
766 SendListToServer();
767 m_lastPublishED2K = ::GetTickCount();
770 void CSharedFileList::Publish()
772 // Variables to save cpu.
773 unsigned int tNow = time(NULL);
774 bool IsFirewalled = theApp->IsFirewalled();
776 if( Kademlia::CKademlia::IsConnected() && ( !IsFirewalled || ( IsFirewalled && theApp->clientlist->GetBuddyStatus() == Connected)) && GetCount() && Kademlia::CKademlia::GetPublish()) {
777 //We are connected to Kad. We are either open or have a buddy. And Kad is ready to start publishing.
779 if( Kademlia::CKademlia::GetTotalStoreKey() < KADEMLIATOTALSTOREKEY) {
781 //We are not at the max simultaneous keyword publishes
782 if (tNow >= m_keywords->GetNextPublishTime()) {
784 //Enough time has passed since last keyword publish
786 //Get the next keyword which has to be (re)-published
787 CPublishKeyword* pPubKw = m_keywords->GetNextKeyword();
788 if (pPubKw) {
790 //We have the next keyword to check if it can be published
792 //Debug check to make sure things are going well.
793 wxASSERT( pPubKw->GetRefCount() != 0 );
795 if (tNow >= pPubKw->GetNextPublishTime()) {
796 //This keyword can be published.
797 Kademlia::CSearch* pSearch = Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::STOREKEYWORD, false, pPubKw->GetKadID());
798 if (pSearch) {
799 //pSearch was created. Which means no search was already being done with this HashID.
800 //This also means that it was checked to see if network load wasn't a factor.
802 //This sets the filename into the search object so we can show it in the gui.
803 pSearch->SetFileName(pPubKw->GetKeyword());
805 //Add all file IDs which relate to the current keyword to be published
806 const KnownFileArray& aFiles = pPubKw->GetReferences();
807 uint32 count = 0;
808 for (unsigned int f = 0; f < aFiles.size(); ++f) {
810 //Only publish complete files as someone else should have the full file to publish these keywords.
811 //As a side effect, this may help reduce people finding incomplete files in the network.
812 if( !aFiles[f]->IsPartFile() ) {
813 count++;
814 pSearch->AddFileID(Kademlia::CUInt128(aFiles[f]->GetFileHash().GetHash()));
815 if( count > 150 ) {
816 //We only publish up to 150 files per keyword publish then rotate the list.
817 pPubKw->RotateReferences(f);
818 break;
823 if( count ) {
824 //Start our keyword publish
825 pPubKw->SetNextPublishTime(tNow+(KADEMLIAREPUBLISHTIMEK));
826 pPubKw->IncPublishedCount();
827 Kademlia::CSearchManager::StartSearch(pSearch);
828 } else {
829 //There were no valid files to publish with this keyword.
830 delete pSearch;
835 m_keywords->SetNextPublishTime(KADEMLIAPUBLISHTIME+tNow);
839 if( Kademlia::CKademlia::GetTotalStoreSrc() < KADEMLIATOTALSTORESRC) {
840 if(tNow >= m_lastPublishKadSrc) {
841 if(m_currFileSrc > GetCount()) {
842 m_currFileSrc = 0;
844 CKnownFile* pCurKnownFile = const_cast<CKnownFile*>(GetFileByIndex(m_currFileSrc));
845 if(pCurKnownFile) {
846 if(pCurKnownFile->PublishSrc()) {
847 Kademlia::CUInt128 kadFileID;
848 kadFileID.SetValueBE(pCurKnownFile->GetFileHash().GetHash());
849 if(Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::STOREFILE, true, kadFileID )==NULL) {
850 pCurKnownFile->SetLastPublishTimeKadSrc(0,0);
854 m_currFileSrc++;
856 // even if we did not publish a source, reset the timer so that this list is processed
857 // only every KADEMLIAPUBLISHTIME seconds.
858 m_lastPublishKadSrc = KADEMLIAPUBLISHTIME+tNow;
862 if( Kademlia::CKademlia::GetTotalStoreNotes() < KADEMLIATOTALSTORENOTES) {
863 if(tNow >= m_lastPublishKadNotes) {
864 if(m_currFileNotes > GetCount()) {
865 m_currFileNotes = 0;
867 CKnownFile* pCurKnownFile = const_cast<CKnownFile*>(GetFileByIndex(m_currFileNotes));
868 if(pCurKnownFile) {
869 if(pCurKnownFile->PublishNotes()) {
870 Kademlia::CUInt128 kadFileID;
871 kadFileID.SetValueBE(pCurKnownFile->GetFileHash().GetHash());
872 if(Kademlia::CSearchManager::PrepareLookup(Kademlia::CSearch::STORENOTES, true, kadFileID )==NULL)
873 pCurKnownFile->SetLastPublishTimeKadNotes(0);
876 m_currFileNotes++;
878 // even if we did not publish a source, reset the timer so that this list is processed
879 // only every KADEMLIAPUBLISHTIME seconds.
880 m_lastPublishKadNotes = KADEMLIAPUBLISHTIME+tNow;
887 void CSharedFileList::AddKeywords(CKnownFile* pFile)
889 m_keywords->AddKeywords(pFile);
893 void CSharedFileList::RemoveKeywords(CKnownFile* pFile)
895 m_keywords->RemoveKeywords(pFile);
899 bool CSharedFileList::RenameFile(CKnownFile* file, const CPath& newName)
901 if (file->IsPartFile()) {
902 CPartFile* pfile = dynamic_cast<CPartFile*>(file);
904 if (file->GetStatus() != PS_COMPLETING) {
905 pfile->SetFileName(newName);
906 pfile->SavePartFile();
908 Notify_SharedFilesUpdateItem(file);
909 Notify_DownloadCtrlUpdateItem(file);
911 return true;
913 } else {
914 CPath oldPath = file->GetFilePath().JoinPaths(file->GetFileName());
915 CPath newPath = file->GetFilePath().JoinPaths(newName);
917 if (CPath::RenameFile(oldPath, newPath)) {
918 // Must create a copy of the word list because:
919 // 1) it will be reset on SetFileName()
920 // 2) we will want to edit it
921 Kademlia::WordList oldwords = file->GetKadKeywords();
922 file->SetFileName(newName);
923 theApp->knownfiles->Save();
924 UpdateItem(file);
925 RepublishFile(file);
927 const Kademlia::WordList& newwords = file->GetKadKeywords();
928 Kademlia::WordList::iterator itold;
929 Kademlia::WordList::const_iterator itnew;
930 // compare keywords in old and new names
931 for (itnew = newwords.begin(); itnew != newwords.end(); ++itnew) {
932 for (itold = oldwords.begin(); itold != oldwords.end(); ++itold) {
933 if (*itold == *itnew) {
934 break;
937 if (itold != oldwords.end()) {
938 // Remove keyword from old name which also exist in new name
939 oldwords.erase(itold);
940 } else {
941 // This is a new keyword not present in the old name
942 m_keywords->AddKeyword(*itnew, file);
945 // Remove all remaining old keywords not present in the new name
946 for (itold = oldwords.begin(); itold != oldwords.end(); ++itold) {
947 m_keywords->RemoveKeyword(*itold, file);
950 Notify_DownloadCtrlUpdateItem(file);
951 Notify_SharedFilesUpdateItem(file);
953 return true;
957 return false;
961 const CPath* CSharedFileList::GetDirForPublicSharedDirName(const wxString& strSharedDir) const
963 StringPathMap::const_iterator it = m_PublicSharedDirNames.find(strSharedDir);
965 if (it != m_PublicSharedDirNames.end()) {
966 return &(it->second);
967 } else {
968 return NULL;
973 wxString CSharedFileList::GetPublicSharedDirName(const CPath& dir)
975 // safety check: is the directory supposed to be shared after all?
976 if (!IsShared(dir)) {
977 wxFAIL;
978 return wxT("");
980 // check if the public name for the directory is cached in our Map
981 StringPathMap::const_iterator it;
982 for (it = m_PublicSharedDirNames.begin(); it != m_PublicSharedDirNames.end(); ++it) {
983 if (it->second.IsSameDir(dir)) {
984 // public name for directory was determined earlier
985 return it->first;
989 // we store the path separator (forward or back slash) for quick access
990 wxChar cPathSepa = wxFileName::GetPathSeparator();
992 // determine and cache the public name for "dir" ...
993 // We need to use the 'raw' filename, so the receiving client can recognize it.
994 wxString strDirectoryTmp = dir.GetRaw();
995 if (strDirectoryTmp.EndsWith(&cPathSepa)) {
996 strDirectoryTmp.RemoveLast();
999 wxString strPublicName;
1000 int iPos;
1001 // check all the subdirectories in the path for being shared
1002 // the public name will consist of these concatenated
1003 while ((iPos = strDirectoryTmp.Find( cPathSepa, true )) != wxNOT_FOUND) {
1004 strPublicName = strDirectoryTmp.Right(strDirectoryTmp.Length() - iPos) + strPublicName;
1005 strDirectoryTmp.Truncate(iPos);
1006 if (!IsShared(CPath(strDirectoryTmp)))
1007 break;
1009 if (!strPublicName.IsEmpty()) {
1010 // remove first path separator ???
1011 wxASSERT( strPublicName.GetChar(0) == cPathSepa );
1012 strPublicName = strPublicName.Right(strPublicName.Length() - 1);
1013 } else {
1014 // must be a rootdirectory on Windos
1015 wxASSERT( strDirectoryTmp.Length() == 2 );
1016 strPublicName = strDirectoryTmp;
1018 // we have the name, make sure it is unique by appending an index if necessary
1019 if (m_PublicSharedDirNames.find(strPublicName) != m_PublicSharedDirNames.end()) {
1020 wxString strUniquePublicName;
1021 for (iPos = 2; ; ++iPos) {
1022 strUniquePublicName = CFormat(wxT("%s_%i")) % strPublicName % iPos;
1024 if (m_PublicSharedDirNames.find(strUniquePublicName) == m_PublicSharedDirNames.end()) {
1025 AddDebugLogLineN(logClient, CFormat(wxT("Using public name '%s' for directory '%s'"))
1026 % strUniquePublicName
1027 % dir.GetPrintable());
1028 m_PublicSharedDirNames.insert(std::pair<wxString, CPath> (strUniquePublicName, dir));
1029 return strUniquePublicName;
1031 // This is from eMule and it checks if there are more than 200 shared folders with the same public name.
1032 // The condition can be true if many shared subfolders with the same name exist in folders that are not
1033 // shared. So they get the names of each shared subfolders concatenated. But those might all be the same!
1034 // It's here for safety reasons so we should not run out of memory.
1035 else if (iPos > 200) // Only 200 identical names are indexed.
1037 wxASSERT( false );
1038 return wxT("");
1041 } else {
1042 AddDebugLogLineN(logClient, CFormat(wxT("Using public name '%s' for directory '%s'")) % strPublicName % dir.GetPrintable());
1043 m_PublicSharedDirNames.insert(std::pair<wxString, CPath> (strPublicName, dir));
1044 return strPublicName;
1049 bool CSharedFileList::IsShared(const CPath& path) const
1051 if( path.IsDir(CPath::exists) ) {
1052 // check if it's a shared folder
1053 const unsigned folderCount = theApp->glob_prefs->shareddir_list.size();
1054 for (unsigned i = 0; i < folderCount; ++i) {
1055 if (path.IsSameDir(theApp->glob_prefs->shareddir_list[i])) {
1056 return true;
1060 // check if it's one of the categories folders (category 0 = incoming)
1061 for (unsigned i = 0; i < theApp->glob_prefs->GetCatCount(); ++i) {
1062 if (path.IsSameDir(theApp->glob_prefs->GetCategory(i)->path)) {
1063 return true;
1068 return false;
1072 void CSharedFileList::CheckAICHHashes(const std::list<CAICHHash>& hashes)
1074 wxMutexLocker locker(list_mut);
1076 // Now we check that all files which are in the sharedfilelist have a
1077 // corresponding hash in our list. Those how don't are queued for hashing.
1078 CKnownFileMap::iterator it = m_Files_map.begin();
1079 for (; it != m_Files_map.end(); ++it) {
1080 const CKnownFile* file = it->second;
1082 if (file->IsPartFile() == false) {
1083 CAICHHashSet* hashset = file->GetAICHHashset();
1085 if (hashset->GetStatus() == AICH_HASHSETCOMPLETE) {
1086 if (std::find(hashes.begin(), hashes.end(), hashset->GetMasterHash()) != hashes.end()) {
1087 continue;
1091 hashset->SetStatus(AICH_ERROR);
1093 CThreadScheduler::AddTask(new CHashingTask(file));
1099 // File_checked_for_headers