📄 queuemanager.cpp
字号:
return;
}
{
Lock l(cs);
QueueItem* q = fileQueue.find(target);
if(q == NULL) {
q = fileQueue.add(target, aSize, aFlags, p, Util::emptyString, 0, GET_TIME(), root);
fire(QueueManagerListener::Added(), q);
} else {
if(q->getSize() != aSize) {
throw QueueException(STRING(FILE_WITH_DIFFERENT_SIZE));
}
if(root != NULL) {
if(q->getTTH() == NULL) {
q->setTTH(new TTHValue(*root));
} else if(!(*root == *q->getTTH())) {
throw QueueException(STRING(FILE_WITH_DIFFERENT_TTH));
}
}
q->setFlag(aFlags);
// We don't add any more sources to user list downloads...
if(q->isSet(QueueItem::FLAG_USER_LIST))
return;
}
wantConnection = addSource(q, aFile, aUser, addBad ? QueueItem::Source::FLAG_MASK : 0, utf8);
}
if(wantConnection && aUser->isOnline())
ConnectionManager::getInstance()->getDownloadConnection(aUser);
}
void QueueManager::readd(const string& target, User::Ptr& aUser) throw(QueueException) {
bool wantConnection = false;
{
Lock l(cs);
QueueItem* q = fileQueue.find(target);
if(q != NULL && q->isBadSource(aUser)) {
QueueItem::Source* s = *q->getBadSource(aUser);
wantConnection = addSource(q, s->getPath(), aUser, QueueItem::Source::FLAG_MASK, s->isSet(QueueItem::Source::FLAG_UTF8));
}
}
if(wantConnection && aUser->isOnline())
ConnectionManager::getInstance()->getDownloadConnection(aUser);
}
string QueueManager::checkTarget(const string& aTarget, int64_t aSize, int& flags) throw(QueueException, FileException) {
#ifdef _WIN32
if(aTarget.length() > MAX_PATH) {
throw QueueException(STRING(TARGET_FILENAME_TOO_LONG));
}
// Check that target starts with a drive or is an UNC path
if( (aTarget[1] != ':' || aTarget[2] != '\\') &&
(aTarget[0] != '\\' && aTarget[1] != '\\') ) {
throw QueueException(STRING(INVALID_TARGET_FILE));
}
#else
if(aTarget.length() > PATH_MAX) {
throw QueueException(STRING(TARGET_FILENAME_TOO_LONG));
}
// Check that target contains at least one directory...we don't want headless files...
if(aTarget[0] != '/') {
throw QueueException(STRING(INVALID_TARGET_FILE));
}
#endif
string target = Util::validateFileName(aTarget);
// Check that the file doesn't already exist...
int64_t sz = File::getSize(target);
if( (aSize != -1) && (aSize <= sz) ) {
throw FileException(STRING(LARGER_TARGET_FILE_EXISTS));
}
if(sz > 0)
flags |= QueueItem::FLAG_EXISTS;
return target;
}
/** Add a source to an existing queue item */
bool QueueManager::addSource(QueueItem* qi, const string& aFile, User::Ptr aUser, Flags::MaskType addBad, bool utf8) throw(QueueException, FileException) {
QueueItem::Source* s = NULL;
bool wantConnection = (qi->getPriority() != QueueItem::PAUSED);
if(qi->isSource(aUser, aFile)) {
throw QueueException(STRING(DUPLICATE_SOURCE));
}
if(qi->isBadSourceExcept(aUser, aFile, addBad)) {
throw QueueException(STRING(DUPLICATE_SOURCE));
}
s = qi->addSource(aUser, aFile);
if(utf8)
s->setFlag(QueueItem::Source::FLAG_UTF8);
if(aUser->isSet(User::PASSIVE) && (SETTING(CONNECTION_TYPE) != SettingsManager::CONNECTION_ACTIVE) ) {
qi->removeSource(aUser, QueueItem::Source::FLAG_PASSIVE);
wantConnection = false;
} else if(qi->getStatus() != QueueItem::STATUS_RUNNING) {
userQueue.add(qi, aUser);
} else {
wantConnection = false;
}
fire(QueueManagerListener::SourcesUpdated(), qi);
setDirty();
return wantConnection;
}
void QueueManager::addDirectory(const string& aDir, const User::Ptr& aUser, const string& aTarget, QueueItem::Priority p /* = QueueItem::DEFAULT */) throw() {
bool needList;
{
Lock l(cs);
DirectoryItem::DirectoryPair dp = directories.equal_range(aUser);
for(DirectoryItem::DirectoryIter i = dp.first; i != dp.second; ++i) {
if(Util::stricmp(aTarget.c_str(), i->second->getName().c_str()) == 0)
return;
}
// Unique directory, fine...
directories.insert(make_pair(aUser, new DirectoryItem(aUser, aDir, aTarget, p)));
needList = (dp.first == dp.second);
setDirty();
}
if(needList) {
try {
addList(aUser, QueueItem::FLAG_DIRECTORY_DOWNLOAD);
} catch(const Exception&) {
// Ignore, we don't really care...
}
}
}
#define isnum(c) (((c) >= '0') && ((c) <= '9'))
static inline u_int32_t adjustSize(u_int32_t sz, const string& name) {
if(name.length() > 2) {
// filename.r32
u_int8_t c1 = (u_int8_t)name[name.length()-2];
u_int8_t c2 = (u_int8_t)name[name.length()-1];
if(isnum(c1) && isnum(c2)) {
return sz + (c1-'0')*10 + (c2-'0');
} else if(name.length() > 6) {
// filename.part32.rar
c1 = name[name.length() - 6];
c2 = name[name.length() - 5];
if(isnum(c1) && isnum(c2)) {
return sz + (c1-'0')*10 + (c2-'0');
}
}
}
return sz;
}
typedef HASH_MULTIMAP<u_int32_t, QueueItem*> SizeMap;
typedef SizeMap::iterator SizeIter;
typedef pair<SizeIter, SizeIter> SizePair;
// *** WARNING ***
// Lock(cs) makes sure that there's only one thread accessing these,
// I put them here to avoid growing a huge stack...
static DirectoryListing* curDl = NULL;
static SizeMap sizeMap;
int QueueManager::matchFiles(DirectoryListing::Directory* dir) throw() {
int matches = 0;
for(DirectoryListing::Directory::Iter j = dir->directories.begin(); j != dir->directories.end(); ++j) {
if(!(*j)->getAdls())
matches += matchFiles(*j);
}
for(DirectoryListing::File::Iter i = dir->files.begin(); i != dir->files.end(); ++i) {
DirectoryListing::File* df = *i;
SizePair files = sizeMap.equal_range(adjustSize((u_int32_t)df->getSize(), df->getName()));
for(SizeIter j = files.first; j != files.second; ++j) {
QueueItem* qi = j->second;
bool equal = false;
if(qi->getTTH() != NULL && df->getTTH() != NULL) {
equal = (*qi->getTTH() == *df->getTTH()) && (qi->getSize() == df->getSize());
}
if(equal) {
try {
addSource(qi, curDl->getPath(df) + df->getName(), curDl->getUser(), QueueItem::Source::FLAG_FILE_NOT_AVAILABLE, curDl->getUtf8());
matches++;
} catch(const Exception&) {
}
}
}
}
return matches;
}
int QueueManager::matchListing(DirectoryListing* dl) throw() {
int matches = 0;
{
Lock l(cs);
sizeMap.clear();
matches = 0;
curDl = dl;
for(QueueItem::StringIter i = fileQueue.getQueue().begin(); i != fileQueue.getQueue().end(); ++i) {
QueueItem* qi = i->second;
if(qi->getSize() != -1) {
sizeMap.insert(make_pair(adjustSize((u_int32_t)qi->getSize(), qi->getTarget()), qi));
}
}
matches = matchFiles(dl->getRoot());
}
if(matches > 0)
ConnectionManager::getInstance()->getDownloadConnection(dl->getUser());
return matches;
}
void QueueManager::move(const string& aSource, const string& aTarget) throw() {
string target = Util::validateFileName(aTarget);
if(Util::stricmp(aSource, target) == 0)
return;
bool delSource = false;
Lock l(cs);
QueueItem* qs = fileQueue.find(aSource);
if(qs != NULL) {
// Don't move running downloads
if(qs->getStatus() == QueueItem::STATUS_RUNNING) {
return;
}
// Don't move file lists
if(qs->isSet(QueueItem::FLAG_USER_LIST))
return;
// Let's see if the target exists...then things get complicated...
QueueItem* qt = fileQueue.find(target);
if(qt == NULL) {
// Good, update the target and move in the queue...
fileQueue.move(qs, target);
fire(QueueManagerListener::Moved(), qs);
setDirty();
} else {
// Don't move to target of different size
if(qs->getSize() != qt->getSize())
return;
try {
for(QueueItem::Source::Iter i = qs->getSources().begin(); i != qs->getSources().end(); ++i) {
QueueItem::Source* s = *i;
addSource(qt, s->getPath(), s->getUser(), QueueItem::Source::FLAG_MASK, s->isSet(QueueItem::Source::FLAG_UTF8));
}
} catch(const Exception&) {
}
delSource = true;
}
}
if(delSource) {
remove(aSource);
}
}
void QueueManager::getTargetsBySize(StringList& sl, int64_t aSize, const string& suffix) throw() {
Lock l(cs);
QueueItem::List ql;
fileQueue.find(ql, aSize, suffix);
for(QueueItem::Iter i = ql.begin(); i != ql.end(); ++i) {
sl.push_back((*i)->getTarget());
}
}
void QueueManager::getTargetsByRoot(StringList& sl, const TTHValue& tth) {
Lock l(cs);
QueueItem::List ql;
fileQueue.find(ql, tth);
for(QueueItem::Iter i = ql.begin(); i != ql.end(); ++i) {
sl.push_back((*i)->getTarget());
}
}
Download* QueueManager::getDownload(User::Ptr& aUser, bool supportsTrees) throw() {
Lock l(cs);
// First check PFS's...
PfsIter pi = pfsQueue.find(aUser->getCID());
if(pi != pfsQueue.end()) {
Download* d = new Download();
d->setFlag(Download::FLAG_PARTIAL_LIST);
d->setFlag(Download::FLAG_UTF8);
d->setSource(pi->second);
return d;
}
QueueItem* q = userQueue.getNext(aUser);
if(q == NULL)
return NULL;
userQueue.setRunning(q, aUser);
fire(QueueManagerListener::StatusUpdated(), q);
Download* d = new Download(q);
if(d->getSize() != -1 && d->getTTH()) {
if(HashManager::getInstance()->getTree(*d->getTTH(), d->getTigerTree())) {
d->setTreeValid(true);
} else if(supportsTrees && !q->getCurrent()->isSet(QueueItem::Source::FLAG_NO_TREE) && d->getSize() > HashManager::MIN_BLOCK_SIZE) {
// Get the tree unless the file is small (for small files, we'd probably only get the root anyway)
d->setFlag(Download::FLAG_TREE_DOWNLOAD);
d->getTigerTree().setFileSize(d->getSize());
d->setPos(0);
d->setSize(-1);
d->unsetFlag(Download::FLAG_RESUME);
} else {
// Use the root as tree to get some sort of validation at least...
d->getTigerTree() = TigerTree(d->getSize(), d->getSize(), *d->getTTH());
d->setTreeValid(true);
}
}
if(!d->isSet(Download::FLAG_TREE_DOWNLOAD) && BOOLSETTING(ANTI_FRAG) ) {
d->setStartPos(q->getDownloadedBytes());
}
q->setCurrentDownload(d);
return d;
}
void QueueManager::putDownload(Download* aDownload, bool finished) throw() {
User::List getConn;
string fname;
User::Ptr up;
int flag = 0;
{
Lock l(cs);
if(aDownload->isSet(Download::FLAG_PARTIAL_LIST)) {
pair<PfsIter, PfsIter> range = pfsQueue.equal_range(aDownload->getUserConnection()->getUser()->getCID());
PfsIter i = find_if(range.first, range.second, CompareSecond<CID, string>(aDownload->getSource()));
if(i != range.second) {
pfsQueue.erase(i);
fire(QueueManagerListener::PartialList(), aDownload->getUserConnection()->getUser(), aDownload->getPFS());
}
} else {
QueueItem* q = fileQueue.find(aDownload->getTarget());
if(q != NULL) {
if(aDownload->isSet(Download::FLAG_USER_LIST)) {
if(aDownload->getSource() == "files.xml.bz2") {
q->setFlag(QueueItem::FLAG_XML_BZLIST);
} else {
q->unsetFlag(QueueItem::FLAG_XML_BZLIST);
}
}
if(finished) {
dcassert(q->getStatus() == QueueItem::STATUS_RUNNING);
if(aDownload->isSet(Download::FLAG_TREE_DOWNLOAD)) {
// Got a full tree, now add it to the HashManager
dcassert(aDownload->getTreeValid());
HashManager::getInstance()->addTree(aDownload->getTigerTree());
q->setCurrentDownload(NULL);
userQueue.setWaiting(q);
fire(QueueManagerListener::StatusUpdated(), q);
} else {
userQueue.remove(q);
fire(QueueManagerListener::Finished(), q);
fire(QueueManagerListener::Removed(), q);
// Now, let's see if this was a directory download filelist...
if( (q->isSet(QueueItem::FLAG_DIRECTORY_DOWNLOAD) && directories.find(q->getCurrent()->getUser()) != directories.end()) ||
(q->isSet(QueueItem::FLAG_MATCH_QUEUE)) )
{
fname = q->getListName();
up = q->getCurrent()->getUser();
flag = (q->isSet(QueueItem::FLAG_DIRECTORY_DOWNLOAD) ? QueueItem::FLAG_DIRECTORY_DOWNLOAD : 0)
| (q->isSet(QueueItem::FLAG_MATCH_QUEUE) ? QueueItem::FLAG_MATCH_QUEUE : 0);
}
fileQueue.remove(q);
setDirty();
}
} else {
if(!aDownload->isSet(Download::FLAG_TREE_DOWNLOAD)) {
q->setDownloadedBytes(aDownload->getPos());
if(q->getDownloadedBytes() > 0) {
q->setFlag(QueueItem::FLAG_EXISTS);
} else {
q->setTempTarget(Util::emptyString);
}
if(q->isSet(QueueItem::FLAG_USER_LIST)) {
// Blah...no use keeping an unfinished file list...
File::deleteFile(q->getListName());
}
}
if(q->getPriority() != QueueItem::PAUSED) {
for(QueueItem::Source::Iter j = q->getSources().begin(); j != q->getSources().end(); ++j) {
if((*j)->getUser()->isOnline()) {
getConn.push_back((*j)->getUser());
}
}
}
q->setCurrentDownload(NULL);
// This might have been set to wait by removesource already...
if(q->getStatus() == QueueItem::STATUS_RUNNING) {
userQueue.setWaiting(q);
fire(QueueManagerListener::StatusUpdated(), q);
}
}
} else if(!aDownload->isSet(Download::FLAG_TREE_DOWNLOAD)) {
if(!aDownload->getTempTarget().empty() && aDownload->getTempTarget() != aDownload->getTarget()) {
File::deleteFile(aDownload->getTempTarget() + Download::ANTI_FRAG_EXT);
File::deleteFile(aDownload->getTempTarget());
}
}
}
aDownload->setUserConnection(NULL);
delete aDownload;
}
for(User::Iter i = getConn.begin(); i != getConn.end(); ++i) {
ConnectionManager::getInstance()->getDownloadConnection(*i);
}
if(!fname.empty()) {
processList(fname, up, flag);
}
}
void QueueManager::processList(const string& name, User::Ptr& user, int flags) {
DirectoryListing dirList(user);
try {
dirList.loadFile(name);
} catch(const Exception&) {
/** @todo Log this */
return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -