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 #include "stdinc.h"
20 #include "DCPlusPlus.h"
21 
22 #include "DirectoryListing.h"
23 
24 #include "QueueManager.h"
25 #include "ClientManager.h"
26 
27 #include "StringTokenizer.h"
28 #include "SimpleXML.h"
29 #include "FilteredFile.h"
30 #include "BZUtils.h"
31 #include "CryptoManager.h"
32 #include "ShareManager.h"
33 
34 #ifdef ff
35 #undef ff
36 #endif
37 
38 namespace dcpp {
39 
getUserFromFilename(const string & fileName)40 UserPtr DirectoryListing::getUserFromFilename(const string& fileName) {
41 	// General file list name format: [username].[CID].[xml|xml.bz2|DcLst]
42 
43 	string name = Util::getFileName(fileName);
44 
45 	// Strip off any extensions
46 	if(Util::stricmp(name.c_str() + name.length() - 6, ".DcLst") == 0) {
47 		name.erase(name.length() - 6);
48 	}
49 
50 	if(Util::stricmp(name.c_str() + name.length() - 4, ".bz2") == 0) {
51 		name.erase(name.length() - 4);
52 	}
53 
54 	if(Util::stricmp(name.c_str() + name.length() - 4, ".xml") == 0) {
55 		name.erase(name.length() - 4);
56 	}
57 
58 	// Find CID
59 	string::size_type i = name.rfind('.');
60 	if(i == string::npos) {
61 		return UserPtr();
62 	}
63 
64 	size_t n = name.length() - (i + 1);
65 	// CID's always 39 chars long...
66 	if(n != 39)
67 		return UserPtr();
68 
69 	CID cid(name.substr(i + 1));
70 	if(cid.isZero())
71 		return UserPtr();
72 
73 	return ClientManager::getInstance()->getUser(cid);
74 }
75 
loadFile(const string & name)76 void DirectoryListing::loadFile(const string& name) throw(Exception) {
77 	string txt;
78 
79 	// For now, we detect type by ending...
80 	string ext = Util::getFileExt(name);
81 
82 	if(Util::stricmp(ext, ".bz2") == 0) {
83 		dcpp::File ff(name, dcpp::File::READ, dcpp::File::OPEN);
84 		FilteredInputStream<UnBZFilter, false> f(&ff);
85 		const size_t BUF_SIZE = 64*1024;
86 		boost::scoped_array<char> buf(new char[BUF_SIZE]);
87 		size_t len;
88 		size_t bytesRead = 0;
89 		for(;;) {
90 			size_t n = BUF_SIZE;
91 			len = f.read(&buf[0], n);
92 			txt.append(&buf[0], len);
93 			bytesRead += len;
94 			if(SETTING(MAX_FILELIST_SIZE) && bytesRead > (size_t)SETTING(MAX_FILELIST_SIZE)*1024*1024)
95 				throw FileException(_("Greater than maximum filelist size"));
96 			if(len < BUF_SIZE)
97 				break;
98 		}
99 	} else if(Util::stricmp(ext, ".xml") == 0) {
100 		int64_t sz = dcpp::File::getSize(name);
101 		if(sz == -1 || sz >= static_cast<int64_t>(txt.max_size()))
102 			throw FileException(_("File not available"));
103 
104 		txt.resize((size_t) sz);
105 		size_t n = txt.length();
106 		dcpp::File(name, dcpp::File::READ, dcpp::File::OPEN).read(&txt[0], n);
107 	}
108 
109 	loadXML(txt, false);
110 }
111 
112 class ListLoader : public SimpleXMLReader::CallBack {
113 public:
ListLoader(DirectoryListing::Directory * root,bool aUpdating)114 	ListLoader(DirectoryListing::Directory* root, bool aUpdating) : cur(root), base("/"), inListing(false), updating(aUpdating) {
115 	}
116 
~ListLoader()117 	virtual ~ListLoader() { }
118 
119 	virtual void startTag(const string& name, StringPairList& attribs, bool simple);
120 	virtual void endTag(const string& name, const string& data);
121 
getBase() const122 	const string& getBase() const { return base; }
123 private:
124 	DirectoryListing::Directory* cur;
125 
126 	StringMap params;
127 	string base;
128 	bool inListing;
129 	bool updating;
130 };
131 
loadXML(const string & xml,bool updating)132 string DirectoryListing::loadXML(const string& xml, bool updating) {
133 	ListLoader ll(getRoot(), updating);
134 	SimpleXMLReader(&ll).fromXML(xml);
135 	return ll.getBase();
136 }
137 
138 static const string sFileListing = "FileListing";
139 static const string sBase = "Base";
140 static const string sDirectory = "Directory";
141 static const string sIncomplete = "Incomplete";
142 static const string sFile = "File";
143 static const string sName = "Name";
144 static const string sSize = "Size";
145 static const string sTTH = "TTH";
146 
startTag(const string & name,StringPairList & attribs,bool simple)147 void ListLoader::startTag(const string& name, StringPairList& attribs, bool simple) {
148 	if(inListing) {
149 		if(name == sFile) {
150 			const string& n = getAttrib(attribs, sName, 0);
151 			if(n.empty())
152 				return;
153 			const string& s = getAttrib(attribs, sSize, 1);
154 			if(s.empty())
155 				return;
156 			const string& h = getAttrib(attribs, sTTH, 2);
157 			if(h.empty()) {
158 				return;
159 			}
160 			DirectoryListing::File* f = new DirectoryListing::File(cur, n, Util::toInt64(s), h);
161 			cur->files.push_back(f);
162 		} else if(name == sDirectory) {
163 			const string& n = getAttrib(attribs, sName, 0);
164 			if(n.empty()) {
165 				throw SimpleXMLException(_("Directory missing name attribute"));
166 			}
167 			bool incomp = getAttrib(attribs, sIncomplete, 1) == "1";
168 			DirectoryListing::Directory* d = NULL;
169 			if(updating) {
170 				for(DirectoryListing::Directory::Iter i = cur->directories.begin(); i != cur->directories.end(); ++i) {
171 					if((*i)->getName() == n) {
172 						d = *i;
173 						if(!d->getComplete())
174 							d->setComplete(!incomp);
175 						break;
176 					}
177 				}
178 			}
179 			if(d == NULL) {
180 				d = new DirectoryListing::Directory(cur, n, false, !incomp);
181 				cur->directories.push_back(d);
182 			}
183 			cur = d;
184 
185 			if(simple) {
186 				// To handle <Directory Name="..." />
187 				endTag(name, Util::emptyString);
188 			}
189 		}
190 	} else if(name == sFileListing) {
191 		const string& b = getAttrib(attribs, sBase, 2);
192 		if(b.size() >= 1 && b[0] == '/' && b[b.size()-1] == '/') {
193 			base = b;
194 		}
195 		StringList sl = StringTokenizer<string>(base.substr(1), '/').getTokens();
196 		for(StringIter i = sl.begin(); i != sl.end(); ++i) {
197 			DirectoryListing::Directory* d = NULL;
198 			for(DirectoryListing::Directory::Iter j = cur->directories.begin(); j != cur->directories.end(); ++j) {
199 				if((*j)->getName() == *i) {
200 					d = *j;
201 					break;
202 				}
203 			}
204 			if(d == NULL) {
205 				d = new DirectoryListing::Directory(cur, *i, false, false);
206 				cur->directories.push_back(d);
207 			}
208 			cur = d;
209 		}
210 		cur->setComplete(true);
211 		inListing = true;
212 
213 		if(simple) {
214 			// To handle <Directory Name="..." />
215 			endTag(name, Util::emptyString);
216 		}
217 	}
218 }
219 
endTag(const string & name,const string &)220 void ListLoader::endTag(const string& name, const string&) {
221 	if(inListing) {
222 		if(name == sDirectory) {
223 			cur = cur->getParent();
224 		} else if(name == sFileListing) {
225 			// cur should be root now...
226 			inListing = false;
227 		}
228 	}
229 }
230 
getPath(const Directory * d) const231 string DirectoryListing::getPath(const Directory* d) const {
232 	if(d == root)
233 		return "";
234 
235 	string dir;
236 	dir.reserve(128);
237 	dir.append(d->getName());
238 	dir.append(1, '\\');
239 
240 	Directory* cur = d->getParent();
241 	while(cur!=root) {
242 		dir.insert(0, cur->getName() + '\\');
243 		cur = cur->getParent();
244 	}
245 	return dir;
246 }
247 
getLocalPath(const File * f) const248 string DirectoryListing::getLocalPath(const File* f) const {
249 	if(getUser() == ClientManager::getInstance()->getMe()) {
250 		string path;
251 		try {
252 			path = ShareManager::getInstance()->toReal(Util::toAdcFile(getPath(f) + f->getName()));
253 		} catch(const ShareException&) {
254 			// Ignore
255 		}
256 		if(!path.empty() && (dcpp::File::getSize(path) != -1)) {
257 			return path;
258 		}
259 	}
260 
261 	return string();
262 }
263 
download(Directory * aDir,const string & aTarget,bool highPrio)264 void DirectoryListing::download(Directory* aDir, const string& aTarget, bool highPrio) {
265 	string tmp;
266 	string target = (aDir == getRoot()) ? aTarget : aTarget + aDir->getName() + PATH_SEPARATOR;
267 	// First, recurse over the directories
268 	Directory::List& lst = aDir->directories;
269 	sort(lst.begin(), lst.end(), Directory::DirSort());
270 	for(Directory::Iter j = lst.begin(); j != lst.end(); ++j) {
271 		download(*j, target, highPrio);
272 	}
273 	// Then add the files
274 	File::List& l = aDir->files;
275 	sort(l.begin(), l.end(), File::FileSort());
276 	for(File::Iter i = aDir->files.begin(); i != aDir->files.end(); ++i) {
277 		File* file = *i;
278 		try {
279 			download(file, target + file->getName(), false, highPrio);
280 		} catch(const QueueException&) {
281 			// Catch it here to allow parts of directories to be added...
282 		} catch(const FileException&) {
283 			//..
284 		}
285 	}
286 }
287 
download(const string & aDir,const string & aTarget,bool highPrio)288 void DirectoryListing::download(const string& aDir, const string& aTarget, bool highPrio) {
289 	dcassert(aDir.size() > 2);
290 	dcassert(aDir[aDir.size() - 1] == '\\'); // This should not be PATH_SEPARATOR
291 	Directory* d = find(aDir, getRoot());
292 	if(d != NULL)
293 		download(d, aTarget, highPrio);
294 }
295 
download(File * aFile,const string & aTarget,bool view,bool highPrio)296 void DirectoryListing::download(File* aFile, const string& aTarget, bool view, bool highPrio) {
297 	int flags = (view ? (QueueItem::FLAG_TEXT | QueueItem::FLAG_CLIENT_VIEW) : 0);
298 
299 	// TODO hubHint?
300 	QueueManager::getInstance()->add(aTarget, aFile->getSize(), aFile->getTTH(), getUser(), Util::emptyString, flags);
301 
302 	if(highPrio)
303 		QueueManager::getInstance()->setPriority(aTarget, QueueItem::HIGHEST);
304 }
305 
find(const string & aName,Directory * current)306 DirectoryListing::Directory* DirectoryListing::find(const string& aName, Directory* current) {
307 	string::size_type end = aName.find('\\');
308 	dcassert(end != string::npos);
309 	string name = aName.substr(0, end);
310 
311 	Directory::Iter i = std::find(current->directories.begin(), current->directories.end(), name);
312 	if(i != current->directories.end()) {
313 		if(end == (aName.size() - 1))
314 			return *i;
315 		else
316 			return find(aName.substr(end + 1), *i);
317 	}
318 	return NULL;
319 }
320 
321 struct HashContained {
HashContaineddcpp::HashContained322 	HashContained(const DirectoryListing::Directory::TTHSet& l) : tl(l) { }
323 	const DirectoryListing::Directory::TTHSet& tl;
operator ()dcpp::HashContained324 	bool operator()(const DirectoryListing::File::Ptr i) const {
325 		return tl.count((i->getTTH())) && (DeleteFunction()(i), true);
326 	}
327 private:
328 	HashContained& operator=(HashContained&);
329 };
330 
331 struct DirectoryEmpty {
operator ()dcpp::DirectoryEmpty332 	bool operator()(const DirectoryListing::Directory::Ptr i) const {
333 		bool r = i->getFileCount() + i->directories.size() == 0;
334 		if (r) DeleteFunction()(i);
335 		return r;
336 	}
337 };
338 
filterList(DirectoryListing & dirList)339 void DirectoryListing::Directory::filterList(DirectoryListing& dirList) {
340 		DirectoryListing::Directory* d = dirList.getRoot();
341 
342 		TTHSet l;
343 		d->getHashList(l);
344 		filterList(l);
345 }
346 
filterList(DirectoryListing::Directory::TTHSet & l)347 void DirectoryListing::Directory::filterList(DirectoryListing::Directory::TTHSet& l) {
348 	for(Iter i = directories.begin(); i != directories.end(); ++i) (*i)->filterList(l);
349 	directories.erase(std::remove_if(directories.begin(),directories.end(),DirectoryEmpty()),directories.end());
350 	files.erase(std::remove_if(files.begin(),files.end(),HashContained(l)),files.end());
351 }
352 
getHashList(DirectoryListing::Directory::TTHSet & l)353 void DirectoryListing::Directory::getHashList(DirectoryListing::Directory::TTHSet& l) {
354 	for(Iter i = directories.begin(); i != directories.end(); ++i) (*i)->getHashList(l);
355 	for(DirectoryListing::File::Iter i = files.begin(); i != files.end(); ++i) l.insert((*i)->getTTH());
356 }
357 
getTotalSize(bool adl)358 int64_t DirectoryListing::Directory::getTotalSize(bool adl) {
359 	int64_t x = getSize();
360 	for(Iter i = directories.begin(); i != directories.end(); ++i) {
361 		if(!(adl && (*i)->getAdls()))
362 			x += (*i)->getTotalSize(adls);
363 	}
364 	return x;
365 }
366 
getTotalFileCount(bool adl)367 size_t DirectoryListing::Directory::getTotalFileCount(bool adl) {
368 	size_t x = getFileCount();
369 	for(Iter i = directories.begin(); i != directories.end(); ++i) {
370 		if(!(adl && (*i)->getAdls()))
371 			x += (*i)->getTotalFileCount(adls);
372 	}
373 	return x;
374 }
375 
376 } // namespace dcpp
377