1 /*
2  * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #pragma once
20 
21 #include "TimerManager.h"
22 #include "CriticalSection.h"
23 #include "Exception.h"
24 #include "User.h"
25 #include "File.h"
26 #include "QueueItem.h"
27 #include "Singleton.h"
28 #include "DirectoryListing.h"
29 #include "MerkleTree.h"
30 #include "QueueManagerListener.h"
31 #include "SearchManagerListener.h"
32 #include "ClientManagerListener.h"
33 
34 namespace dcpp {
35 
36 STANDARD_EXCEPTION(QueueException);
37 
38 class UserConnection;
39 
40 class DirectoryItem {
41 public:
42     typedef DirectoryItem* Ptr;
43     typedef unordered_multimap<UserPtr, Ptr, User::Hash> DirectoryMap;
44     typedef DirectoryMap::iterator DirectoryIter;
45     typedef pair<DirectoryIter, DirectoryIter> DirectoryPair;
46 
47     typedef vector<Ptr> List;
48     typedef List::iterator Iter;
49 
DirectoryItem()50     DirectoryItem() : priority(QueueItem::DEFAULT) { }
DirectoryItem(const UserPtr & aUser,const string & aName,const string & aTarget,QueueItem::Priority p)51     DirectoryItem(const UserPtr& aUser, const string& aName, const string& aTarget,
52         QueueItem::Priority p) : name(aName), target(aTarget), priority(p), user(aUser) { }
~DirectoryItem()53     ~DirectoryItem() { }
54 
getUser()55     UserPtr& getUser() { return user; }
setUser(const UserPtr & aUser)56     void setUser(const UserPtr& aUser) { user = aUser; }
57 
58     GETSET(string, name, Name);
59     GETSET(string, target, Target);
60     GETSET(QueueItem::Priority, priority, Priority);
61 private:
62     UserPtr user;
63 };
64 
65 class ConnectionQueueItem;
66 class QueueLoader;
67 
68 class QueueManager : public Singleton<QueueManager>, public Speaker<QueueManagerListener>, private TimerManagerListener,
69     private SearchManagerListener, private ClientManagerListener
70 {
71 public:
72     //NOTE: freedcpp
73     void add(const string& aTarget, int64_t aSize, const TTHValue& root);
74 
75     /** Add a file to the queue. */
76     void add(const string& aTarget, int64_t aSize, const TTHValue& root, const HintedUser& aUser,
77     int aFlags = 0, bool addBad = true);
78     /** Add a user's filelist to the queue. */
79     void addList(const HintedUser& HintedUser, int aFlags, const string& aInitialDir = Util::emptyString);
80     /** Readd a source that was removed */
81     void readd(const string& target, const HintedUser& aUser);
82     /** Add a directory to the queue (downloads filelist and matches the directory). */
83     void addDirectory(const string& aDir, const HintedUser& aUser, const string& aTarget,
84             QueueItem::Priority p = QueueItem::DEFAULT) noexcept;
85 
86     int matchListing(const DirectoryListing& dl) noexcept;
87     void matchAllListings();
88 
89     bool getTTH(const string& name, TTHValue& tth) noexcept;
90 
91     int64_t getSize(const string& target) noexcept;
92     int64_t getPos(const string& target) noexcept;
93 
94     /** Move the target location of a queued item. Running items are silently ignored */
95     void move(const string& aSource, const string& aTarget) noexcept;
96 
97     void remove(const string& aTarget) noexcept;
98     void removeSource(const string& aTarget, const UserPtr& aUser, int reason, bool removeConn = true) noexcept;
99     void removeSource(const UserPtr& aUser, int reason) noexcept;
100 
101     void recheck(const string& aTarget);
102 
103     void setPriority(const string& aTarget, QueueItem::Priority p) noexcept;
104 
105     void getTargets(const TTHValue& tth, StringList& sl);
lockQueue()106     QueueItem::StringMap& lockQueue() noexcept { cs.lock(); return fileQueue.getQueue(); } ;
unlockQueue()107     void unlockQueue() noexcept { cs.unlock(); }
108 
109     Download* getDownload(UserConnection& aSource, bool supportsTrees) noexcept;
110     void putDownload(Download* aDownload, bool finished) noexcept;
111     void setFile(Download* download);
112 
113     int64_t getQueued(const UserPtr& aUser) const;
114 
115     /** @return The highest priority download the user has, PAUSED may also mean no downloads */
116     QueueItem::Priority hasDownload(const UserPtr& aUser) noexcept;
117 
118     bool getQueueInfo(const UserPtr& aUser, string& aTarget, int64_t& aSize, int& aFlags) noexcept;
119 
120     int countOnlineSources(const string& aTarget);
121 
122     void loadQueue() noexcept;
123     void saveQueue(bool force = false) noexcept;
124 
125     void noDeleteFileList(const string& path);
126 
127     bool handlePartialSearch(const TTHValue& tth, PartsInfo& _outPartsInfo);
128     bool handlePartialResult(const UserPtr& aUser, const string& hubHint, const TTHValue& tth, const QueueItem::PartialSource& partialSource, PartsInfo& outPartialInfo);
129 
isChunkDownloaded(const TTHValue & tth,int64_t startPos,int64_t & bytes,string & tempTarget,int64_t & size)130     bool isChunkDownloaded(const TTHValue& tth, int64_t startPos, int64_t& bytes, string& tempTarget, int64_t& size) {
131         Lock l(cs);
132         QueueItem::List ql;
133         fileQueue.find(ql, tth);
134 
135         if(ql.empty()) return false;
136 
137         QueueItem* qi = ql.front();
138 
139         tempTarget = qi->getTempTarget();
140         size = qi->getSize();
141 
142         return qi->isChunkDownloaded(startPos, bytes);
143     }
144 
145     GETSET(uint64_t, lastSave, LastSave);
146     GETSET(string, queueFile, QueueFile);
147 private:
148     enum { MOVER_LIMIT = 10*1024*1024 };
149     class FileMover : public Thread {
150     public:
FileMover()151         FileMover() : active(false) { }
~FileMover()152         virtual ~FileMover() { join(); }
153 
154         void moveFile(const string& source, const string& target);
155         virtual int run();
156     private:
157         typedef pair<string, string> FilePair;
158         typedef vector<FilePair> FileList;
159         typedef FileList::iterator FileIter;
160 
161         bool active;
162 
163         FileList files;
164         CriticalSection cs;
165     } mover;
166 
167     typedef vector<pair<QueueItem::SourceConstIter, const QueueItem*> > PFSSourceList;
168 
169     class Rechecker : public Thread {
170         struct DummyOutputStream : OutputStream {
writeDummyOutputStream171             virtual size_t write(const void*, size_t n) { return n; }
flushDummyOutputStream172             virtual size_t flush() { return 0; }
173         };
174 
175     public:
Rechecker(QueueManager * qm_)176         explicit Rechecker(QueueManager* qm_) : qm(qm_), active(false) { }
~Rechecker()177         virtual ~Rechecker() { join(); }
178 
179         void add(const string& file);
180         virtual int run();
181 
182     private:
183         QueueManager* qm;
184         bool active;
185 
186         StringList files;
187         CriticalSection cs;
188     } rechecker;
189 
190     /** All queue items by target */
191     class FileQueue {
192     public:
FileQueue()193         FileQueue() : lastInsert(queue.end()) { }
~FileQueue()194         ~FileQueue() {
195             for(QueueItem::StringIter i = queue.begin(); i != queue.end(); ++i)
196                 delete i->second;
197         }
198         void add(QueueItem* qi);
199         QueueItem* add(const string& aTarget, int64_t aSize, int aFlags, QueueItem::Priority p,
200             const string& aTempTarget, time_t aAdded, const TTHValue& root);
201 
202         QueueItem* find(const string& target);
203         void find(QueueItem::List& sl, int64_t aSize, const string& ext);
204         void find(QueueItem::List& ql, const TTHValue& tth);
205         // find some PFS sources to exchange parts info
206         void findPFSSources(PFSSourceList&);
207         bool exists(const TTHValue& tth) const;
208 
209 #ifdef WITH_DHT
210         // return a PFS tth to DHT publish
211         TTHValue* findPFSPubTTH();
212 #endif
213 
214         QueueItem* findAutoSearch(StringList& recent);
getSize()215         size_t getSize() { return queue.size(); }
getQueue()216         QueueItem::StringMap& getQueue() { return queue; }
217         void move(QueueItem* qi, const string& aTarget);
218         void remove(QueueItem* qi);
219     private:
220         QueueItem::StringMap queue;
221         /** A hint where to insert an item... */
222         QueueItem::StringIter lastInsert;
223     };
224 
225     /** All queue items indexed by user (this is a cache for the FileQueue really...) */
226     class UserQueue {
227     public:
228         void add(QueueItem* qi);
229         void add(QueueItem* qi, const UserPtr& aUser);
230         QueueItem* getNext(const UserPtr& aUser, QueueItem::Priority minPrio = QueueItem::LOWEST, int64_t wantedSize = 0,int64_t lastSpeed =0 ,bool allowRemove = true);
231         QueueItem* getRunning(const UserPtr& aUser);
232         void addDownload(QueueItem* qi, Download* d);
233         void removeDownload(QueueItem* qi, const UserPtr& d);
getList(int p)234         QueueItem::UserListMap& getList(int p) { return userQueue[p]; }
235         void remove(QueueItem* qi, bool removeRunning = true);
236         void remove(QueueItem* qi, const UserPtr& aUser, bool removeRunning = true);
237         void setPriority(QueueItem* qi, QueueItem::Priority p);
238 
getRunning()239         QueueItem::UserMap& getRunning() { return running; }
isRunning(const UserPtr & aUser)240         bool isRunning(const UserPtr& aUser) const {
241             return (running.find(aUser) != running.end());
242         }
243         int64_t getQueued(const UserPtr& aUser) const;
244     private:
245         /** QueueItems by priority and user (this is where the download order is determined) */
246         QueueItem::UserListMap userQueue[QueueItem::LAST];
247         /** Currently running downloads, a QueueItem is always either here or in the userQueue */
248         QueueItem::UserMap running;
249     };
250 
251     friend class QueueLoader;
252     friend class Singleton<QueueManager>;
253 
254     QueueManager();
255     virtual ~QueueManager();
256 
257     mutable CriticalSection cs;
258 
259     /** QueueItems by target */
260     FileQueue fileQueue;
261     /** QueueItems by user */
262     UserQueue userQueue;
263     /** Directories queued for downloading */
264     DirectoryItem::DirectoryMap directories;
265     /** Recent searches list, to avoid searching for the same thing too often */
266     StringList recent;
267     /** The queue needs to be saved */
268     bool dirty;
269     /** Next search */
270     uint64_t nextSearch;
271     /** File lists not to delete */
272     StringList protectedFileLists;
273     /** Sanity check for the target filename */
274     static string checkTarget(const string& aTarget, bool checkExsistence);
275     /** Add a source to an existing queue item */
276     bool addSource(QueueItem* qi, const HintedUser& aUser, Flags::MaskType addBad);
277 
278     void processList(const string& name, const HintedUser& user, int flags);
279 
280     void load(const SimpleXML& aXml);
281     void moveFile(const string& source, const string& target);
282     static void moveFile_(const string& source, const string& target);
283     void moveStuckFile(QueueItem* qi);
284     void rechecked(QueueItem* qi);
285 
286     void setDirty();
287 
288     string getListPath(const HintedUser& user);
289 
290     bool checkSfv(QueueItem* qi, Download* d);
291     uint32_t calcCrc32(const string& file);
292 
293     void logFinishedDownload(QueueItem* qi, Download* d, bool crcError);
294 
295     // TimerManagerListener
296     virtual void on(TimerManagerListener::Second, uint64_t aTick) noexcept;
297     virtual void on(TimerManagerListener::Minute, uint64_t aTick) noexcept;
298 
299     // SearchManagerListener
300     virtual void on(SearchManagerListener::SR, const SearchResultPtr&) noexcept;
301 
302     // ClientManagerListener
303     virtual void on(ClientManagerListener::UserConnected, const UserPtr& aUser) noexcept;
304     virtual void on(ClientManagerListener::UserDisconnected, const UserPtr& aUser) noexcept;
305 };
306 
307 } // namespace dcpp
308