1 /*
2  * Copyright (C) 2001-2009 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #ifndef DCPLUSPLUS_DCPP_QUEUE_MANAGER_H
20 #define DCPLUSPLUS_DCPP_QUEUE_MANAGER_H
21 
22 #include "TimerManager.h"
23 
24 #include "CriticalSection.h"
25 #include "Exception.h"
26 #include "User.h"
27 #include "File.h"
28 #include "QueueItem.h"
29 #include "Singleton.h"
30 #include "DirectoryListing.h"
31 #include "MerkleTree.h"
32 
33 #include "QueueManagerListener.h"
34 #include "SearchManagerListener.h"
35 #include "ClientManagerListener.h"
36 
37 namespace dcpp {
38 
39 STANDARD_EXCEPTION(QueueException);
40 
41 class UserConnection;
42 
43 class DirectoryItem {
44 public:
45 	typedef DirectoryItem* Ptr;
46 	typedef unordered_multimap<UserPtr, Ptr, User::Hash> DirectoryMap;
47 	typedef DirectoryMap::iterator DirectoryIter;
48 	typedef pair<DirectoryIter, DirectoryIter> DirectoryPair;
49 
50 	typedef vector<Ptr> List;
51 	typedef List::iterator Iter;
52 
DirectoryItem()53 	DirectoryItem() : priority(QueueItem::DEFAULT) { }
DirectoryItem(const UserPtr & aUser,const string & aName,const string & aTarget,QueueItem::Priority p)54 	DirectoryItem(const UserPtr& aUser, const string& aName, const string& aTarget,
55 		QueueItem::Priority p) : name(aName), target(aTarget), priority(p), user(aUser) { }
~DirectoryItem()56 	~DirectoryItem() { }
57 
getUser()58 	UserPtr& getUser() { return user; }
setUser(const UserPtr & aUser)59 	void setUser(const UserPtr& aUser) { user = aUser; }
60 
61 	GETSET(string, name, Name);
62 	GETSET(string, target, Target);
63 	GETSET(QueueItem::Priority, priority, Priority);
64 private:
65 	UserPtr user;
66 };
67 
68 class ConnectionQueueItem;
69 class QueueLoader;
70 
71 class QueueManager : public Singleton<QueueManager>, public Speaker<QueueManagerListener>, private TimerManagerListener,
72 	private SearchManagerListener, private ClientManagerListener
73 {
74 public:
75 	/** Add a file to the queue. */
76 	void add(const string& aTarget, int64_t aSize, const TTHValue& root, const UserPtr& aUser, const string& hubHint,
77 		int aFlags = 0, bool addBad = true) throw(QueueException, FileException);
78 	/** Add a user's filelist to the queue. */
79 	void addList(const UserPtr& aUser, const string& hubHint, int aFlags, const string& aInitialDir = Util::emptyString) throw(QueueException, FileException);
80 	/** Readd a source that was removed */
81 	void readd(const string& target, const UserPtr& aUser, const string& hubHint) throw(QueueException);
82 	/** Add a directory to the queue (downloads filelist and matches the directory). */
83 	void addDirectory(const string& aDir, const UserPtr& aUser, const string& hubHint, const string& aTarget, QueueItem::Priority p = QueueItem::DEFAULT) throw();
84 
85 	int matchListing(const DirectoryListing& dl, const string& hubHint) throw();
86 
87 	bool getTTH(const string& name, TTHValue& tth) throw();
88 
89 	int64_t getSize(const string& target) throw();
90 	int64_t getPos(const string& target) throw();
91 
92 	/** Move the target location of a queued item. Running items are silently ignored */
93 	void move(const string& aSource, const string& aTarget) throw();
94 
95 	void remove(const string& aTarget) throw();
96 	void removeSource(const string& aTarget, const UserPtr& aUser, int reason, bool removeConn = true) throw();
97 	void removeSource(const UserPtr& aUser, int reason) throw();
98 
99 	void recheck(const string& aTarget);
100 
101 	void setPriority(const string& aTarget, QueueItem::Priority p) throw();
102 
103 	void getTargets(const TTHValue& tth, StringList& sl);
lockQueue()104 	QueueItem::StringMap& lockQueue() throw() { cs.enter(); return fileQueue.getQueue(); } ;
unlockQueue()105 	void unlockQueue() throw() { cs.leave(); }
106 
107 	Download* getDownload(UserConnection& aSource, bool supportsTrees) throw();
108 	void putDownload(Download* aDownload, bool finished) throw();
109 	void setFile(Download* download);
110 
111 	int64_t getQueued(const UserPtr& aUser) const;
112 
113 	/** @return The highest priority download the user has, PAUSED may also mean no downloads */
114 	QueueItem::Priority hasDownload(const UserPtr& aUser) throw();
115 
116 	int countOnlineSources(const string& aTarget);
117 
118 	void loadQueue() throw();
119 	void saveQueue() throw();
120 
121 	GETSET(uint64_t, lastSave, LastSave);
122 	GETSET(string, queueFile, QueueFile);
123 private:
124 	enum { MOVER_LIMIT = 10*1024*1024 };
125 	class FileMover : public Thread {
126 	public:
FileMover()127 		FileMover() : active(false) { }
~FileMover()128 		virtual ~FileMover() { join(); }
129 
130 		void moveFile(const string& source, const string& target);
131 		virtual int run();
132 	private:
133 		typedef pair<string, string> FilePair;
134 		typedef vector<FilePair> FileList;
135 		typedef FileList::iterator FileIter;
136 
137 		bool active;
138 
139 		FileList files;
140 		CriticalSection cs;
141 	} mover;
142 
143 	class Rechecker : public Thread {
144 		struct DummyOutputStream : OutputStream {
writeDummyOutputStream145 			virtual size_t write(const void*, size_t n) throw(Exception) { return n; }
flushDummyOutputStream146 			virtual size_t flush() throw(Exception) { return 0; }
147 		};
148 
149 	public:
Rechecker(QueueManager * qm_)150 		explicit Rechecker(QueueManager* qm_) : qm(qm_), active(false) { }
~Rechecker()151 		virtual ~Rechecker() { join(); }
152 
153 		void add(const string& file);
154 		virtual int run();
155 
156 	private:
157 		QueueManager* qm;
158 		bool active;
159 
160 		StringList files;
161 		CriticalSection cs;
162 	} rechecker;
163 
164 	/** All queue items by target */
165 	class FileQueue {
166 	public:
FileQueue()167 		FileQueue() : lastInsert(queue.end()) { }
~FileQueue()168 		~FileQueue() {
169 			for(QueueItem::StringIter i = queue.begin(); i != queue.end(); ++i)
170 				delete i->second;
171 		}
172 		void add(QueueItem* qi);
173 		QueueItem* add(const string& aTarget, int64_t aSize, int aFlags, QueueItem::Priority p,
174 			const string& aTempTarget, time_t aAdded, const TTHValue& root)
175 			throw(QueueException, FileException);
176 
177 		QueueItem* find(const string& target);
178 		void find(QueueItem::List& sl, int64_t aSize, const string& ext);
179 		void find(QueueItem::List& ql, const TTHValue& tth);
180 
181 		bool exists(const TTHValue& tth) const;
182 
183 		QueueItem* findAutoSearch(StringList& recent);
getSize()184 		size_t getSize() { return queue.size(); }
getQueue()185 		QueueItem::StringMap& getQueue() { return queue; }
186 		void move(QueueItem* qi, const string& aTarget);
187 		void remove(QueueItem* qi);
188 	private:
189 		QueueItem::StringMap queue;
190 		/** A hint where to insert an item... */
191 		QueueItem::StringIter lastInsert;
192 	};
193 
194 	/** All queue items indexed by user (this is a cache for the FileQueue really...) */
195 	class UserQueue {
196 	public:
197 		void add(QueueItem* qi);
198 		void add(QueueItem* qi, const UserPtr& aUser);
199 		QueueItem* getNext(const UserPtr& aUser, QueueItem::Priority minPrio = QueueItem::LOWEST, int64_t wantedSize = 0);
200 		QueueItem* getRunning(const UserPtr& aUser);
201 		void addDownload(QueueItem* qi, Download* d);
202 		void removeDownload(QueueItem* qi, const UserPtr& d);
getList(int p)203 		QueueItem::UserListMap& getList(int p) { return userQueue[p]; }
204 		void remove(QueueItem* qi, bool removeRunning = true);
205 		void remove(QueueItem* qi, const UserPtr& aUser, bool removeRunning = true);
206 		void setPriority(QueueItem* qi, QueueItem::Priority p);
207 
getRunning()208 		QueueItem::UserMap& getRunning() { return running; }
isRunning(const UserPtr & aUser)209 		bool isRunning(const UserPtr& aUser) const {
210 			return (running.find(aUser) != running.end());
211 		}
212 		int64_t getQueued(const UserPtr& aUser) const;
213 	private:
214 		/** QueueItems by priority and user (this is where the download order is determined) */
215 		QueueItem::UserListMap userQueue[QueueItem::LAST];
216 		/** Currently running downloads, a QueueItem is always either here or in the userQueue */
217 		QueueItem::UserMap running;
218 	};
219 
220 	friend class QueueLoader;
221 	friend class Singleton<QueueManager>;
222 
223 	QueueManager();
224 	virtual ~QueueManager() throw();
225 
226 	mutable CriticalSection cs;
227 
228 	/** QueueItems by target */
229 	FileQueue fileQueue;
230 	/** QueueItems by user */
231 	UserQueue userQueue;
232 	/** Directories queued for downloading */
233 	DirectoryItem::DirectoryMap directories;
234 	/** Recent searches list, to avoid searching for the same thing too often */
235 	StringList recent;
236 	/** The queue needs to be saved */
237 	bool dirty;
238 	/** Next search */
239 	uint32_t nextSearch;
240 	/** Sanity check for the target filename */
241 	static string checkTarget(const string& aTarget, int64_t aSize) throw(QueueException, FileException);
242 	/** Add a source to an existing queue item */
243 	bool addSource(QueueItem* qi, const UserPtr& aUser, Flags::MaskType addBad) throw(QueueException, FileException);
244 
245 	void processList(const string& name, UserPtr& user, int flags);
246 
247 	void load(const SimpleXML& aXml);
248 	void moveFile(const string& source, const string& target);
249 	void moveStuckFile(QueueItem* qi);
250 	void rechecked(QueueItem* qi);
251 
252 	void setDirty();
253 
254 	string getListPath(const UserPtr& user);
255 
256 	// TimerManagerListener
257 	virtual void on(TimerManagerListener::Second, uint32_t aTick) throw();
258 	virtual void on(TimerManagerListener::Minute, uint32_t aTick) throw();
259 
260 	// SearchManagerListener
261 	virtual void on(SearchManagerListener::SR, const SearchResultPtr&) throw();
262 
263 	// ClientManagerListener
264 	virtual void on(ClientManagerListener::UserConnected, const UserPtr& aUser) throw();
265 	virtual void on(ClientManagerListener::UserDisconnected, const UserPtr& aUser) throw();
266 };
267 
268 } // namespace dcpp
269 
270 #endif // !defined(QUEUE_MANAGER_H)
271