📄 sharemanager.cpp
字号:
/*
* Copyright (C) 2001-2006 Jacek Sieka, arnetheduck on gmail point com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "stdinc.h"
#include "DCPlusPlus.h"
#include "ShareManager.h"
#include "ResourceManager.h"
#include "CryptoManager.h"
#include "ClientManager.h"
#include "LogManager.h"
#include "HashManager.h"
#include "SimpleXML.h"
#include "StringTokenizer.h"
#include "File.h"
#include "FilteredFile.h"
#include "BZUtils.h"
#ifndef _WIN32
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fnmatch.h>
#endif
#include <limits>
ShareManager::ShareManager() : hits(0), listLen(0), bzXmlListLen(0),
xmlDirty(true), refreshDirs(false), update(false), initial(true), listN(0), refreshing(0), lFile(NULL),
xFile(NULL), lastXmlUpdate(0), lastFullUpdate(GET_TICK()), bloom(1<<20)
{
SettingsManager::getInstance()->addListener(this);
TimerManager::getInstance()->addListener(this);
DownloadManager::getInstance()->addListener(this);
HashManager::getInstance()->addListener(this);
}
ShareManager::~ShareManager() {
SettingsManager::getInstance()->removeListener(this);
TimerManager::getInstance()->removeListener(this);
DownloadManager::getInstance()->removeListener(this);
HashManager::getInstance()->removeListener(this);
join();
delete lFile;
lFile = NULL;
delete xFile;
xFile = NULL;
#ifdef _WIN32
WIN32_FIND_DATA data;
HANDLE hFind;
hFind = FindFirstFile(Text::toT(Util::getConfigPath() + "files*.xml.bz2").c_str(), &data);
if(hFind != INVALID_HANDLE_VALUE) {
do {
if(_tcslen(data.cFileName) > 13) // length of "files.xml.bz2"
File::deleteFile(Util::getAppPath() + Text::fromT(data.cFileName));
} while(FindNextFile(hFind, &data));
FindClose(hFind);
}
#else
DIR* dir = opendir(Util::getAppName().c_str());
if (dir) {
while (struct dirent* ent = readdir(dir)) {
if (fnmatch("files*.xml.bz2", ent->d_name, 0) == 0) {
File::deleteFile(Util::getConfigPath() + ent->d_name);
}
}
closedir(dir);
}
#endif
for(Directory::MapIter j = directories.begin(); j != directories.end(); ++j) {
delete j->second;
}
}
ShareManager::Directory::~Directory() {
for(MapIter i = directories.begin(); i != directories.end(); ++i)
delete i->second;
for(File::Iter i = files.begin(); i != files.end(); ++i) {
ShareManager::getInstance()->removeTTH(i->getTTH(), *i);
}
}
string ShareManager::translateTTH(const string& TTH) throw(ShareException) {
TTHValue v(TTH);
HashFileIter i = tthIndex.find(&v);
if(i != tthIndex.end()) {
return i->second->getADCPath();
} else {
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
}
}
string ShareManager::translateFileName(const string& aFile) throw(ShareException) {
RLock<> l(cs);
if(aFile == "MyList.DcLst") {
throw ShareException("NMDC-style lists no longer supported, please upgrade your client");
} else if(aFile == "files.xml.bz2" || aFile == "files.xml") {
generateXmlList();
return getBZXmlFile();
} else {
if(aFile.length() < 3)
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
string file;
// Check for tth root identifier
if(aFile.compare(0, 4, "TTH/") == 0) {
file = translateTTH(aFile.substr(4));
} else if(aFile[0] != '/') {
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
} else {
file = aFile;
}
string::size_type i = file.find('/', 1);
if(i == string::npos)
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
RLock<> l(cs);
StringPairIter j = lookupVirtual(file.substr(1, i-1));
if(j == virtualMap.end()) {
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
}
file = file.substr(i + 1);
Directory::File::Iter it;
if(!checkFile(j->second, file, it)) {
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
}
#ifdef _WIN32
for(i = 0; i < file.length(); ++i) {
if(file[i] == '/')
file[i] = '\\';
}
#endif
return j->second + file;
}
}
AdcCommand ShareManager::getFileInfo(const string& aFile) throw(ShareException) {
if(aFile == "files.xml") {
generateXmlList();
/** todo fix size... */
AdcCommand cmd(AdcCommand::CMD_RES);
cmd.addParam("FN", "files.xml");
cmd.addParam("TR", xmlRoot.toBase32());
return cmd;
} else if(aFile == "files.xml.bz2") {
generateXmlList();
AdcCommand cmd(AdcCommand::CMD_RES);
cmd.addParam("FN", "files.xml.bz2");
cmd.addParam("SI", Util::toString(File::getSize(getBZXmlFile())));
cmd.addParam("TR", xmlbzRoot.toBase32());
return cmd;
}
if(aFile.compare(0, 4, "TTH/") != 0)
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
RLock<> l(cs);
TTHValue val(aFile.substr(4));
HashFileIter i = tthIndex.find(&val);
if(i == tthIndex.end()) {
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
}
Directory::File::Iter f = i->second;
AdcCommand cmd(AdcCommand::CMD_RES);
cmd.addParam("FN", f->getADCPath());
cmd.addParam("SI", Util::toString(f->getSize()));
cmd.addParam("TR", f->getTTH().toBase32());
return cmd;
}
StringPairIter ShareManager::findVirtual(const string& name) {
for(StringPairIter i = virtualMap.begin(); i != virtualMap. end(); ++i) {
if(Util::stricmp(name, i->second) == 0)
return i;
}
return virtualMap.end();
}
StringPairIter ShareManager::lookupVirtual(const string& name) {
for(StringPairIter i = virtualMap.begin(); i != virtualMap. end(); ++i) {
if(Util::stricmp(name, i->first) == 0)
return i;
}
return virtualMap.end();
}
bool ShareManager::checkFile(const string& dir, const string& aFile, Directory::File::Iter& it) {
Directory::MapIter mi = directories.find(dir);
if(mi == directories.end())
return false;
Directory* d = mi->second;
string::size_type i;
string::size_type j = 0;
while( (i = aFile.find('/', j)) != string::npos) {
mi = d->directories.find(aFile.substr(j, i-j));
j = i + 1;
if(mi == d->directories.end())
return false;
d = mi->second;
}
it = find_if(d->files.begin(), d->files.end(), Directory::File::StringComp(aFile.substr(j)));
if(it == d->files.end())
return false;
return true;
}
string ShareManager::validateVirtual(const string& aVirt) {
string tmp = aVirt;
string::size_type idx;
while( (idx = tmp.find_first_of("$|:\\/")) != string::npos) {
tmp[idx] = '_';
}
return tmp;
}
void ShareManager::load(SimpleXML* aXml) {
WLock<> l(cs);
if(aXml->findChild("Share")) {
aXml->stepIn();
while(aXml->findChild("Directory")) {
const string& virt = aXml->getChildAttrib("Virtual");
string d(aXml->getChildData()), newVirt;
if(d[d.length() - 1] != PATH_SEPARATOR)
d += PATH_SEPARATOR;
if(!virt.empty()) {
newVirt = virt;
if(newVirt[newVirt.length() - 1] == PATH_SEPARATOR) {
newVirt.erase(newVirt.length() -1, 1);
}
} else {
newVirt = Util::getLastDir(d);
}
// get rid of bad characters in virtual names
newVirt = validateVirtual(newVirt);
// add only unique directories
if(lookupVirtual(newVirt) == virtualMap.end()) {
Directory* dp = new Directory(newVirt);
directories[d] = dp;
virtualMap.push_back(make_pair(newVirt, d));
}
}
aXml->stepOut();
}
}
struct ShareLoader : public SimpleXMLReader::CallBack {
ShareLoader(ShareManager::Directory::Map& aDirs, StringPairList& aVirts) : dirs(aDirs), virts(aVirts), cur(NULL), depth(0) { }
virtual void startTag(const string& name, StringPairList& attribs, bool simple) {
if(name == "Directory") {
if(depth == 0) {
const string& name = getAttrib(attribs, "Name", 0);
for(StringPairIter i = virts.begin(); i != virts.end(); ++i) {
if(i->first == name) {
cur = dirs[i->second];
break;
}
}
} else if(cur != NULL) {
cur = new ShareManager::Directory(getAttrib(attribs, "Name", 0), cur);
cur->addType(SearchManager::TYPE_DIRECTORY); // needed since we match our own name in directory searches
cur->getParent()->directories[cur->getName()] = cur;
}
if(simple)
cur = cur->getParent();
else
depth++;
} else if(cur != NULL && name == "File") {
const string& fname = getAttrib(attribs, "Name", 0);
const string& size = getAttrib(attribs, "Size", 1);
const string& root = getAttrib(attribs, "TTH", 2);
if(fname.empty() || size.empty() || (root.size() != 39)) {
dcdebug("Invalid file found: %s\n", fname.c_str());
return;
}
cur->files.insert(ShareManager::Directory::File(fname, Util::toInt64(size), cur, TTHValue(root)));
}
}
virtual void endTag(const string& name, const string&) {
if(name == "Directory") {
depth--;
if(cur) {
cur = cur->getParent();
}
}
}
private:
ShareManager::Directory::Map& dirs;
StringPairList& virts;
ShareManager::Directory* cur;
size_t depth;
};
bool ShareManager::loadCache() {
try {
ShareLoader loader(directories, virtualMap);
string txt;
::File ff(Util::getConfigPath() + "files.xml.bz2", ::File::READ, ::File::OPEN);
FilteredInputStream<UnBZFilter, false> f(&ff);
const size_t BUF_SIZE = 64*1024;
char buf[BUF_SIZE];
size_t len;
for(;;) {
size_t n = BUF_SIZE;
len = f.read(buf, n);
txt.append(buf, len);
if(len < BUF_SIZE)
break;
}
SimpleXMLReader(&loader).fromXML(txt);
for(Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
addTree(i->second);
}
return true;
} catch(const Exception& e) {
dcdebug("%s\n", e.getError().c_str());
}
return false;
}
void ShareManager::save(SimpleXML* aXml) {
RLock<> l(cs);
aXml->addTag("Share");
aXml->stepIn();
for(StringPairIter i = virtualMap.begin(); i != virtualMap.end(); ++i) {
aXml->addTag("Directory", i->second);
aXml->addChildAttrib("Virtual", i->first);
}
aXml->stepOut();
}
void ShareManager::addDirectory(const string& aDirectory, const string& aName) throw(ShareException) {
if(aDirectory.empty() || aName.empty()) {
throw ShareException(STRING(NO_DIRECTORY_SPECIFIED));
}
if(Util::stricmp(SETTING(TEMP_DOWNLOAD_DIRECTORY), aDirectory) == 0) {
throw ShareException(STRING(DONT_SHARE_TEMP_DIRECTORY));
}
string d(aDirectory);
if(d[d.length() - 1] != PATH_SEPARATOR)
d += PATH_SEPARATOR;
string vName = validateVirtual(aName);
Directory* dp = NULL;
{
RLock<> l(cs);
for(Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
if(Util::strnicmp(d, i->first, i->first.length()) == 0) {
// Trying to share an already shared directory
throw ShareException(STRING(DIRECTORY_ALREADY_SHARED));
} else if(Util::strnicmp(d, i->first, d.length()) == 0) {
// Trying to share a parent directory
throw ShareException(STRING(REMOVE_ALL_SUBDIRECTORIES));
}
}
if(lookupVirtual(vName) != virtualMap.end()) {
throw ShareException(STRING(VIRTUAL_NAME_EXISTS));
}
}
dp = buildTree(d, NULL);
dp->setName(vName);
{
WLock<> l(cs);
addTree(dp);
directories[d] = dp;
virtualMap.push_back(make_pair(vName, d));
setDirty();
}
}
void ShareManager::removeDirectory(const string& aDirectory, bool duringRefresh) {
WLock<> l(cs);
string d(aDirectory);
if(d[d.length() - 1] != PATH_SEPARATOR)
d += PATH_SEPARATOR;
Directory::MapIter i = directories.find(d);
if(i != directories.end()) {
delete i->second;
directories.erase(i);
}
for(StringPairIter j = virtualMap.begin(); j != virtualMap.end(); ++j) {
if(Util::stricmp(j->second.c_str(), d.c_str()) == 0) {
virtualMap.erase(j);
break;
}
}
if(!duringRefresh)
HashManager::getInstance()->stopHashing(d);
setDirty();
}
void ShareManager::renameDirectory(const string& oName, const string& nName) throw(ShareException) {
StringPairIter i;
WLock<> l(cs);
//Find the virtual name
i = lookupVirtual(oName);
if (lookupVirtual(nName) != virtualMap.end()) {
throw ShareException(STRING(VIRTUAL_NAME_EXISTS));
} else {
// Valid newName, lets rename
i->first = nName;
//rename the directory, so it will be updated once
//a new list is generated.
if( directories.find(i->second) != directories.end() ) {
directories.find(i->second)->second->setName(nName);
}
}
//Do not call setDirty here since there might be more
//dirs that should be renamed, this is to avoid creating
//a new list during renaming.
}
int64_t ShareManager::getShareSize(const string& aDir) throw() {
RLock<> l(cs);
dcassert(aDir.size()>0);
Directory::MapIter i = directories.find(aDir);
if(i != directories.end()) {
return i->second->getSize();
}
return -1;
}
int64_t ShareManager::getShareSize() throw() {
RLock<> l(cs);
int64_t tmp = 0;
for(Directory::MapIter i = directories.begin(); i != directories.end(); ++i) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -