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