📄 sharemanager.cpp
字号:
return n;
}
#define LITERAL(n) n, sizeof(n)-1
void ShareManager::Directory::toNmdc(string& nmdc, string& indent, string& tmp2) {
tmp2.clear();
nmdc.append(indent);
nmdc.append(Text::utf8ToAcp(name, tmp2));
nmdc.append(LITERAL("\r\n"));
indent += '\t';
for(MapIter i = directories.begin(); i != directories.end(); ++i) {
i->second->toNmdc(nmdc, indent, tmp2);
}
Directory::File::Iter j = files.begin();
for(Directory::File::Iter i = files.begin(); i != files.end(); ++i) {
const Directory::File& f = *i;
nmdc.append(indent);
tmp2.clear();
nmdc.append(Text::utf8ToAcp(f.getName(), tmp2));
nmdc.append(LITERAL("|"));
nmdc.append(Util::toString(f.getSize()));
nmdc.append(LITERAL("\r\n"));
}
indent.erase(indent.length()-1);
}
void ShareManager::Directory::toXml(OutputStream& xmlFile, string& indent, string& tmp2, bool fullList) {
xmlFile.write(indent);
xmlFile.write(LITERAL("<Directory Name=\""));
xmlFile.write(escaper(name, tmp2));
if(fullList) {
xmlFile.write(LITERAL("\">\r\n"));
indent += '\t';
for(MapIter i = directories.begin(); i != directories.end(); ++i) {
i->second->toXml(xmlFile, indent, tmp2, fullList);
}
filesToXml(xmlFile, indent, tmp2);
indent.erase(indent.length()-1);
xmlFile.write(indent);
xmlFile.write(LITERAL("</Directory>\r\n"));
} else {
if(directories.empty() && files.empty()) {
xmlFile.write(LITERAL("\" />\r\n"));
} else {
xmlFile.write(LITERAL("\" Incomplete=\"1\" />\r\n"));
}
}
}
void ShareManager::Directory::filesToXml(OutputStream& xmlFile, string& indent, string& tmp2) {
for(Directory::File::Iter i = files.begin(); i != files.end(); ++i) {
const Directory::File& f = *i;
xmlFile.write(indent);
xmlFile.write(LITERAL("<File Name=\""));
xmlFile.write(escaper(f.getName(), tmp2));
xmlFile.write(LITERAL("\" Size=\""));
xmlFile.write(Util::toString(f.getSize()));
xmlFile.write(LITERAL("\" TTH=\""));
tmp2.clear();
xmlFile.write(f.getTTH().toBase32(tmp2));
xmlFile.write(LITERAL("\"/>\r\n"));
}
}
// These ones we can look up as ints (4 bytes...)...
static const char* typeAudio[] = { ".mp3", ".mp2", ".mid", ".wav", ".ogg", ".wma" };
static const char* typeCompressed[] = { ".zip", ".ace", ".rar" };
static const char* typeDocument[] = { ".htm", ".doc", ".txt", ".nfo" };
static const char* typeExecutable[] = { ".exe" };
static const char* typePicture[] = { ".jpg", ".gif", ".png", ".eps", ".img", ".pct", ".psp", ".pic", ".tif", ".rle", ".bmp", ".pcx" };
static const char* typeVideo[] = { ".mpg", ".mov", ".asf", ".avi", ".pxp", ".wmv", ".ogm", ".mkv" };
static const string type2Audio[] = { ".au", ".aiff", ".flac" };
static const string type2Picture[] = { ".ai", ".ps", ".pict" };
static const string type2Video[] = { ".rm", ".divx", ".mpeg" };
#define IS_TYPE(x) ( type == (*((u_int32_t*)x)) )
#define IS_TYPE2(x) (Util::stricmp(aString.c_str() + aString.length() - x.length(), x.c_str()) == 0)
static bool checkType(const string& aString, int aType) {
if(aType == SearchManager::TYPE_ANY)
return true;
if(aString.length() < 5)
return false;
const char* c = aString.c_str() + aString.length() - 3;
if(!Text::isAscii(c))
return false;
u_int32_t type = '.' | (Text::asciiToLower(c[0]) << 8) | (Text::asciiToLower(c[1]) << 16) | (((u_int32_t)Text::asciiToLower(c[2])) << 24);
switch(aType) {
case SearchManager::TYPE_AUDIO:
{
for(size_t i = 0; i < (sizeof(typeAudio) / sizeof(typeAudio[0])); i++) {
if(IS_TYPE(typeAudio[i])) {
return true;
}
}
if( IS_TYPE2(type2Audio[0]) || IS_TYPE2(type2Audio[1]) ) {
return true;
}
}
break;
case SearchManager::TYPE_COMPRESSED:
if( IS_TYPE(typeCompressed[0]) || IS_TYPE(typeCompressed[1]) || IS_TYPE(typeCompressed[2]) ) {
return true;
}
break;
case SearchManager::TYPE_DOCUMENT:
if( IS_TYPE(typeDocument[0]) || IS_TYPE(typeDocument[1]) ||
IS_TYPE(typeDocument[2]) || IS_TYPE(typeDocument[3]) ) {
return true;
}
break;
case SearchManager::TYPE_EXECUTABLE:
if(IS_TYPE(typeExecutable[0]) ) {
return true;
}
break;
case SearchManager::TYPE_PICTURE:
{
for(size_t i = 0; i < (sizeof(typePicture) / sizeof(typePicture[0])); i++) {
if(IS_TYPE(typePicture[i])) {
return true;
}
}
if( IS_TYPE2(type2Picture[0]) || IS_TYPE2(type2Picture[1]) || IS_TYPE2(type2Picture[2]) ) {
return true;
}
}
break;
case SearchManager::TYPE_VIDEO:
{
for(size_t i = 0; i < (sizeof(typeVideo) / sizeof(typeVideo[0])); i++) {
if(IS_TYPE(typeVideo[i])) {
return true;
}
}
if( IS_TYPE2(type2Video[0]) || IS_TYPE2(type2Video[1]) || IS_TYPE2(type2Video[2]) ) {
return true;
}
}
break;
default:
dcasserta(0);
break;
}
return false;
}
SearchManager::TypeModes ShareManager::getType(const string& aFileName) {
if(aFileName[aFileName.length() - 1] == PATH_SEPARATOR) {
return SearchManager::TYPE_DIRECTORY;
}
if(checkType(aFileName, SearchManager::TYPE_VIDEO))
return SearchManager::TYPE_VIDEO;
else if(checkType(aFileName, SearchManager::TYPE_AUDIO))
return SearchManager::TYPE_AUDIO;
else if(checkType(aFileName, SearchManager::TYPE_COMPRESSED))
return SearchManager::TYPE_COMPRESSED;
else if(checkType(aFileName, SearchManager::TYPE_DOCUMENT))
return SearchManager::TYPE_DOCUMENT;
else if(checkType(aFileName, SearchManager::TYPE_EXECUTABLE))
return SearchManager::TYPE_EXECUTABLE;
else if(checkType(aFileName, SearchManager::TYPE_PICTURE))
return SearchManager::TYPE_PICTURE;
return SearchManager::TYPE_ANY;
}
/**
* Alright, the main point here is that when searching, a search string is most often found in
* the filename, not directory name, so we want to make that case faster. Also, we want to
* avoid changing StringLists unless we absolutely have to --> this should only be done if a string
* has been matched in the directory name. This new stringlist should also be used in all descendants,
* but not the parents...
*/
void ShareManager::Directory::search(SearchResult::List& aResults, StringSearch::List& aStrings, int aSearchType, int64_t aSize, int aFileType, Client* aClient, StringList::size_type maxResults) throw() {
// Skip everything if there's nothing to find here (doh! =)
if(!hasType(aFileType))
return;
StringSearch::List* cur = &aStrings;
auto_ptr<StringSearch::List> newStr;
// Find any matches in the directory name
for(StringSearch::Iter k = aStrings.begin(); k != aStrings.end(); ++k) {
if(k->match(name)) {
if(!newStr.get()) {
newStr = auto_ptr<StringSearch::List>(new StringSearch::List(aStrings));
}
dcassert(find(newStr->begin(), newStr->end(), *k) != newStr->end());
newStr->erase(find(newStr->begin(), newStr->end(), *k));
}
}
if(newStr.get() != 0) {
cur = newStr.get();
}
bool sizeOk = (aSearchType != SearchManager::SIZE_ATLEAST) || (aSize == 0);
if( (cur->empty()) &&
(((aFileType == SearchManager::TYPE_ANY) && sizeOk) || (aFileType == SearchManager::TYPE_DIRECTORY)) ) {
// We satisfied all the search words! Add the directory...(NMDC searches don't support directory size)
SearchResult* sr = new SearchResult(SearchResult::TYPE_DIRECTORY, 0, getFullName(), NULL);
aResults.push_back(sr);
ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
}
if(aFileType != SearchManager::TYPE_DIRECTORY) {
for(File::Iter i = files.begin(); i != files.end(); ++i) {
if(aSearchType == SearchManager::SIZE_ATLEAST && aSize > i->getSize()) {
continue;
} else if(aSearchType == SearchManager::SIZE_ATMOST && aSize < i->getSize()) {
continue;
}
StringSearch::Iter j = cur->begin();
for(; j != cur->end() && j->match(i->getName()); ++j)
; // Empty
if(j != cur->end())
continue;
// Check file type...
if(checkType(i->getName(), aFileType)) {
SearchResult* sr = new SearchResult(SearchResult::TYPE_FILE, i->getSize(), getFullName() + i->getName(), &i->getTTH());
aResults.push_back(sr);
ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
if(aResults.size() >= maxResults) {
break;
}
}
}
}
for(Directory::MapIter l = directories.begin(); (l != directories.end()) && (aResults.size() < maxResults); ++l) {
l->second->search(aResults, *cur, aSearchType, aSize, aFileType, aClient, maxResults);
}
}
void ShareManager::search(SearchResult::List& results, const string& aString, int aSearchType, int64_t aSize, int aFileType, Client* aClient, StringList::size_type maxResults) {
RLock<> l(cs);
if(aFileType == SearchManager::TYPE_TTH) {
if(aString.compare(0, 4, "TTH:") == 0) {
TTHValue tth(aString.substr(4));
HashFileIter i = tthIndex.find(&tth);
if(i != tthIndex.end()) {
SearchResult* sr = new SearchResult(SearchResult::TYPE_FILE, i->second->getSize(),
i->second->getParent()->getFullName() + i->second->getName(), &i->second->getTTH());
results.push_back(sr);
ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
}
}
return;
}
StringTokenizer<string> t(Text::toLower(aString), '$');
StringList& sl = t.getTokens();
if(!bloom.match(sl))
return;
StringSearch::List ssl;
for(StringList::iterator i = sl.begin(); i != sl.end(); ++i) {
if(!i->empty()) {
ssl.push_back(StringSearch(*i));
}
}
if(ssl.empty())
return;
for(Directory::MapIter j = directories.begin(); (j != directories.end()) && (results.size() < maxResults); ++j) {
j->second->search(results, ssl, aSearchType, aSize, aFileType, aClient, maxResults);
}
}
namespace {
inline u_int16_t toCode(char a, char b) { return (u_int16_t)a | ((u_int16_t)b)<<8; }
}
ShareManager::AdcSearch::AdcSearch(const StringList& params) : include(&includeX), gt(0),
lt(numeric_limits<int64_t>::max()), hasRoot(false), isDirectory(false)
{
for(StringIterC i = params.begin(); i != params.end(); ++i) {
const string& p = *i;
if(p.length() <= 2)
continue;
u_int16_t cmd = toCode(p[0], p[1]);
if(toCode('T', 'R') == cmd) {
hasRoot = true;
root = TTHValue(p.substr(2));
return;
} else if(toCode('A', 'N') == cmd) {
includeX.push_back(StringSearch(p.substr(2)));
} else if(toCode('N', 'O') == cmd) {
exclude.push_back(StringSearch(p.substr(2)));
} else if(toCode('E', 'X') == cmd) {
ext.push_back(p.substr(2));
} else if(toCode('G', 'E') == cmd) {
gt = Util::toInt64(p.substr(2));
} else if(toCode('L', 'E') == cmd) {
lt = Util::toInt64(p.substr(2));
} else if(toCode('E', 'Q') == cmd) {
lt = gt = Util::toInt64(p.substr(2));
} else if(toCode('T', 'Y') == cmd) {
isDirectory = (p[2] == '2');
}
}
}
void ShareManager::Directory::search(SearchResult::List& aResults, AdcSearch& aStrings, StringList::size_type maxResults) throw() {
StringSearch::List* cur = aStrings.include;
StringSearch::List* old = aStrings.include;
auto_ptr<StringSearch::List> newStr;
// Find any matches in the directory name
for(StringSearch::Iter k = cur->begin(); k != cur->end(); ++k) {
if(k->match(name) && !aStrings.isExcluded(name)) {
if(!newStr.get()) {
newStr = auto_ptr<StringSearch::List>(new StringSearch::List(*cur));
}
dcassert(find(newStr->begin(), newStr->end(), *k) != newStr->end());
newStr->erase(find(newStr->begin(), newStr->end(), *k));
}
}
if(newStr.get() != 0) {
cur = newStr.get();
}
bool sizeOk = (aStrings.gt == 0);
if( cur->empty() && aStrings.ext.empty() && sizeOk ) {
// We satisfied all the search words! Add the directory...
SearchResult* sr = new SearchResult(SearchResult::TYPE_DIRECTORY, getSize(), getFullName(), NULL);
aResults.push_back(sr);
ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
}
if(!aStrings.isDirectory) {
for(File::Iter i = files.begin(); i != files.end(); ++i) {
if(!(i->getSize() >= aStrings.gt)) {
continue;
} else if(!(i->getSize() <= aStrings.lt)) {
continue;
}
if(aStrings.isExcluded(i->getName()))
continue;
StringSearch::Iter j = cur->begin();
for(; j != cur->end() && j->match(i->getName()); ++j)
; // Empty
if(j != cur->end())
continue;
// Check file type...
if(aStrings.hasExt(i->getName())) {
SearchResult* sr = new SearchResult(SearchResult::TYPE_FILE,
i->getSize(), getFullName() + i->getName(), &i->getTTH());
aResults.push_back(sr);
ShareManager::getInstance()->addHits(1);
if(aResults.size() >= maxResults) {
return;
}
}
}
}
for(Directory::MapIter l = directories.begin(); (l != directories.end()) && (aResults.size() < maxResults); ++l) {
l->second->search(aResults, aStrings, maxResults);
}
aStrings.include = old;
}
void ShareManager::search(SearchResult::List& results, const StringList& params, StringList::size_type maxResults) {
AdcSearch srch(params);
RLock<> l(cs);
if(srch.hasRoot) {
HashFileIter i = tthIndex.find(&srch.root);
if(i != tthIndex.end()) {
SearchResult* sr = new SearchResult(SearchResult::TYPE_FILE,
i->second->getSize(), i->second->getParent()->getFullName() + i->second->getName(),
&i->second->getTTH());
results.push_back(sr);
ShareManager::getInstance()->addHits(1);
}
return;
}
for(StringSearch::Iter i = srch.includeX.begin(); i != srch.includeX.end(); ++i) {
if(!bloom.match(i->getPattern()))
return;
}
for(Directory::MapIter j = directories.begin(); (j != directories.end()) && (results.size() < maxResults); ++j) {
j->second->search(results, srch, maxResults);
}
}
ShareManager::Directory* ShareManager::getDirectory(const string& fname) {
for(Directory::MapIter mi = directories.begin(); mi != directories.end(); ++mi) {
if(Util::strnicmp(fname, mi->first, mi->first.length()) == 0) {
Directory* d = mi->second;
string::size_type i;
string::size_type j = mi->first.length();
while( (i = fname.find(PATH_SEPARATOR, j)) != string::npos) {
mi = d->directories.find(fname.substr(j, i-j));
j = i + 1;
if(mi == d->directories.end())
return NULL;
d = mi->second;
}
return d;
}
}
return NULL;
}
void ShareManager::on(DownloadManagerListener::Complete, Download* d) throw() {
if(BOOLSETTING(ADD_FINISHED_INSTANTLY)) {
// Check if finished download is supposed to be shared
WLock<> l(cs);
const string& n = d->getTarget();
for(Directory::MapIter i = directories.begin(); i != directories.end(); i++) {
if(Util::strnicmp(i->first, n, i->first.size()) == 0 && n[i->first.size()] == PATH_SEPARATOR) {
string s = n.substr(i->first.size()+1);
try {
// Schedule for hashing, it'll be added automatically later on...
HashManager::getInstance()->checkTTH(n, d->getSize(), 0);
} catch(const Exception&) {
// Not a vital feature...
}
break;
}
}
}
}
void ShareManager::on(HashManagerListener::TTHDone, const string& fname, const TTHValue& root) throw() {
WLock<> l(cs);
Directory* d = getDirectory(fname);
if(d != NULL) {
Directory::File::Iter i = d->findFile(Util::getFileName(fname));
if(i != d->files.end()) {
if(root != i->getTTH())
removeTTH(i->getTTH(), *i);
// Get rid of false constness...
Directory::File* f = const_cast<Directory::File*>(&(*i));
f->setTTH(root);
tthIndex.insert(make_pair(const_cast<TTHValue*>(&f->getTTH()), i));
} else {
string name = Util::getFileName(fname);
int64_t size = File::getSize(fname);
Directory::File::Iter it = d->files.insert(Directory::File(name, size, d, root)).first;
addFile(d, it);
}
setDirty();
}
}
void ShareManager::on(TimerManagerListener::Minute, u_int32_t tick) throw() {
if(SETTING(AUTO_REFRESH_TIME) > 0) {
if(lastFullUpdate + SETTING(AUTO_REFRESH_TIME) * 60 * 1000 < tick) {
try {
refresh(true, true);
} catch(const ShareException&) {
}
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -