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 #include "stdinc.h"
20 
21 #include "ShareManager.h"
22 #include "AdcHub.h"
23 #include "CryptoManager.h"
24 #include "ClientManager.h"
25 #include "LogManager.h"
26 #include "HashManager.h"
27 #include "QueueManager.h"
28 #include "UploadManager.h"
29 #include "SimpleXML.h"
30 #include "StringTokenizer.h"
31 #include "File.h"
32 #include "FilteredFile.h"
33 #include "BZUtils.h"
34 #include "Wildcards.h"
35 #include "Transfer.h"
36 #include "UserConnection.h"
37 #include "Download.h"
38 #include "HashBloom.h"
39 #include "SearchResult.h"
40 #include "version.h"
41 #ifdef WITH_DHT
42 #include "dht/IndexManager.h"
43 #endif
44 #ifndef _WIN32
45 #include <sys/types.h>
46 #include <dirent.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 #include <fnmatch.h>
50 #endif
51 
52 #include <limits>
53 
54 namespace dcpp {
55 
ShareManager()56 ShareManager::ShareManager() : hits(0), xmlListLen(0), bzXmlListLen(0),
57     xmlDirty(true), forceXmlRefresh(false), refreshDirs(false), update(false), initial(true), listN(0), refreshing(false),
58     lastXmlUpdate(0), lastFullUpdate(GET_TICK()), bloom(1<<20)
59 {
60     SettingsManager::getInstance()->addListener(this);
61     TimerManager::getInstance()->addListener(this);
62     QueueManager::getInstance()->addListener(this);
63     HashManager::getInstance()->addListener(this);
64 }
65 
~ShareManager()66 ShareManager::~ShareManager() {
67     SettingsManager::getInstance()->removeListener(this);
68     TimerManager::getInstance()->removeListener(this);
69     QueueManager::getInstance()->removeListener(this);
70     HashManager::getInstance()->removeListener(this);
71 
72     join();
73 
74     if(bzXmlRef.get()) {
75         bzXmlRef.reset();
76         File::deleteFile(getBZXmlFile());
77     }
78 }
79 
Directory(const string & aName,const ShareManager::Directory::Ptr & aParent)80 ShareManager::Directory::Directory(const string& aName, const ShareManager::Directory::Ptr& aParent) :
81     size(0),
82     name(aName),
83     parent(aParent.get()),
84     fileTypes(1 << SearchManager::TYPE_DIRECTORY)
85 {
86 }
87 
getADCPath() const88 string ShareManager::Directory::getADCPath() const noexcept {
89     if(!getParent())
90         return '/' + name + '/';
91     return getParent()->getADCPath() + name + '/';
92 }
93 
getFullName() const94 string ShareManager::Directory::getFullName() const noexcept {
95     if(!getParent())
96         return getName() + '\\';
97     return getParent()->getFullName() + getName() + '\\';
98 }
99 
addType(uint32_t type)100 void ShareManager::Directory::addType(uint32_t type) noexcept {
101     if(!hasType(type)) {
102         fileTypes |= (1 << type);
103         if(getParent())
104             getParent()->addType(type);
105     }
106 }
107 
getRealPath(const std::string & path) const108 string ShareManager::Directory::getRealPath(const std::string& path) const {
109     if(getParent()) {
110         return getParent()->getRealPath(getName() + PATH_SEPARATOR_STR + path);
111     } else {
112         return ShareManager::getInstance()->findRealRoot(getName(), path);
113     }
114 }
115 
findRealRoot(const string & virtualRoot,const string & virtualPath) const116 string ShareManager::findRealRoot(const string& virtualRoot, const string& virtualPath) const {
117     for(auto i = shares.begin(); i != shares.end(); ++i) {
118         if(Util::stricmp(i->second, virtualRoot) == 0) {
119             std::string name = i->first + virtualPath;
120             dcdebug("Matching %s\n", name.c_str());
121             if (File::getSize(name) != -1)//NOTE: see core 0.750
122                 return name;
123             }
124         }
125 
126     throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
127 }
128 
getSize() const129 int64_t ShareManager::Directory::getSize() const noexcept {
130     int64_t tmp = size;
131     for(auto i = directories.begin(); i != directories.end(); ++i)
132         tmp+=i->second->getSize();
133     return tmp;
134 }
135 
toVirtual(const TTHValue & tth) const136 string ShareManager::toVirtual(const TTHValue& tth) const {
137     if(tth == bzXmlRoot) {
138         return Transfer::USER_LIST_NAME_BZ;
139     } else if(tth == xmlRoot) {
140         return Transfer::USER_LIST_NAME;
141     }
142 
143     Lock l(cs);
144     auto i = tthIndex.find(tth);
145     if(i != tthIndex.end()) {
146         return i->second->getADCPath();
147     } else {
148         throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
149     }
150 }
151 
toReal(const string & virtualFile)152 string ShareManager::toReal(const string& virtualFile) {
153     Lock l(cs);
154     if(virtualFile == "MyList.DcLst") {
155         throw ShareException("NMDC-style lists no longer supported, please upgrade your client");
156     } else if(virtualFile == Transfer::USER_LIST_NAME_BZ || virtualFile == Transfer::USER_LIST_NAME) {
157         generateXmlList();
158         return getBZXmlFile();
159     }
160 
161     return findFile(virtualFile)->getRealPath();
162 }
163 
getRealPaths(const string & virtualPath)164 StringList ShareManager::getRealPaths(const string& virtualPath) {
165     if(virtualPath.empty())
166         throw ShareException("empty virtual path");
167 
168     StringList ret;
169 
170     Lock l(cs);
171 
172     if(*(virtualPath.end() - 1) == '/') {
173         // directory
174         Directory::Ptr d = splitVirtual(virtualPath).first;
175 
176         // imitate Directory::getRealPath
177         if(d->getParent()) {
178             ret.push_back(d->getParent()->getRealPath(d->getName()));
179         } else {
180             for(auto i = shares.begin(); i != shares.end(); ++i) {
181                 if(Util::stricmp(i->second, d->getName()) == 0) {
182                     // remove the trailing path sep
183                     if(FileFindIter(i->first.substr(0, i->first.size() - 1)) != FileFindIter()) {
184                         ret.push_back(i->first);
185                     }
186                 }
187             }
188         }
189 
190     } else {
191         // file
192         ret.push_back(toReal(virtualPath));
193     }
194 
195     return ret;
196 }
197 
getTTH(const string & virtualFile) const198 TTHValue ShareManager::getTTH(const string& virtualFile) const {
199     Lock l(cs);
200     if(virtualFile == Transfer::USER_LIST_NAME_BZ) {
201         return bzXmlRoot;
202     } else if(virtualFile == Transfer::USER_LIST_NAME) {
203         return xmlRoot;
204     }
205 
206     return findFile(virtualFile)->getTTH();
207 }
208 
getTree(const string & virtualFile) const209 MemoryInputStream* ShareManager::getTree(const string& virtualFile) const {
210     TigerTree tree;
211     if(virtualFile.compare(0, 4, "TTH/") == 0) {
212         if(!HashManager::getInstance()->getTree(TTHValue(virtualFile.substr(4)), tree))
213             return 0;
214     } else {
215         try {
216             TTHValue tth = getTTH(virtualFile);
217             HashManager::getInstance()->getTree(tth, tree);
218         } catch(const Exception&) {
219             return 0;
220         }
221     }
222 
223     ByteVector buf = tree.getLeafData();
224     return new MemoryInputStream(&buf[0], buf.size());
225 }
226 
getFileInfo(const string & aFile)227 AdcCommand ShareManager::getFileInfo(const string& aFile) {
228     if(aFile == Transfer::USER_LIST_NAME) {
229         generateXmlList();
230         AdcCommand cmd(AdcCommand::CMD_RES);
231         cmd.addParam("FN", aFile);
232         cmd.addParam("SI", Util::toString(xmlListLen));
233         cmd.addParam("TR", xmlRoot.toBase32());
234         return cmd;
235     } else if(aFile == Transfer::USER_LIST_NAME_BZ) {
236         generateXmlList();
237 
238         AdcCommand cmd(AdcCommand::CMD_RES);
239         cmd.addParam("FN", aFile);
240         cmd.addParam("SI", Util::toString(bzXmlListLen));
241         cmd.addParam("TR", bzXmlRoot.toBase32());
242         return cmd;
243     }
244 
245     if(aFile.compare(0, 4, "TTH/") != 0)
246         throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
247 
248     TTHValue val(aFile.substr(4));
249     Lock l(cs);
250     auto i = tthIndex.find(val);
251     if(i == tthIndex.end()) {
252         throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
253     }
254 
255     const Directory::File& f = *i->second;
256     AdcCommand cmd(AdcCommand::CMD_RES);
257     cmd.addParam("FN", f.getADCPath());
258     cmd.addParam("SI", Util::toString(f.getSize()));
259     cmd.addParam("TR", f.getTTH().toBase32());
260     return cmd;
261 }
262 
splitVirtual(const string & virtualPath) const263 pair<ShareManager::Directory::Ptr, string> ShareManager::splitVirtual(const string& virtualPath) const {
264     if(virtualPath.empty() || virtualPath[0] != '/') {
265         throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
266     }
267 
268     auto i = virtualPath.find('/', 1);
269     if(i == string::npos || i == 1) {
270         throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
271     }
272 
273     auto dmi = getByVirtual( virtualPath.substr(1, i - 1));
274     if(dmi == directories.end()) {
275         throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
276     }
277 
278     auto d = *dmi;
279 
280     auto j = i + 1;
281     while((i = virtualPath.find('/', j)) != string::npos) {
282         auto mi = d->directories.find(virtualPath.substr(j, i - j));
283         j = i + 1;
284         if(mi == d->directories.end())
285             throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
286         d = mi->second;
287     }
288 
289     return make_pair(d, virtualPath.substr(j));
290 }
291 
findFile(const string & virtualFile) const292 ShareManager::Directory::File::Set::const_iterator ShareManager::findFile(const string& virtualFile) const {
293     if(virtualFile.compare(0, 4, "TTH/") == 0) {
294         auto i = tthIndex.find(TTHValue(virtualFile.substr(4)));
295         if(i == tthIndex.end()) {
296             throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
297         }
298         return i->second;
299     }
300 
301     auto v = splitVirtual(virtualFile);
302     auto it = find_if(v.first->files.begin(), v.first->files.end(),
303             Directory::File::StringComp(v.second));
304     if(it == v.first->files.end())
305         throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
306     return it;
307 }
308 
validateVirtual(const string & aVirt) const309 string ShareManager::validateVirtual(const string& aVirt) const noexcept {
310     string tmp = aVirt;
311     string::size_type idx = 0;
312 
313     while( (idx = tmp.find_first_of("\\/"), idx) != string::npos) {
314         tmp[idx] = '_';
315     }
316     return tmp;
317 }
318 
hasVirtual(const string & virtualName) const319 bool ShareManager::hasVirtual(const string& virtualName) const noexcept {
320     Lock l(cs);
321     return getByVirtual(virtualName) != directories.end();
322 }
323 
load(SimpleXML & aXml)324 void ShareManager::load(SimpleXML& aXml) {
325     Lock l(cs);
326 
327     aXml.resetCurrentChild();
328     if(aXml.findChild("Share")) {
329         aXml.stepIn();
330         while(aXml.findChild("Directory")) {
331             string realPath = aXml.getChildData();
332             if(realPath.empty()) {
333                 continue;
334             }
335             // make sure realPath ends with a PATH_SEPARATOR
336             if(realPath[realPath.size() - 1] != PATH_SEPARATOR) {
337                 realPath += PATH_SEPARATOR;
338             }
339 
340             const string& virtualName = aXml.getChildAttrib("Virtual");
341             string vName = validateVirtual(virtualName.empty() ? Util::getLastDir(realPath) : virtualName);
342             shares.insert(std::make_pair(realPath, vName));
343             if(getByVirtual(vName) == directories.end()) {
344                 directories.push_back(Directory::create(vName));
345             }
346         }
347         aXml.stepOut();
348     }
349 }
350 
351 static const string SDIRECTORY = "Directory";
352 static const string SFILE = "File";
353 static const string SNAME = "Name";
354 static const string SSIZE = "Size";
355 static const string STTH = "TTH";
356 
357 struct ShareLoader : public SimpleXMLReader::CallBack {
ShareLoaderdcpp::ShareLoader358     ShareLoader(ShareManager::DirList& aDirs) : dirs(aDirs), cur(0), depth(0) { }
startTagdcpp::ShareLoader359     virtual void startTag(const string& name, StringPairList& attribs, bool simple) {
360         if(name == SDIRECTORY) {
361             const string& name = getAttrib(attribs, SNAME, 0);
362             if(!name.empty()) {
363                 if(depth == 0) {
364                     for(auto i = dirs.begin(); i != dirs.end(); ++i) {
365                         if(Util::stricmp((*i)->getName(), name) == 0) {
366                             cur = *i;
367                             break;
368                         }
369                     }
370                 } else if(cur) {
371                     cur = ShareManager::Directory::create(name, cur);
372                     cur->getParent()->directories[cur->getName()] = cur;
373                 }
374             }
375 
376             if(simple) {
377                 if(cur) {
378                     cur = cur->getParent();
379                 }
380             } else {
381                 depth++;
382             }
383         } else if(cur && name == SFILE) {
384             const string& fname = getAttrib(attribs, SNAME, 0);
385             const string& size = getAttrib(attribs, SSIZE, 1);
386             const string& root = getAttrib(attribs, STTH, 2);
387             if(fname.empty() || size.empty() || (root.size() != 39)) {
388                 dcdebug("Invalid file found: %s\n", fname.c_str());
389                 return;
390             }
391             cur->files.insert(ShareManager::Directory::File(fname, Util::toInt64(size), cur, TTHValue(root)));
392         }
393     }
endTagdcpp::ShareLoader394     virtual void endTag(const string& name, const string&) {
395         if(name == SDIRECTORY) {
396             depth--;
397             if(cur) {
398                 cur = cur->getParent();
399             }
400         }
401     }
402 
403 private:
404     ShareManager::DirList& dirs;
405 
406     ShareManager::Directory::Ptr cur;
407     size_t depth;
408 };
409 
loadCache()410 bool ShareManager::loadCache() noexcept {
411     try {
412         ShareLoader loader(directories);
413         SimpleXMLReader xml(&loader);
414 
415         dcpp::File ff(Util::getPath(Util::PATH_USER_CONFIG) + "files.xml.bz2", dcpp::File::READ, dcpp::File::OPEN);
416         FilteredInputStream<UnBZFilter, false> f(&ff);
417 
418         xml.parse(f);
419 
420         for(auto i = directories.begin(); i != directories.end(); ++i) {
421             const Directory::Ptr& d = *i;
422             updateIndices(*d);
423         }
424 
425         return true;
426     } catch(const Exception& e) {
427         dcdebug("%s\n", e.getError().c_str());
428     }
429     return false;
430 }
431 
save(SimpleXML & aXml)432 void ShareManager::save(SimpleXML& aXml) {
433     Lock l(cs);
434 
435     aXml.addTag("Share");
436     aXml.stepIn();
437     for(auto i = shares.begin(); i != shares.end(); ++i) {
438         aXml.addTag("Directory", i->first);
439         aXml.addChildAttrib("Virtual", i->second);
440     }
441     aXml.stepOut();
442 }
443 
addDirectory(const string & realPath,const string & virtualName)444 void ShareManager::addDirectory(const string& realPath, const string& virtualName) {
445     if(realPath.empty() || virtualName.empty()) {
446         throw ShareException(_("No directory specified"));
447     }
448 
449     if (!checkHidden(realPath)) {
450         throw ShareException(_("Directory is hidden"));
451     }
452 
453     if(Util::stricmp(SETTING(TEMP_DOWNLOAD_DIRECTORY), realPath) == 0) {
454         throw ShareException(_("The temporary download directory cannot be shared"));
455     }
456     list<string> removeMap;
457     {
458         Lock l(cs);
459 
460         for(auto i = shares.begin(); i != shares.end(); ++i) {
461             if(Util::strnicmp(realPath, i->first, i->first.length()) == 0) {
462                 // Trying to share an already shared directory
463                 //throw ShareException(_("Directory already shared"));
464                 removeMap.push_front(i->first);
465             } else if(Util::strnicmp(realPath, i->first, realPath.length()) == 0) {
466                 // Trying to share a parent directory
467                 //throw ShareException(_("Remove all subdirectories before adding this one"));
468                 removeMap.push_front(i->first);
469             }
470         }
471     }
472     for(list<string>::const_iterator i = removeMap.begin(); i != removeMap.end(); ++i) {
473         removeDirectory(*i);
474     }
475 
476 
477     HashManager::HashPauser pauser;
478 
479     Directory::Ptr dp = buildTree(realPath, Directory::Ptr());
480 
481     string vName = validateVirtual(virtualName);
482     dp->setName(vName);
483 
484     {
485         Lock l(cs);
486 
487         shares.insert(std::make_pair(realPath, vName));
488         updateIndices(*merge(dp));
489 
490         setDirty();
491     }
492 }
493 
merge(const Directory::Ptr & directory)494 ShareManager::Directory::Ptr ShareManager::merge(const Directory::Ptr& directory) {
495     for(auto i = directories.begin(); i != directories.end(); ++i) {
496         if(Util::stricmp((*i)->getName(), directory->getName()) == 0) {
497             dcdebug("Merging directory %s\n", directory->getName().c_str());
498             (*i)->merge(directory);
499             return *i;
500         }
501     }
502 
503     dcdebug("Adding new directory %s\n", directory->getName().c_str());
504 
505     directories.push_back(directory);
506     return directory;
507 }
508 
merge(const Directory::Ptr & source)509 void ShareManager::Directory::merge(const Directory::Ptr& source) {
510     for(auto i = source->directories.begin(); i != source->directories.end(); ++i) {
511         auto subSource = i->second;
512 
513         auto ti = directories.find(subSource->getName());
514         if(ti == directories.end()) {
515             if(findFile(subSource->getName()) != files.end()) {
516                 dcdebug("File named the same as directory");
517             } else {
518                 directories.insert(std::make_pair(subSource->getName(), subSource));
519                                 subSource->parent = this;
520             }
521         } else {
522             auto subTarget = ti->second;
523             subTarget->merge(subSource);
524         }
525     }
526 
527     // All subdirs either deleted or moved to target...
528     source->directories.clear();
529 
530     for(auto i = source->files.begin(); i != source->files.end(); ++i) {
531         if(findFile(i->getName()) == files.end()) {
532             if(directories.find(i->getName()) != directories.end()) {
533                 dcdebug("Directory named the same as file");
534             } else {
535                 auto added = files.insert(*i);
536                 if(added.second) {
537                     const_cast<File&>(*added.first).setParent(this);
538                 }
539             }
540         }
541     }
542 }
543 
removeDirectory(const string & realPath)544 void ShareManager::removeDirectory(const string& realPath) {
545     if(realPath.empty())
546         return;
547 
548     HashManager::getInstance()->stopHashing(realPath);
549 
550     Lock l(cs);
551 
552     auto i = shares.find(realPath);
553     if(i == shares.end()) {
554         return;
555     }
556 
557     auto vName = i->second;
558     for(auto j = directories.begin(); j != directories.end(); ) {
559         if(Util::stricmp((*j)->getName(), vName) == 0) {
560             directories.erase(j++);
561         } else {
562             ++j;
563         }
564     }
565 
566     shares.erase(i);
567 
568     HashManager::HashPauser pauser;
569 
570     // Readd all directories with the same vName
571     for(i = shares.begin(); i != shares.end(); ++i) {
572         if(Util::stricmp(i->second, vName) == 0 && checkHidden(i->first)) {
573             Directory::Ptr dp = buildTree(i->first, 0);
574             dp->setName(i->second);
575             merge(dp);
576         }
577     }
578 
579     rebuildIndices();
580     setDirty();
581 }
582 
renameDirectory(const string & realPath,const string & virtualName)583 void ShareManager::renameDirectory(const string& realPath, const string& virtualName) {
584     removeDirectory(realPath);
585     addDirectory(realPath, virtualName);
586 }
587 
getByVirtual(const string & virtualName) const588 ShareManager::DirList::const_iterator ShareManager::getByVirtual(const string& virtualName) const noexcept {
589     for(auto i = directories.begin(); i != directories.end(); ++i) {
590         if(Util::stricmp((*i)->getName(), virtualName) == 0) {
591             return i;
592         }
593     }
594     return directories.end();
595 }
596 
getShareSize(const string & realPath) const597 int64_t ShareManager::getShareSize(const string& realPath) const noexcept {
598     Lock l(cs);
599     dcassert(!realPath.empty());
600     auto i = shares.find(realPath);
601 
602     if(i != shares.end()) {
603         auto j = getByVirtual(i->second);
604         if(j != directories.end()) {
605             return (*j)->getSize();
606         }
607     }
608     return -1;
609 }
610 
getShareSize() const611 int64_t ShareManager::getShareSize() const noexcept {
612     Lock l(cs);
613     int64_t tmp = 0;
614     for(auto i = tthIndex.begin(); i != tthIndex.end(); ++i) {
615         tmp += i->second->getSize();
616     }
617     return tmp;
618 }
619 
getSharedFiles() const620 size_t ShareManager::getSharedFiles() const noexcept {
621     Lock l(cs);
622     return tthIndex.size();
623 }
624 
buildTree(const string & aName,const Directory::Ptr & aParent)625 ShareManager::Directory::Ptr ShareManager::buildTree(const string& aName, const Directory::Ptr& aParent) {
626     auto dir = Directory::create(Util::getLastDir(aName), aParent);
627 
628     auto lastFileIter = dir->files.begin();
629 
630     FileFindIter end;
631     const string l_skip_list = SETTING(SKIPLIST_SHARE);
632 #ifdef _WIN32
633     for(FileFindIter i(aName + "*"); i != end; ++i) {
634 #else
635     //the fileiter just searches directorys for now, not sure if more
636     //will be needed later
637     //for(FileFindIter i(aName + "*"); i != end; ++i) {
638     for(FileFindIter i(aName); i != end; ++i) {
639 #endif
640         string name = i->getFileName();
641         if(name.empty()) {
642             LogManager::getInstance()->message(str(F_("Invalid file name found while hashing folder %1%") % Util::addBrackets(aName)));
643             continue;
644         }
645 
646         if(name == "." || name == "..")
647             continue;
648         if(!BOOLSETTING(SHARE_HIDDEN) && i->isHidden())
649             continue;
650         if(!BOOLSETTING(FOLLOW_LINKS) && i->isLink())
651             continue;
652 
653         int64_t size = i->getSize();
654 
655         string fileName = aName + name;
656 
657         if (l_skip_list.size())
658         {
659             if (Wildcard::patternMatch(fileName , l_skip_list, '|'))
660             {
661                 LogManager::getInstance()->message(str(F_("Skip share file: %1% (Size: %2%)")
662                 % Util::addBrackets(fileName) % Util::formatBytes(size)));
663                 continue;
664             }
665         }
666         if(i->isDirectory()) {
667             string newName = aName + name + PATH_SEPARATOR;
668             if((::strcmp(newName.c_str(), SETTING(TEMP_DOWNLOAD_DIRECTORY).c_str()) != 0)
669                     && (::strcmp(newName.c_str(), Util::getPath(Util::PATH_USER_CONFIG).c_str()) != 0)
670                     && (::strcmp(newName.c_str(), SETTING(LOG_DIRECTORY).c_str()) != 0)) {
671                 dir->directories[name] = buildTree(newName, dir);
672             }
673         } else {
674             // Not a directory, assume it's a file...make sure we're not sharing the settings file...
675             const string l_ext = Util::getFileExt(name);
676             if ((name != "Thumbs.db") &&
677                 (name != "desktop.ini") &&
678                 (name != "folder.htt")
679                 ) {
680                 if (!BOOLSETTING(SHARE_TEMP_FILES) &&
681                     (::strcmp(l_ext.c_str(), ".dctmp") == 0)) {
682                     LogManager::getInstance()->message(str(F_("Skip share temp file: %1% (Size: %2%)")
683                     % Util::addBrackets(fileName) % Util::formatBytes(size)));
684                     continue;
685                 }
686                 if (BOOLSETTING(SHARE_SKIP_ZERO_BYTE) && size == 0)
687                     continue;
688                 if(Util::stricmp(fileName, SETTING(TLS_PRIVATE_KEY_FILE)) == 0) {
689                     continue;
690                 }
691                 try {
692                     if(HashManager::getInstance()->checkTTH(fileName, size, i->getLastWriteTime()))
693                         lastFileIter = dir->files.insert(lastFileIter, Directory::File(name, size, dir, HashManager::getInstance()->getTTH(fileName, size)));
694                 } catch(const HashException&) {
695                 }
696             }
697         }
698     }
699 
700     return dir;
701 }
702 
703 //NOTE: freedcpp [+
704 #ifdef _WIN32
705 bool ShareManager::checkHidden(const string& aName) const {
706         FileFindIter ff = FileFindIter(aName.substr(0, aName.size() - 1));
707 
708         if (ff != FileFindIter()) {
709                 return (BOOLSETTING(SHARE_HIDDEN) || !ff->isHidden());
710         }
711 
712         return true;
713 }
714 
715 #else // !_WIN32
716 
717 bool ShareManager::checkHidden(const string& aName) const
718 {
719         // check open a directory
720         if (!(FileFindIter(aName) != FileFindIter()))
721                 return true;
722 
723         // check hidden directory
724         bool hidden = false;
725         string path = aName.substr(0, aName.size() - 1);
726         string::size_type i = path.rfind(PATH_SEPARATOR);
727 
728         if (i != string::npos)
729         {
730             string dir = path.substr(i + 1);
731             if (dir[0] == '.')
732                 hidden = true;
733         }
734 
735         return (BOOLSETTING(SHARE_HIDDEN) || !hidden);
736 }
737 #endif // !_WIN32
738 //NOTE: freedcpp +]
739 
740 void ShareManager::updateIndices(Directory& dir) {
741     bloom.add(Text::toLower(dir.getName()));
742 
743     for(auto i = dir.directories.begin(); i != dir.directories.end(); ++i) {
744         updateIndices(*i->second);
745     }
746 
747     dir.size = 0;
748 
749     for(auto i = dir.files.begin(); i != dir.files.end(); ++i) {
750         updateIndices(dir, i);
751     }
752 }
753 
754 void ShareManager::rebuildIndices() {
755     tthIndex.clear();
756     bloom.clear();
757 
758     for(auto i = directories.begin(); i != directories.end(); ++i) {
759         updateIndices(**i);
760     }
761 }
762 
763 void ShareManager::updateIndices(Directory& dir, const Directory::File::Set::iterator& i) {
764     const Directory::File& f = *i;
765 
766     auto j = tthIndex.find(f.getTTH());
767     if(j == tthIndex.end()) {
768         dir.size+=f.getSize();
769     } else {
770         if(!SETTING(LIST_DUPES)) {
771             try {
772                 LogManager::getInstance()->message(str(F_("Duplicate file will not be shared: %1% (Size: %2% B) Dupe matched against: %3%")
773                 % Util::addBrackets(dir.getRealPath(f.getName())) % Util::toString(f.getSize()) % Util::addBrackets(j->second->getParent()->getRealPath(j->second->getName()))));
774             dir.files.erase(i);
775             } catch (const ShareException&) { }
776             return;
777         }
778     }
779 
780     dir.addType(getType(f.getName()));
781 
782     tthIndex.insert(make_pair(f.getTTH(), i));
783     bloom.add(Text::toLower(f.getName()));
784 #ifdef WITH_DHT
785     dht::IndexManager* im = dht::IndexManager::getInstance();
786     if(im && im->isTimeForPublishing())
787         im->publishFile(f.getTTH(), f.getSize());
788 #endif
789 }
790 
791 void ShareManager::refresh(bool dirs /* = false */, bool aUpdate /* = true */, bool block /* = false */) noexcept {
792     if(refreshing.exchange(true) == true) {
793         LogManager::getInstance()->message(_("File list refresh in progress, please wait for it to finish before trying to refresh again"));
794         return;
795     }
796     UploadManager::getInstance()->updateLimits();
797 
798     update = aUpdate;
799     refreshDirs = dirs;
800     join();
801     bool cached = false;
802     if(initial) {
803         cached = loadCache();
804         initial = false;
805     }
806     try {
807         start();
808         if(block && !cached) {
809             join();
810         } else {
811             setThreadPriority(Thread::LOW);
812         }
813     } catch(const ThreadException& e) {
814         LogManager::getInstance()->message(str(F_("File list refresh failed: %1%") % e.getError()));
815     }
816 }
817 
818 StringPairList ShareManager::getDirectories() const noexcept {
819     Lock l(cs);
820     StringPairList ret;
821     for(auto i = shares.begin(); i != shares.end(); ++i) {
822         ret.push_back(make_pair(i->second, i->first));
823     }
824     return ret;
825 }
826 
827 int ShareManager::run() {
828     setThreadName("ShareManager");
829 
830     StringPairList dirs = getDirectories();
831     // Don't need to refresh if no directories are shared
832     if(dirs.empty())
833         refreshDirs = false;
834 
835     if(refreshDirs) {
836         HashManager::HashPauser pauser;
837         LogManager::getInstance()->message(_("File list refresh initiated"));
838 
839         lastFullUpdate = GET_TICK();
840 
841         DirList newDirs;
842         for(auto i = dirs.begin(); i != dirs.end(); ++i) {
843             if (checkHidden(i->second)) {
844                 Directory::Ptr dp = buildTree(i->second, Directory::Ptr());
845                 dp->setName(i->first);
846                 newDirs.push_back(dp);
847             }
848         }
849 
850         {
851             Lock l(cs);
852             directories.clear();
853 
854             for(auto i = newDirs.begin(); i != newDirs.end(); ++i) {
855                 merge(*i);
856             }
857 
858             rebuildIndices();
859         }
860         refreshDirs = false;
861 
862         LogManager::getInstance()->message(_("File list refresh finished"));
863     }
864 
865     if(update) {
866         ClientManager::getInstance()->infoUpdated();
867     }
868     refreshing = false;
869 #ifdef WITH_DHT
870     dht::IndexManager* im = dht::IndexManager::getInstance();
871     if(im && im->isTimeForPublishing())
872         im->setNextPublishing();
873 #endif
874     return 0;
875 }
876 
877 void ShareManager::getBloom(ByteVector& v, size_t k, size_t m, size_t h) const {
878     dcdebug("Creating bloom filter, k=%u, m=%u, h=%u\n",
879             static_cast<unsigned int>(k), static_cast<unsigned int>(m), static_cast<unsigned int>(h));
880     Lock l(cs);
881 
882     HashBloom bloom;
883     bloom.reset(k, m, h);
884     for(auto i = tthIndex.begin(); i != tthIndex.end(); ++i) {
885         bloom.add(i->first);
886     }
887     bloom.copy_to(v);
888 }
889 
890 void ShareManager::generateXmlList() {
891     Lock l(cs);
892     if(forceXmlRefresh || (xmlDirty && (lastXmlUpdate + 15 * 60 * 1000 < GET_TICK() || lastXmlUpdate < lastFullUpdate))) {
893         listN++;
894 
895         try {
896             string tmp2;
897             string indent;
898 
899             string newXmlName = Util::getPath(Util::PATH_USER_CONFIG) + "files" + Util::toString(listN) + ".xml.bz2";
900             {
901                 File f(newXmlName, File::WRITE, File::TRUNCATE | File::CREATE);
902                 // We don't care about the leaves...
903                 CalcOutputStream<TTFilter<1024*1024*1024>, false> bzTree(&f);
904                 FilteredOutputStream<BZFilter, false> bzipper(&bzTree);
905                 CountOutputStream<false> count(&bzipper);
906                 CalcOutputStream<TTFilter<1024*1024*1024>, false> newXmlFile(&count);
907 
908                 newXmlFile.write(SimpleXML::utf8Header);
909                 newXmlFile.write("<FileListing Version=\"1\" CID=\"" + ClientManager::getInstance()->getMe()->getCID().toBase32() + "\" Base=\"/\" Generator=\"" APPNAME " " VERSIONSTRING "\">\r\n");
910                 for(auto i = directories.begin(); i != directories.end(); ++i) {
911                     (*i)->toXml(newXmlFile, indent, tmp2, true);
912                 }
913                 newXmlFile.write("</FileListing>");
914                 newXmlFile.flush();
915 
916                 xmlListLen = count.getCount();
917 
918                 newXmlFile.getFilter().getTree().finalize();
919                 bzTree.getFilter().getTree().finalize();
920 
921                 xmlRoot = newXmlFile.getFilter().getTree().getRoot();
922                 bzXmlRoot = bzTree.getFilter().getTree().getRoot();
923             }
924             const string XmlListFileName = Util::getPath(Util::PATH_USER_CONFIG) + "files.xml.bz2";
925             if(bzXmlRef.get()) {
926                 bzXmlRef.reset();
927                 try {
928                     File::renameFile(XmlListFileName, XmlListFileName + ".bak");
929                 } catch(const FileException&) { }
930             }
931 
932             try {
933                 File::renameFile(newXmlName, XmlListFileName);
934                 newXmlName = XmlListFileName;
935             } catch(const FileException&) {
936                 // Ignore, this is for caching only...
937             }
938             try {
939                 File::copyFile(XmlListFileName, XmlListFileName + ".bak");
940             } catch(const FileException&) { }
941             bzXmlRef = unique_ptr<File>(new File(newXmlName, File::READ, File::OPEN));
942             setBZXmlFile(newXmlName);
943             bzXmlListLen = File::getSize(newXmlName);
944             LogManager::getInstance()->message(str(F_("File list %1% generated") % Util::addBrackets(bzXmlFile)));
945         } catch(const Exception&) {
946             // No new file lists...
947         }
948 
949         xmlDirty = false;
950         forceXmlRefresh = false;
951         lastXmlUpdate = GET_TICK();
952     }
953 }
954 
955 MemoryInputStream* ShareManager::generatePartialList(const string& dir, bool recurse) const {
956     if(dir[0] != '/' || dir[dir.size()-1] != '/')
957         return 0;
958 
959     string xml = SimpleXML::utf8Header;
960     string tmp;
961     xml += "<FileListing Version=\"1\" CID=\"" + ClientManager::getInstance()->getMe()->getCID().toBase32() + "\" Base=\"" + SimpleXML::escape(dir, tmp, false) + "\" Generator=\"" APPNAME " " VERSIONSTRING "\">\r\n";
962     StringOutputStream sos(xml);
963     string indent = "\t";
964 
965     Lock l(cs);
966     if(dir == "/") {
967         for(auto i = directories.begin(); i != directories.end(); ++i) {
968             tmp.clear();
969             (*i)->toXml(sos, indent, tmp, recurse);
970         }
971     } else {
972         string::size_type i = 1, j = 1;
973 
974         Directory::Ptr root;
975 
976         bool first = true;
977         while( (i = dir.find('/', j)) != string::npos) {
978             if(i == j) {
979                 j++;
980                 continue;
981             }
982 
983             if(first) {
984                 first = false;
985                 auto it = getByVirtual(dir.substr(j, i-j));
986 
987                 if(it == directories.end())
988                     return 0;
989                 root = *it;
990 
991             } else {
992                 auto it2 = root->directories.find(dir.substr(j, i-j));
993                 if(it2 == root->directories.end()) {
994                     return 0;
995                 }
996                 root = it2->second;
997             }
998             j = i + 1;
999         }
1000 
1001         if(!root)
1002             return 0;
1003 
1004         for(auto it2 = root->directories.begin(); it2 != root->directories.end(); ++it2) {
1005             it2->second->toXml(sos, indent, tmp, recurse);
1006         }
1007         root->filesToXml(sos, indent, tmp);
1008     }
1009 
1010     xml += "</FileListing>";
1011     return new MemoryInputStream(xml);
1012 }
1013 
1014 #define LITERAL(n) n, sizeof(n)-1
1015 void ShareManager::Directory::toXml(OutputStream& xmlFile, string& indent, string& tmp2, bool fullList) const {
1016     xmlFile.write(indent);
1017     xmlFile.write(LITERAL("<Directory Name=\""));
1018     xmlFile.write(SimpleXML::escape(name, tmp2, true));
1019 
1020     if(fullList) {
1021         xmlFile.write(LITERAL("\">\r\n"));
1022 
1023         indent += '\t';
1024         for(auto i = directories.begin(); i != directories.end(); ++i) {
1025             i->second->toXml(xmlFile, indent, tmp2, fullList);
1026         }
1027 
1028         filesToXml(xmlFile, indent, tmp2);
1029 
1030         indent.erase(indent.length()-1);
1031         xmlFile.write(indent);
1032         xmlFile.write(LITERAL("</Directory>\r\n"));
1033     } else {
1034         if(directories.empty() && files.empty()) {
1035             xmlFile.write(LITERAL("\" />\r\n"));
1036         } else {
1037             xmlFile.write(LITERAL("\" Incomplete=\"1\" />\r\n"));
1038         }
1039     }
1040 }
1041 
1042 void ShareManager::Directory::filesToXml(OutputStream& xmlFile, string& indent, string& tmp2) const {
1043     for(auto i = files.begin(); i != files.end(); ++i) {
1044         const Directory::File& f = *i;
1045 
1046         xmlFile.write(indent);
1047         xmlFile.write(LITERAL("<File Name=\""));
1048         xmlFile.write(SimpleXML::escape(f.getName(), tmp2, true));
1049         xmlFile.write(LITERAL("\" Size=\""));
1050         xmlFile.write(Util::toString(f.getSize()));
1051         xmlFile.write(LITERAL("\" TTH=\""));
1052         tmp2.clear();
1053         xmlFile.write(f.getTTH().toBase32(tmp2));
1054         xmlFile.write(LITERAL("\"/>\r\n"));
1055     }
1056 }
1057 
1058 // These ones we can look up as ints (4 bytes...)...
1059 
1060 static const char* typeAudio[] = { ".mp3", ".mp2", ".mid", ".wav", ".ogg", ".wma", ".669", ".aac", ".aif", ".amf", ".ams", ".ape", ".dbm", ".dmf", ".dsm", ".far", ".mdl", ".med", ".mod", ".mol", ".mp1", ".mpa", ".mpc", ".mpp", ".mtm", ".nst", ".okt", ".psm", ".ptm", ".rmi", ".s3m", ".stm", ".ult", ".umx", ".wow" };
1061 static const char* typeCompressed[] = { ".rar", ".zip", ".ace", ".arj", ".hqx", ".lha", ".sea", ".tar", ".tgz", ".uc2" };
1062 static const char* typeDocument[] = { ".htm", ".doc", ".txt", ".nfo", ".pdf", ".chm", ".rtf",
1063                                       ".xls", ".ppt", ".odt", ".ods", ".odf", ".odp" };
1064 static const char* typeExecutable[] = { ".exe", ".com", ".msi" };
1065 static const char* typePicture[] = { ".jpg", ".gif", ".png", ".eps", ".img", ".pct", ".psp", ".pic", ".tif", ".rle", ".bmp", ".pcx", ".jpe", ".dcx", ".emf", ".ico", ".psd", ".tga", ".wmf", ".xif" };
1066 static const char* typeVideo[] = { ".avi", ".mpg", ".mov", ".flv", ".asf",  ".pxp", ".wmv", ".ogm", ".mkv", ".m1v", ".m2v", ".mpe", ".mps", ".mpv", ".ram", ".vob", ".mp4" };
1067 static const char* typeCDImage[] = {".iso", ".mdf", ".mds", ".nrg", ".vcd", ".bwt", ".ccd", ".cdi", ".pdi", ".cue", ".isz", ".img", ".vc4"};
1068 
1069 static const string type2Audio[] = { ".au", ".it", ".ra", ".xm", ".aiff", ".flac", ".midi" };
1070 static const string type2Picture[] = { ".ai", ".ps", ".pict", ".jpeg", ".tiff" };
1071 static const string type2Video[] = { ".rm", ".divx", ".mpeg", ".mp1v", ".mp2v", ".mpv1", ".mpv2", ".qt", ".rv", ".vivo", ".ts", ".ps" };
1072 
1073 #define IS_TYPE(x) ( type == (*((uint32_t*)x)) )
1074 #define IS_TYPE2(x) (Util::stricmp(aString.c_str() + aString.length() - x.length(), x.c_str()) == 0)
1075 
1076 static bool checkType(const string& aString, int aType) {
1077     if(aType == SearchManager::TYPE_ANY)
1078         return true;
1079 
1080     if(aString.length() < 5)
1081         return false;
1082 
1083     const char* c = aString.c_str() + aString.length() - 3;
1084     if(!Text::isAscii(c))
1085         return false;
1086 
1087     uint32_t type = '.' | (Text::asciiToLower(c[0]) << 8) | (Text::asciiToLower(c[1]) << 16) | (((uint32_t)Text::asciiToLower(c[2])) << 24);
1088 
1089     switch(aType) {
1090     case SearchManager::TYPE_AUDIO:
1091         {
1092             for(size_t i = 0; i < (sizeof(typeAudio) / sizeof(typeAudio[0])); i++) {
1093                 if(IS_TYPE(typeAudio[i])) {
1094                     return true;
1095                 }
1096             }
1097             if( IS_TYPE2(type2Audio[0]) || IS_TYPE2(type2Audio[1]) || IS_TYPE2(type2Audio[2]) ) {
1098                 return true;
1099             }
1100         }
1101         break;
1102     case SearchManager::TYPE_CD_IMAGE:
1103         for(size_t i = 0; i < (sizeof(typeCDImage) / sizeof(typeCDImage[0])); i++) {
1104             if(IS_TYPE(typeCDImage[i])) {
1105                 return true;
1106             }
1107         }
1108 
1109         break;
1110     case SearchManager::TYPE_COMPRESSED:
1111         if( IS_TYPE(typeCompressed[0]) || IS_TYPE(typeCompressed[1]) || IS_TYPE(typeCompressed[2]) ) {
1112             return true;
1113         }
1114         break;
1115     case SearchManager::TYPE_DOCUMENT:
1116         if( IS_TYPE(typeDocument[0]) || IS_TYPE(typeDocument[1]) ||
1117             IS_TYPE(typeDocument[2]) || IS_TYPE(typeDocument[3]) ) {
1118             return true;
1119         }
1120         break;
1121     case SearchManager::TYPE_EXECUTABLE:
1122         if(IS_TYPE(typeExecutable[0]) ) {
1123             return true;
1124         }
1125         break;
1126     case SearchManager::TYPE_PICTURE:
1127         {
1128             for(size_t i = 0; i < (sizeof(typePicture) / sizeof(typePicture[0])); i++) {
1129                 if(IS_TYPE(typePicture[i])) {
1130                     return true;
1131                 }
1132             }
1133             if( IS_TYPE2(type2Picture[0]) || IS_TYPE2(type2Picture[1]) || IS_TYPE2(type2Picture[2]) ) {
1134                 return true;
1135             }
1136         }
1137         break;
1138     case SearchManager::TYPE_VIDEO:
1139         {
1140             for(size_t i = 0; i < (sizeof(typeVideo) / sizeof(typeVideo[0])); i++) {
1141                 if(IS_TYPE(typeVideo[i])) {
1142                     return true;
1143                 }
1144             }
1145             if( IS_TYPE2(type2Video[0]) || IS_TYPE2(type2Video[1]) || IS_TYPE2(type2Video[2]) ) {
1146                 return true;
1147             }
1148         }
1149         break;
1150     default:
1151         dcassert(0);
1152         break;
1153     }
1154     return false;
1155 }
1156 
1157 SearchManager::TypeModes ShareManager::getType(const string& aFileName) const noexcept {
1158     if(aFileName[aFileName.length() - 1] == PATH_SEPARATOR) {
1159         return SearchManager::TYPE_DIRECTORY;
1160     }
1161 
1162     if(checkType(aFileName, SearchManager::TYPE_VIDEO))
1163         return SearchManager::TYPE_VIDEO;
1164     else if(checkType(aFileName, SearchManager::TYPE_AUDIO))
1165         return SearchManager::TYPE_AUDIO;
1166     else if(checkType(aFileName, SearchManager::TYPE_COMPRESSED))
1167         return SearchManager::TYPE_COMPRESSED;
1168     else if(checkType(aFileName, SearchManager::TYPE_DOCUMENT))
1169         return SearchManager::TYPE_DOCUMENT;
1170     else if(checkType(aFileName, SearchManager::TYPE_EXECUTABLE))
1171         return SearchManager::TYPE_EXECUTABLE;
1172     else if(checkType(aFileName, SearchManager::TYPE_PICTURE))
1173         return SearchManager::TYPE_PICTURE;
1174     else if(checkType(aFileName, SearchManager::TYPE_CD_IMAGE))
1175         return SearchManager::TYPE_CD_IMAGE;
1176 
1177     return SearchManager::TYPE_ANY;
1178 }
1179 
1180 /**
1181  * Alright, the main point here is that when searching, a search string is most often found in
1182  * the filename, not directory name, so we want to make that case faster. Also, we want to
1183  * avoid changing StringLists unless we absolutely have to --> this should only be done if a string
1184  * has been matched in the directory name. This new stringlist should also be used in all descendants,
1185  * but not the parents...
1186  */
1187 void ShareManager::Directory::search(SearchResultList& aResults, StringSearch::List& aStrings, int aSearchType, int64_t aSize, int aFileType, Client* aClient, StringList::size_type maxResults) const noexcept {
1188     // Skip everything if there's nothing to find here (doh! =)
1189     if(!hasType(aFileType))
1190         return;
1191 
1192     StringSearch::List* cur = &aStrings;
1193     unique_ptr<StringSearch::List> newStr;
1194 
1195     // Find any matches in the directory name
1196     for(auto k = aStrings.begin(); k != aStrings.end(); ++k) {
1197         if(k->match(name)) {
1198             if(!newStr.get()) {
1199                 newStr = unique_ptr<StringSearch::List>(new StringSearch::List(aStrings));
1200             }
1201             newStr->erase(remove(newStr->begin(), newStr->end(), *k), newStr->end());
1202         }
1203     }
1204 
1205     if(newStr.get() != 0) {
1206         cur = newStr.get();
1207     }
1208 
1209     bool sizeOk = (aSearchType != SearchManager::SIZE_ATLEAST) || (aSize == 0);
1210     if( (cur->empty()) &&
1211         (((aFileType == SearchManager::TYPE_ANY) && sizeOk) || (aFileType == SearchManager::TYPE_DIRECTORY)) ) {
1212         // We satisfied all the search words! Add the directory...(NMDC searches don't support directory size)
1213         SearchResultPtr sr(new SearchResult(SearchResult::TYPE_DIRECTORY, 0, getFullName(), TTHValue()));
1214         aResults.push_back(sr);
1215         ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
1216     }
1217 
1218     if(aFileType != SearchManager::TYPE_DIRECTORY) {
1219         for(auto i = files.begin(); i != files.end(); ++i) {
1220 
1221             if(aSearchType == SearchManager::SIZE_ATLEAST && aSize > i->getSize()) {
1222                 continue;
1223             } else if(aSearchType == SearchManager::SIZE_ATMOST && aSize < i->getSize()) {
1224                 continue;
1225             }
1226             auto j = cur->begin();
1227             for(; j != cur->end() && j->match(i->getName()); ++j)
1228                 ;   // Empty
1229 
1230             if(j != cur->end())
1231                 continue;
1232 
1233             // Check file type...
1234             if(checkType(i->getName(), aFileType)) {
1235                 SearchResultPtr sr(new SearchResult(SearchResult::TYPE_FILE, i->getSize(), getFullName() + i->getName(), i->getTTH()));
1236                 aResults.push_back(sr);
1237                 ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
1238                 if(aResults.size() >= maxResults) {
1239                     break;
1240                 }
1241             }
1242         }
1243     }
1244 
1245     for(auto l = directories.begin(); (l != directories.end()) && (aResults.size() < maxResults); ++l) {
1246         l->second->search(aResults, *cur, aSearchType, aSize, aFileType, aClient, maxResults);
1247     }
1248 }
1249 
1250 void ShareManager::search(SearchResultList& results, const string& aString, int aSearchType, int64_t aSize, int aFileType, Client* aClient, StringList::size_type maxResults) noexcept {
1251     Lock l(cs);
1252     if(aFileType == SearchManager::TYPE_TTH) {
1253         if(aString.compare(0, 4, "TTH:") == 0) {
1254             TTHValue tth(aString.substr(4));
1255             auto i = tthIndex.find(tth);
1256             if(i != tthIndex.end()) {
1257                 SearchResultPtr sr(new SearchResult(SearchResult::TYPE_FILE, i->second->getSize(),
1258                     i->second->getParent()->getFullName() + i->second->getName(), i->second->getTTH()));
1259 
1260                 results.push_back(sr);
1261                 ShareManager::getInstance()->addHits(1);
1262             }
1263         }
1264         return;
1265     }
1266     StringTokenizer<string> t(Text::toLower(aString), '$');
1267     StringList& sl = t.getTokens();
1268     if(!bloom.match(sl))
1269         return;
1270 
1271     StringSearch::List ssl;
1272     for(auto i = sl.begin(); i != sl.end(); ++i) {
1273         if(!i->empty()) {
1274             ssl.push_back(StringSearch(*i));
1275         }
1276     }
1277     if(ssl.empty())
1278         return;
1279 
1280     for(auto j = directories.begin(); (j != directories.end()) && (results.size() < maxResults); ++j) {
1281         (*j)->search(results, ssl, aSearchType, aSize, aFileType, aClient, maxResults);
1282     }
1283 }
1284 
1285 namespace {
1286     inline uint16_t toCode(char a, char b) { return (uint16_t)a | ((uint16_t)b)<<8; }
1287 }
1288 
1289 ShareManager::AdcSearch::AdcSearch(const StringList& params) : include(&includeX), gt(0),
1290     lt(numeric_limits<int64_t>::max()), hasRoot(false), isDirectory(false)
1291 {
1292     for(auto i = params.begin(); i != params.end(); ++i) {
1293         const string& p = *i;
1294         if(p.length() <= 2)
1295             continue;
1296 
1297         uint16_t cmd = toCode(p[0], p[1]);
1298         if(toCode('T', 'R') == cmd) {
1299             hasRoot = true;
1300             root = TTHValue(p.substr(2));
1301             return;
1302         } else if(toCode('A', 'N') == cmd) {
1303             includeX.push_back(StringSearch(p.substr(2)));
1304         } else if(toCode('N', 'O') == cmd) {
1305             exclude.push_back(StringSearch(p.substr(2)));
1306         } else if(toCode('E', 'X') == cmd) {
1307             ext.push_back(p.substr(2));
1308         } else if(toCode('G', 'R') == cmd) {
1309             auto exts = AdcHub::parseSearchExts(Util::toInt(p.substr(2)));
1310             ext.insert(ext.begin(), exts.begin(), exts.end());
1311         } else if(toCode('R', 'X') == cmd) {
1312             noExt.push_back(p.substr(2));
1313         } else if(toCode('G', 'E') == cmd) {
1314             gt = Util::toInt64(p.substr(2));
1315         } else if(toCode('L', 'E') == cmd) {
1316             lt = Util::toInt64(p.substr(2));
1317         } else if(toCode('E', 'Q') == cmd) {
1318             lt = gt = Util::toInt64(p.substr(2));
1319         } else if(toCode('T', 'Y') == cmd) {
1320             isDirectory = (p[2] == '2');
1321         }
1322     }
1323 }
1324 
1325 bool ShareManager::AdcSearch::isExcluded(const string& str) {
1326     for(auto i = exclude.begin(); i != exclude.end(); ++i) {
1327         if(i->match(str))
1328             return true;
1329     }
1330     return false;
1331 }
1332 
1333 bool ShareManager::AdcSearch::hasExt(const string& name) {
1334     if(ext.empty())
1335         return true;
1336     if(!noExt.empty()) {
1337         ext = StringList(ext.begin(), set_difference(ext.begin(), ext.end(), noExt.begin(), noExt.end(), ext.begin()));
1338         noExt.clear();
1339     }
1340     for(auto i = ext.cbegin(), iend = ext.cend(); i != iend; ++i) {
1341         if(name.length() >= i->length() && Util::stricmp(name.c_str() + name.length() - i->length(), i->c_str()) == 0)
1342             return true;
1343     }
1344     return false;
1345 }
1346 
1347 void ShareManager::Directory::search(SearchResultList& aResults, AdcSearch& aStrings, StringList::size_type maxResults) const noexcept {
1348     StringSearch::List* cur = aStrings.include;
1349     StringSearch::List* old = aStrings.include;
1350 
1351     unique_ptr<StringSearch::List> newStr;
1352 
1353     // Find any matches in the directory name
1354     for(auto k = cur->begin(); k != cur->end(); ++k) {
1355         if(k->match(name) && !aStrings.isExcluded(name)) {
1356             if(!newStr.get()) {
1357                 newStr = unique_ptr<StringSearch::List>(new StringSearch::List(*cur));
1358             }
1359             newStr->erase(remove(newStr->begin(), newStr->end(), *k), newStr->end());
1360         }
1361     }
1362 
1363     if(newStr.get() != 0) {
1364         cur = newStr.get();
1365     }
1366 
1367     bool sizeOk = (aStrings.gt == 0);
1368     if( cur->empty() && aStrings.ext.empty() && sizeOk ) {
1369         // We satisfied all the search words! Add the directory...
1370         SearchResultPtr sr(new SearchResult(SearchResult::TYPE_DIRECTORY, getSize(), getFullName(), TTHValue()));
1371         aResults.push_back(sr);
1372         ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
1373     }
1374 
1375     if(!aStrings.isDirectory) {
1376         for(auto i = files.begin(); i != files.end(); ++i) {
1377 
1378             if(!(i->getSize() >= aStrings.gt)) {
1379                 continue;
1380             } else if(!(i->getSize() <= aStrings.lt)) {
1381                 continue;
1382             }
1383 
1384             if(aStrings.isExcluded(i->getName()))
1385                 continue;
1386 
1387             auto j = cur->begin();
1388             for(; j != cur->end() && j->match(i->getName()); ++j)
1389                 ;   // Empty
1390 
1391             if(j != cur->end())
1392                 continue;
1393 
1394             // Check file type...
1395             if(aStrings.hasExt(i->getName())) {
1396 
1397                 SearchResultPtr sr(new SearchResult(SearchResult::TYPE_FILE,
1398                     i->getSize(), getFullName() + i->getName(), i->getTTH()));
1399                 aResults.push_back(sr);
1400                 ShareManager::getInstance()->addHits(1);
1401                 if(aResults.size() >= maxResults) {
1402                     return;
1403                 }
1404             }
1405         }
1406     }
1407 
1408     for(auto l = directories.begin(); (l != directories.end()) && (aResults.size() < maxResults); ++l) {
1409         l->second->search(aResults, aStrings, maxResults);
1410     }
1411     aStrings.include = old;
1412 }
1413 
1414 void ShareManager::search(SearchResultList& results, const StringList& params, StringList::size_type maxResults) noexcept {
1415     AdcSearch srch(params);
1416 
1417     Lock l(cs);
1418 
1419     if(srch.hasRoot) {
1420         auto i = tthIndex.find(srch.root);
1421         if(i != tthIndex.end()) {
1422             SearchResultPtr sr(new SearchResult(SearchResult::TYPE_FILE,
1423                 i->second->getSize(), i->second->getParent()->getFullName() + i->second->getName(),
1424                 i->second->getTTH()));
1425             results.push_back(sr);
1426             addHits(1);
1427         }
1428         return;
1429     }
1430 
1431     for(auto i = srch.includeX.begin(); i != srch.includeX.end(); ++i) {
1432         if(!bloom.match(i->getPattern()))
1433             return;
1434     }
1435 
1436     for(auto j = directories.begin(); (j != directories.end()) && (results.size() < maxResults); ++j) {
1437         (*j)->search(results, srch, maxResults);
1438     }
1439 }
1440 
1441 ShareManager::Directory::Ptr ShareManager::getDirectory(const string& fname) {
1442     for(auto mi = shares.begin(); mi != shares.end(); ++mi) {
1443         if(Util::strnicmp(fname, mi->first, mi->first.length()) == 0) {
1444             Directory::Ptr d;
1445             for(auto i = directories.begin(); i != directories.end(); ++i) {
1446                 if(Util::stricmp((*i)->getName(), mi->second) == 0) {
1447                     d = *i;
1448                 }
1449             }
1450 
1451             if(!d) {
1452                 return Directory::Ptr();
1453             }
1454 
1455             string::size_type i;
1456             string::size_type j = mi->first.length();
1457             while( (i = fname.find(PATH_SEPARATOR, j)) != string::npos) {
1458                 auto dmi = d->directories.find(fname.substr(j, i-j));
1459                 j = i + 1;
1460                 if(dmi == d->directories.end())
1461                     return Directory::Ptr();
1462                 d = dmi->second;
1463             }
1464             return d;
1465         }
1466     }
1467     return Directory::Ptr();
1468 }
1469 
1470 void ShareManager::on(QueueManagerListener::FileMoved, const string& n) noexcept {
1471     if(BOOLSETTING(ADD_FINISHED_INSTANTLY)) {
1472         // Check if finished download is supposed to be shared
1473         Lock l(cs);
1474         for(auto i = shares.begin(); i != shares.end(); ++i) {
1475             if(Util::strnicmp(i->first, n, i->first.size()) == 0 && n[i->first.size() - 1] == PATH_SEPARATOR) {
1476                 try {
1477                     // Schedule for hashing, it'll be added automatically later on...
1478                     HashManager::getInstance()->checkTTH(n, File::getSize(n), 0);
1479                 } catch(const Exception&) {
1480                     // Not a vital feature...
1481                 }
1482                 break;
1483             }
1484         }
1485     }
1486 }
1487 
1488 void ShareManager::on(HashManagerListener::TTHDone, const string& fname, const TTHValue& root) noexcept {
1489     Lock l(cs);
1490     Directory::Ptr d = getDirectory(fname);
1491     if(d) {
1492         auto i = d->findFile(Util::getFileName(fname));
1493         if(i != d->files.end()) {
1494             if(root != i->getTTH())
1495                 tthIndex.erase(i->getTTH());
1496             // Get rid of false constness...
1497             auto f = const_cast<Directory::File*>(&(*i));
1498             f->setTTH(root);
1499             tthIndex.insert(make_pair(f->getTTH(), i));
1500         } else {
1501             string name = Util::getFileName(fname);
1502             int64_t size = File::getSize(fname);
1503             auto it = d->files.insert(Directory::File(name, size, d, root)).first;
1504             updateIndices(*d, it);
1505         }
1506         setDirty();
1507         forceXmlRefresh = true;
1508     }
1509 }
1510 
1511 void ShareManager::on(TimerManagerListener::Minute, uint64_t tick) noexcept {
1512     if (SETTING(AUTO_REFRESH_TIME) > 0) {
1513         if (lastFullUpdate + SETTING(AUTO_REFRESH_TIME) * 60 * 1000 < tick) {
1514             refresh(true, true);
1515         }
1516     }
1517 }
1518 
1519 } // namespace dcpp
1520