www.pudn.com > DCPlusPlus-src.zip > QueueManager.cpp
/* * Copyright (C) 2001-2004 Jacek Sieka, j_s at telia 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. */ #ifndef _WIN32 #include#include #include #endif #include "stdinc.h" #include "DCPlusPlus.h" #include "QueueManager.h" #include "ConnectionManager.h" #include "SearchManager.h" #include "ClientManager.h" #include "DownloadManager.h" #include "CryptoManager.h" #include "ShareManager.h" #include "LogManager.h" #include "UserConnection.h" #include "SimpleXML.h" #include "StringTokenizer.h" #include "DirectoryListing.h" #ifdef _WIN32 #define FILELISTS_DIR "FileLists\\" #else #define FILELISTS_DIR "filelists/" #endif const string QueueManager::USER_LIST_NAME = "MyList.DcLst"; const string QueueManager::TEMP_EXTENSION = ".dctmp"; namespace { const char* badChars = "$|.[]()-_+"; } string QueueItem::getSearchString() const { return SearchManager::clean(getTargetFileName()); } QueueItem* QueueManager::FileQueue::add(const string& aTarget, int64_t aSize, int aFlags, QueueItem::Priority p, const string& aTempTarget, int64_t aDownloadedBytes, u_int32_t aAdded, const TTHValue* root) throw(QueueException, FileException) { if(p == QueueItem::DEFAULT) p = (aSize <= 64*1024) ? QueueItem::HIGHEST : QueueItem::NORMAL; QueueItem* qi = new QueueItem(aTarget, aSize, p, aFlags, aDownloadedBytes, aAdded, root); if(!qi->isSet(QueueItem::FLAG_USER_LIST)) { if(aTempTarget.empty()) { if(!SETTING(TEMP_DOWNLOAD_DIRECTORY).empty() && (File::getSize(qi->getTarget()) == -1)) { qi->setTempTarget(SETTING(TEMP_DOWNLOAD_DIRECTORY) + getTempName(qi->getTargetFileName(), root)); } } else { qi->setTempTarget(aTempTarget); } } if((qi->getDownloadedBytes() > 0)) qi->setFlag(QueueItem::FLAG_EXISTS); dcassert(find(aTarget) == NULL); add(qi); return qi; } void QueueManager::FileQueue::add(QueueItem* qi) { if(lastInsert == queue.end()) lastInsert = queue.insert(make_pair(const_cast (&qi->getTarget()), qi)).first; else lastInsert = queue.insert(lastInsert, make_pair(const_cast (&qi->getTarget()), qi)); } QueueItem* QueueManager::FileQueue::find(const string& target) { QueueItem::StringIter i = queue.find(const_cast (&target)); return (i == queue.end()) ? NULL : i->second; } void QueueManager::FileQueue::find(QueueItem::List& sl, int64_t aSize, const string& suffix) { for(QueueItem::StringIter i = queue.begin(); i != queue.end(); ++i) { if(i->second->getSize() == aSize) { const string& t = i->second->getTarget(); if(suffix.empty() || (suffix.length() < t.length() && Util::stricmp(suffix.c_str(), t.c_str() + (t.length() - suffix.length())) == 0) ) sl.push_back(i->second); } } } void QueueManager::FileQueue::find(QueueItem::List& ql, const TTHValue* tth) { for(QueueItem::StringIter i = queue.begin(); i != queue.end(); ++i) { QueueItem* qi = i->second; if(qi->getTTH() != NULL && *qi->getTTH() == *tth) { ql.push_back(qi); } } } static QueueItem* findCandidate(QueueItem::StringIter start, QueueItem::StringIter end, StringList& recent) { QueueItem* cand = NULL; for(QueueItem::StringIter i = start; i != end; ++i) { QueueItem* q = i->second; // We prefer to search for things that are not running... if((cand != NULL) && (q->getStatus() == QueueItem::STATUS_RUNNING)) continue; // No user lists if(q->isSet(QueueItem::FLAG_USER_LIST)) continue; // No paused downloads if(q->getPriority() == QueueItem::PAUSED) continue; // No files that already have more than 5 online sources if(q->countOnlineUsers() >= 5) continue; // Did we search for it recently? if(find(recent.begin(), recent.end(), q->getTarget()) != recent.end()) continue; cand = q; if(cand->getStatus() != QueueItem::STATUS_RUNNING) break; } return cand; } QueueItem* QueueManager::FileQueue::findAutoSearch(StringList& recent) { // We pick a start position at random, hoping that we will find something to search for... QueueItem::StringMap::size_type start = (QueueItem::StringMap::size_type)Util::rand((u_int32_t)queue.size()); QueueItem::StringIter i = queue.begin(); advance(i, start); QueueItem* cand = findCandidate(i, queue.end(), recent); if(cand == NULL) { cand = findCandidate(queue.begin(), i, recent); } else if(cand->getStatus() == QueueItem::STATUS_RUNNING) { QueueItem* cand2 = findCandidate(queue.begin(), i, recent); if(cand2 != NULL && (cand2->getStatus() != QueueItem::STATUS_RUNNING)) { cand = cand2; } } return cand; } void QueueManager::FileQueue::move(QueueItem* qi, const string& aTarget) { if(lastInsert != queue.end() && Util::stricmp(*lastInsert->first, qi->getTarget()) == 0) lastInsert = queue.end(); queue.erase(const_cast (&qi->getTarget())); qi->setTarget(aTarget); add(qi); } void QueueManager::UserQueue::add(QueueItem* qi) { for(QueueItem::Source::Iter i = qi->getSources().begin(); i != qi->getSources().end(); ++i) { add(qi, (*i)->getUser()); } } void QueueManager::UserQueue::add(QueueItem* qi, const User::Ptr& aUser) { dcassert(qi->getStatus() == QueueItem::STATUS_WAITING); dcassert(qi->isSource(aUser)); dcassert(qi->getCurrent() == NULL); QueueItem::List& l = userQueue[qi->getPriority()][aUser]; if(qi->isSet(QueueItem::FLAG_EXISTS)) { l.insert(l.begin(), qi); } else { l.push_back(qi); } } QueueItem* QueueManager::UserQueue::getNext(const User::Ptr& aUser, QueueItem::Priority minPrio) { int p = QueueItem::LAST - 1; do { QueueItem::UserListIter i = userQueue[p].find(aUser); if(i != userQueue[p].end()) { dcassert(!i->second.empty()); return i->second.front(); } p--; } while(p >= minPrio); return NULL; } void QueueManager::UserQueue::setRunning(QueueItem* qi, const User::Ptr& aUser) { dcassert(qi->getCurrent() == NULL); dcassert(qi->getStatus() == QueueItem::STATUS_WAITING); // Remove the download from the userQueue... remove(qi); // Set the flag to running... qi->setStatus(QueueItem::STATUS_RUNNING); qi->setCurrent(aUser); // Move the download to the running list... dcassert(running.find(aUser) == running.end()); running[aUser] = qi; } void QueueManager::UserQueue::setWaiting(QueueItem* qi) { dcassert(qi->getCurrent() != NULL); dcassert(qi->getStatus() == QueueItem::STATUS_RUNNING); dcassert(running.find(qi->getCurrent()->getUser()) != running.end()); // Remove the download from running running.erase(qi->getCurrent()->getUser()); // Set flag to waiting qi->setCurrent(NULL); qi->setStatus(QueueItem::STATUS_WAITING); // Add to the userQueue add(qi); } QueueItem* QueueManager::UserQueue::getRunning(const User::Ptr& aUser) { QueueItem::UserIter i = running.find(aUser); return (i == running.end()) ? NULL : i->second; } void QueueManager::UserQueue::remove(QueueItem* qi) { if(qi->getStatus() == QueueItem::STATUS_RUNNING) { dcassert(qi->getCurrent() != NULL); remove(qi, qi->getCurrent()->getUser()); } else { for(QueueItem::Source::Iter i = qi->getSources().begin(); i != qi->getSources().end(); ++i) { remove(qi, (*i)->getUser()); } } } void QueueManager::UserQueue::remove(QueueItem* qi, const User::Ptr& aUser) { if(qi->getStatus() == QueueItem::STATUS_RUNNING) { // Remove from running... dcassert(qi->getCurrent() != NULL); dcassert(running.find(aUser) != running.end()); running.erase(aUser); } else { dcassert(qi->isSource(aUser)); dcassert(qi->getCurrent() == NULL); QueueItem::UserListMap& ulm = userQueue[qi->getPriority()]; QueueItem::UserListIter j = ulm.find(aUser); dcassert(j != ulm.end()); QueueItem::List& l = j->second; dcassert(find(l.begin(), l.end(), qi) != l.end()); l.erase(find(l.begin(), l.end(), qi)); if(l.empty()) { ulm.erase(j); } } } QueueManager::QueueManager() : lastSave(0), queueFile(Util::getAppPath() + "Queue.xml"), dirty(true), nextSearch(0) { TimerManager::getInstance()->addListener(this); SearchManager::getInstance()->addListener(this); ClientManager::getInstance()->addListener(this); File::ensureDirectory(Util::getAppPath() + FILELISTS_DIR); }; QueueManager::~QueueManager() { SearchManager::getInstance()->removeListener(this); TimerManager::getInstance()->removeListener(this); ClientManager::getInstance()->removeListener(this); saveQueue(); if(!BOOLSETTING(KEEP_LISTS)) { string path = Util::getAppPath() + FILELISTS_DIR; #ifdef _WIN32 WIN32_FIND_DATA data; HANDLE hFind; hFind = FindFirstFile(Text::toT(path + "\\*.xml.bz2").c_str(), &data); if(hFind != INVALID_HANDLE_VALUE) { do { File::deleteFile(path + Text::fromT(data.cFileName)); } while(FindNextFile(hFind, &data)); FindClose(hFind); } hFind = FindFirstFile(Text::toT(path + "\\*.DcLst").c_str(), &data); if(hFind != INVALID_HANDLE_VALUE) { do { File::deleteFile(path + Text::fromT(data.cFileName)); } while(FindNextFile(hFind, &data)); FindClose(hFind); } #else DIR* dir = opendir(path.c_str()); if (dir) { while (struct dirent* ent = readdir(dir)) { if (fnmatch("*.xml.bz2", ent->d_name, 0) == 0 || fnmatch("*.DcLst", ent->d_name, 0) == 0) { File::deleteFile(path + ent->d_name); } } closedir(dir); } #endif } }; void QueueManager::on(TimerManagerListener::Minute, u_int32_t aTick) throw() { string fn; string searchString; int64_t sz = 0; bool online = false; bool hasTTH = false; { Lock l(cs); QueueItem::UserMap& um = userQueue.getRunning(); for(QueueItem::UserIter j = um.begin(); j != um.end(); ++j) { QueueItem* q = j->second; dcassert(q->getCurrentDownload() != NULL); q->setDownloadedBytes(q->getCurrentDownload()->getPos()); } if(!um.empty()) setDirty(); if(BOOLSETTING(AUTO_SEARCH) && (aTick >= nextSearch) && (fileQueue.getSize() > 0)) { // We keep 30 recent searches to avoid duplicate searches while((recent.size() > fileQueue.getSize()) || (recent.size() > 30)) { recent.erase(recent.begin()); } QueueItem* qi = fileQueue.findAutoSearch(recent); if(qi != NULL) { if(qi->getTTH()) { hasTTH = true; searchString = "TTH:" + qi->getTTH()->toBase32(); } else { fn = qi->getTargetFileName(); sz = qi->getSize() - 1; searchString = qi->getSearchString(); } online = qi->hasOnlineUsers(); recent.push_back(qi->getTarget()); nextSearch = aTick + (online ? 120000 : 300000); } } } if(hasTTH) { SearchManager::getInstance()->search(searchString, 0, SearchManager::TYPE_HASH, SearchManager::SIZE_DONTCARE); } else if(!fn.empty()) { SearchManager::getInstance()->search(searchString, sz, ShareManager::getInstance()->getType(fn), SearchManager::SIZE_ATLEAST); } } string QueueManager::getTempName(const string& aFileName, const TTHValue* aRoot) { string tmp(aFileName); if(aRoot != NULL) { TTHValue tmpRoot(*aRoot); tmp += "." + tmpRoot.toBase32(); } tmp += TEMP_EXTENSION; return tmp; } void QueueManager::add(const string& aFile, int64_t aSize, User::Ptr aUser, const string& aTarget, const TTHValue* root, int aFlags /* = QueueItem::FLAG_RESUME */, QueueItem::Priority p /* = QueueItem::DEFAULT */, const string& aTempTarget /* = Util::emptyString */, bool addBad /* = true */) throw(QueueException, FileException) { bool wantConnection = true; dcassert((aFile != USER_LIST_NAME) || (aFlags &QueueItem::FLAG_USER_LIST)); // Check that we're not downloading from ourselves... if(aUser->getClientNick() == aUser->getNick()) { throw QueueException(STRING(NO_DOWNLOADS_FROM_SELF)); } bool utf8 = (aFlags & QueueItem::FLAG_SOURCE_UTF8) > 0; aFlags &= ~QueueItem::FLAG_SOURCE_UTF8; string target = checkTarget(aTarget, aSize, aFlags); // Check if it's a zero-byte file, if so, create and return... if(aSize == 0) { if(!BOOLSETTING(SKIP_ZERO_BYTE)) { File f(target, File::WRITE, File::CREATE); } return; } { Lock l(cs); QueueItem* q = fileQueue.find(target); if(q == NULL) { q = fileQueue.add(target, aSize, aFlags, p, aTempTarget, 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 // 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 SizeMap; typedef SizeMap::iterator SizeIter; typedef pair 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; static string utfTmp; static const string& utfEscaper(const string& x) { return curDl->getUtf8() ? x : (utfTmp.clear(), Text::acpToUtf8(x, utfTmp)); } 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()); } else { if(Util::stricmp(utfEscaper(df->getName()), qi->getTargetFileName()) == 0) { dcassert(df->getSize() == qi->getSize()); equal = true; if(df->getTTH() != NULL) { dcassert(qi->getTTH() == NULL); qi->setTTH(new TTHValue(*df->getTTH())); } } } 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); } } Download* QueueManager::getDownload(User::Ptr& aUser) throw() { Lock l(cs); QueueItem* q = userQueue.getNext(aUser); Download *d; if(q == NULL) return NULL; userQueue.setRunning(q, aUser); fire(QueueManagerListener::StatusUpdated(), q); d = new Download(q); if( BOOLSETTING(ANTI_FRAG) ) { d->setStartPos(q->getDownloadedBytes()); } q->setCurrentDownload(d); return d; } void QueueManager::putDownload(Download* aDownload, bool finished /* = false */) throw() { User::List getConn; string fname; User::Ptr up; int flag = 0; { Lock l(cs); 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); 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()); q->setCurrentDownload(NULL); if(q->getDownloadedBytes() > 0) q->setFlag(QueueItem::FLAG_EXISTS); 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()); } } } // This might have been set to wait by removesource already... if(q->getStatus() == QueueItem::STATUS_RUNNING) { userQueue.setWaiting(q); fire(QueueManagerListener::StatusUpdated(), q); } if(q->isSet(QueueItem::FLAG_USER_LIST)) { // Blah...no use keeping an unfinished file list... File::deleteFile(q->getListName()); } } } else { 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()) { DirectoryListing dirList(up); try { dirList.loadFile(fname, false); } catch(const Exception&) { addList(up, flag); return; } if(flag & QueueItem::FLAG_DIRECTORY_DOWNLOAD) { DirectoryItem::List dl; { Lock l(cs); DirectoryItem::DirectoryPair dp = directories.equal_range(up); for(DirectoryItem::DirectoryIter i = dp.first; i != dp.second; ++i) { dl.push_back(i->second); } directories.erase(up); } for(DirectoryItem::Iter i = dl.begin(); i != dl.end(); ++i) { DirectoryItem* di = *i; dirList.download(di->getName(), di->getTarget(), false); delete di; } } if(flag & QueueItem::FLAG_MATCH_QUEUE) { AutoArray tmp(STRING(MATCHED_FILES).size() + 16); sprintf(tmp, CSTRING(MATCHED_FILES), matchListing(&dirList)); LogManager::getInstance()->message(up->getNick() + ": " + string(tmp)); } } } void QueueManager::remove(const string& aTarget) throw() { string x; { Lock l(cs); QueueItem* q = fileQueue.find(aTarget); if(q != NULL) { if(q->isSet(QueueItem::FLAG_DIRECTORY_DOWNLOAD)) { dcassert(q->getSources().size() == 1); DirectoryItem::DirectoryPair dp = directories.equal_range(q->getSources()[0]->getUser()); for(DirectoryItem::DirectoryIter i = dp.first; i != dp.second; ++i) { delete i->second; } directories.erase(q->getSources()[0]->getUser()); } if(q->getStatus() == QueueItem::STATUS_RUNNING) { x = q->getTarget(); } else if(!q->getTempTarget().empty() && q->getTempTarget() != q->getTarget()) { File::deleteFile(q->getTempTarget() + Download::ANTI_FRAG_EXT); File::deleteFile(q->getTempTarget()); } userQueue.remove(q); fire(QueueManagerListener::Removed(), q); fileQueue.remove(q); setDirty(); } } if(!x.empty()) { DownloadManager::getInstance()->abortDownload(x); } } void QueueManager::removeSource(const string& aTarget, User::Ptr& aUser, int reason, bool removeConn /* = true */) throw() { Lock l(cs); QueueItem* q = fileQueue.find(aTarget); string x; if(q != NULL) { dcassert(q->isSource(aUser)); if(q->isSet(QueueItem::FLAG_USER_LIST)) { remove(q->getTarget()); return; } if(reason == QueueItem::Source::FLAG_CRC_WARN) { // Already flagged? QueueItem::Source* s = *q->getSource(aUser); if(s->isSet(QueueItem::Source::FLAG_CRC_WARN)) { reason = QueueItem::Source::FLAG_CRC_FAILED; } else { s->setFlag(reason); return; } } if((q->getStatus() == QueueItem::STATUS_RUNNING) && q->getCurrent()->getUser() == aUser) { if(removeConn) x = q->getTarget(); userQueue.setWaiting(q); userQueue.remove(q, aUser); } else if(q->getStatus() == QueueItem::STATUS_WAITING) { userQueue.remove(q, aUser); } q->removeSource(aUser, reason); fire(QueueManagerListener::SourcesUpdated(), q); setDirty(); } if(!x.empty()) { DownloadManager::getInstance()->abortDownload(x); } } void QueueManager::removeSources(User::Ptr& aUser, int reason) throw() { string x; { Lock l(cs); QueueItem* qi = NULL; while( (qi = userQueue.getNext(aUser, QueueItem::PAUSED)) != NULL) { if(qi->isSet(QueueItem::FLAG_USER_LIST)) { remove(qi->getTarget()); } else { userQueue.remove(qi, aUser); qi->removeSource(aUser, reason); fire(QueueManagerListener::SourcesUpdated(), qi); setDirty(); } } qi = userQueue.getRunning(aUser); if(qi != NULL) { if(qi->isSet(QueueItem::FLAG_USER_LIST)) { remove(qi->getTarget()); } else { userQueue.setWaiting(qi); userQueue.remove(qi, aUser); x = qi->getTarget(); qi->removeSource(aUser, reason); fire(QueueManagerListener::SourcesUpdated(), qi); setDirty(); } } } if(!x.empty()) { DownloadManager::getInstance()->abortDownload(x); } } void QueueManager::setPriority(const string& aTarget, QueueItem::Priority p) throw() { User::List ul; { Lock l(cs); QueueItem* q = fileQueue.find(aTarget); if( (q != NULL) && (q->getPriority() != p) ) { if( q->getStatus() != QueueItem::STATUS_RUNNING ) { if(q->getPriority() == QueueItem::PAUSED) { // Problem, we have to request connections to all these users... q->getOnlineUsers(ul); } userQueue.remove(q); q->setPriority(p); userQueue.add(q); } else { q->setPriority(p); } setDirty(); fire(QueueManagerListener::StatusUpdated(), q); } } for(User::Iter i = ul.begin(); i != ul.end(); ++i) { ConnectionManager::getInstance()->getDownloadConnection(*i); } } void QueueManager::saveQueue() throw() { if(!dirty) return; Lock l(cs); try { #define STRINGLEN(n) n, sizeof(n)-1 #define CHECKESCAPE(n) SimpleXML::escape(n, tmp, true) File ff(getQueueFile() + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE); BufferedOutputStream f(&ff); f.write(SimpleXML::utf8Header); f.write(STRINGLEN(" \r\n")); string tmp; string b32tmp; for(QueueItem::StringIter i = fileQueue.getQueue().begin(); i != fileQueue.getQueue().end(); ++i) { QueueItem* d = i->second; if(!d->isSet(QueueItem::FLAG_USER_LIST)) { f.write(STRINGLEN("\t \r\n"); f.flush(); ff.close(); File::deleteFile(getQueueFile()); File::renameFile(getQueueFile() + ".tmp", getQueueFile()); dirty = false; } catch(const FileException&) { // ... } // Put this here to avoid very many saves tries when disk is full... lastSave = GET_TICK(); } class QueueLoader : public SimpleXMLReader::CallBack { public: QueueLoader() : cur(NULL), inDownloads(false) { }; virtual ~QueueLoader() { } virtual void startTag(const string& name, StringPairList& attribs, bool simple); virtual void endTag(const string& name, const string& data); private: string target; QueueItem* cur; bool inDownloads; }; void QueueManager::loadQueue() throw() { try { QueueLoader l; SimpleXMLReader(&l).fromXML(File(getQueueFile(), File::READ, File::OPEN).read()); dirty = false; } catch(const Exception&) { // ... } } static const string sDownload = "Download"; static const string sTempTarget = "TempTarget"; static const string sTarget = "Target"; static const string sSize = "Size"; static const string sDownloaded = "Downloaded"; static const string sPriority = "Priority"; static const string sSource = "Source"; static const string sNick = "Nick"; static const string sPath = "Path"; static const string sDirectory = "Directory"; static const string sAdded = "Added"; static const string sUtf8 = "Utf8"; static const string sTTH = "TTH"; void QueueLoader::startTag(const string& name, StringPairList& attribs, bool simple) { QueueManager* qm = QueueManager::getInstance(); if(!inDownloads && name == "Downloads") { inDownloads = true; } else if(inDownloads) { if(cur == NULL && name == sDownload) { int flags = QueueItem::FLAG_RESUME; int64_t size = Util::toInt64(getAttrib(attribs, sSize, 1)); if(size == 0) return; try { const string& tgt = getAttrib(attribs, sTarget, 0); target = QueueManager::checkTarget(tgt, size, flags); if(target.empty()) return; } catch(const Exception&) { return; } QueueItem::Priority p = (QueueItem::Priority)Util::toInt(getAttrib(attribs, sPriority, 3)); string tempTarget = getAttrib(attribs, sTempTarget, 4); u_int32_t added = (u_int32_t)Util::toInt(getAttrib(attribs, sAdded, 5)); const string& tthRoot = getAttrib(attribs, sTTH, 6); int64_t downloaded = Util::toInt64(getAttrib(attribs, sDownloaded, 6)); if (downloaded > size || downloaded < 0) downloaded = 0; if(added == 0) added = GET_TIME(); QueueItem* qi = qm->fileQueue.find(target); if(qi == NULL) { if(tthRoot.empty()) { qi = qm->fileQueue.add(target, size, flags, p, tempTarget, downloaded, added, NULL); } else { TTHValue root(tthRoot); qi = qm->fileQueue.add(target, size, flags, p, tempTarget, downloaded, added, &root); } qm->fire(QueueManagerListener::Added(), qi); } if(!simple) cur = qi; } else if(cur != NULL && name == sSource) { const string& nick = getAttrib(attribs, sNick, 0); if(nick.empty()) return; const string& path = getAttrib(attribs, sPath, 1); if(path.empty()) return; const string& utf8 = getAttrib(attribs, sUtf8, 2); bool isUtf8 = (utf8 == "1"); User::Ptr user = ClientManager::getInstance()->getUser(nick); try { if(qm->addSource(cur, path, user, 0, isUtf8) && user->isOnline()) ConnectionManager::getInstance()->getDownloadConnection(user); } catch(const Exception&) { return; } } else if(cur == NULL && name == sDirectory) { const string& targetd = getAttrib(attribs, sTarget, 0); if(targetd.empty()) return; const string& nick = getAttrib(attribs, sNick, 1); if(nick.empty()) return; QueueItem::Priority p = (QueueItem::Priority)Util::toInt(getAttrib(attribs, sPriority, 2)); const string& source = getAttrib(attribs, sSource, 3); if(source.empty()) return; qm->addDirectory(source, ClientManager::getInstance()->getUser(nick), targetd, p); } } } void QueueLoader::endTag(const string& name, const string&) { if(inDownloads) { if(name == sDownload) cur = NULL; else if(name == "Downloads") inDownloads = false; } } // SearchManagerListener void QueueManager::on(SearchManagerListener::SR, SearchResult* sr) throw() { bool added = false; if(BOOLSETTING(AUTO_SEARCH)) { Lock l(cs); QueueItem::List matches; fileQueue.find(matches, sr->getSize(), Util::getFileExt(sr->getFile())); if(sr->getTTH() != NULL) { fileQueue.find(matches, sr->getTTH()); } for(QueueItem::Iter i = matches.begin(); i != matches.end(); ++i) { QueueItem* qi = *i; bool found = false; if(qi->getTTH()) { found = sr->getTTH() && (*qi->getTTH() == *sr->getTTH()) && (qi->getSize() == sr->getSize()); } else { found = (Util::stricmp(qi->getTargetFileName(), sr->getFileName()) == 0); } if(found) { try { addSource(qi, sr->getFile(), sr->getUser(), 0, false); added = true; } catch(const Exception&) { // ... } break; } } } if(added && BOOLSETTING(AUTO_SEARCH_AUTO_MATCH)) try { addList(sr->getUser(), QueueItem::FLAG_MATCH_QUEUE); } catch(const Exception&) { // ... } } // ClientManagerListener void QueueManager::on(ClientManagerListener::UserUpdated, const User::Ptr& aUser) throw() { bool hasDown = false; { Lock l(cs); for(int i = 0; i < QueueItem::LAST; ++i) { QueueItem::UserListIter j = userQueue.getList(i).find(aUser); if(j != userQueue.getList(i).end()) { for(QueueItem::Iter m = j->second.begin(); m != j->second.end(); ++m) fire(QueueManagerListener::StatusUpdated(), *m); if(i != QueueItem::PAUSED) hasDown = true; } } } if(aUser->isOnline() && hasDown) ConnectionManager::getInstance()->getDownloadConnection(aUser); } void QueueManager::on(TimerManagerListener::Second, u_int32_t aTick) throw() { if(dirty && ((lastSave + 10000) < aTick)) { saveQueue(); } } /** * @file * $Id: QueueManager.cpp,v 1.105 2004/09/26 18:54:08 arnetheduck Exp $ */getTarget())); f.write(STRINGLEN("\" Size=\"")); f.write(Util::toString(d->getSize())); f.write(STRINGLEN("\" Priority=\"")); f.write(Util::toString((int)d->getPriority())); f.write(STRINGLEN("\" TempTarget=\"")); f.write(CHECKESCAPE(d->getTempTarget())); f.write(STRINGLEN("\" Added=\"")); f.write(Util::toString(d->getAdded())); if(d->getTTH() != NULL) { b32tmp.clear(); f.write(STRINGLEN("\" TTH=\"")); f.write(d->getTTH()->toBase32(b32tmp)); } if(d->getDownloadedBytes() != 0) { f.write(STRINGLEN("\" Downloaded=\"")); f.write(Util::toString(d->getDownloadedBytes())); } f.write(STRINGLEN("\">\r\n")); for(QueueItem::Source::List::const_iterator j = d->sources.begin(); j != d->sources.end(); ++j) { QueueItem::Source* s = *j; f.write(STRINGLEN("\t\t \r\n")); } } for(DirectoryItem::DirectoryIter j = directories.begin(); j != directories.end(); ++j) { DirectoryItem::Ptr d = j->second; f.write(STRINGLEN("\tgetUser()->getNick())); f.write(STRINGLEN("\" Path=\"")); f.write(CHECKESCAPE(s->getPath())); f.write(STRINGLEN("\" Utf8=\"")); f.write(s->isSet(QueueItem::Source::FLAG_UTF8) ? "1" : "0", 1); f.write(STRINGLEN("\"/>\r\n")); } f.write(STRINGLEN("\t getTarget())); f.write(STRINGLEN("\" Nick=\"")); f.write(CHECKESCAPE(d->getUser()->getNick())); f.write(STRINGLEN("\" Priority=\"")); f.write(Util::toString((int)d->getPriority())); f.write(STRINGLEN("\" Source=\"")); f.write(CHECKESCAPE(d->getName())); f.write(STRINGLEN("\"/>\r\n")); } f.write("