diff --git a/examples/ktcli/ktcli.cpp b/examples/ktcli/ktcli.cpp index 33b47c4..e31a8d1 100644 --- a/examples/ktcli/ktcli.cpp +++ b/examples/ktcli/ktcli.cpp @@ -1,253 +1,253 @@ /*************************************************************************** * Copyright (C) 2010 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "ktcli.h" + #include #include #include #include #include -#include "ktcli.h" - #include #include #include #include #include #include #include #include using namespace bt; KTCLI::KTCLI(int argc, char** argv) : QCoreApplication(argc, argv), tc(new TorrentControl()),updates(0) { parser.addPositionalArgument("url", tr("Torrent to open")); parser.addOption(QCommandLineOption("port", tr("Port to use"), "", "6881")); parser.addOption(QCommandLineOption("tmpdir", tr("Port to use"), "", QDir::tempPath())); parser.addOption(QCommandLineOption("encryption", tr("Whether or not to enable encryption"))); parser.addOption(QCommandLineOption("pex", tr("Whether or not to enable peer exchange"))); parser.addOption(QCommandLineOption("utp", tr("Whether or not to use utp"))); parser.process(*this); qsrand(time(0)); bt::SetClientInfo("ktcli",bt::MAJOR,bt::MINOR,bt::RELEASE,bt::NORMAL,"KT"); bt::InitLog("ktcli.log",false,true,false); connect(tc.get(),SIGNAL(finished(bt::TorrentInterface*)),this,SLOT(finished(bt::TorrentInterface*))); connect(this,SIGNAL(aboutToQuit()),this,SLOT(shutdown())); } KTCLI::~KTCLI() { } bool KTCLI::start() { bool ok = false; quint16 port = parser.value("port").toInt(&ok); if (!ok) port = qrand(); if (parser.isSet("encryption")) { Out(SYS_GEN|LOG_NOTICE) << "Enabled encryption" << endl; ServerInterface::enableEncryption(false); } if (parser.isSet("pex")) { Out(SYS_GEN|LOG_NOTICE) << "Enabled PEX" << endl; UTPex::setEnabled(true); } if (parser.isSet("utp")) { Out(SYS_GEN|LOG_NOTICE) << "Enabled UTP" << endl; if (!bt::Globals::instance().initUTPServer(port)) { Out(SYS_GEN|LOG_NOTICE) << "Failed to listen on port " << port << endl; return false; } ServerInterface::setPort(port); ServerInterface::setUtpEnabled(true,true); ServerInterface::setPrimaryTransportProtocol(UTP); } else { if (!bt::Globals::instance().initTCPServer(port)) { Out(SYS_GEN|LOG_NOTICE) << "Failed to listen on port " << port << endl; return false; } } if (parser.positionalArguments().isEmpty()) return false; return load(QUrl::fromLocalFile(parser.positionalArguments().at(0))); } bool KTCLI::load(const QUrl &url) { QDir dir(url.toLocalFile()); if (dir.exists() && dir.exists("torrent") && dir.exists("stats")) { // Load existing torrent if (loadFromDir(dir.absolutePath())) { tc->start(); connect(&timer,SIGNAL(timeout()),this,SLOT(update())); timer.start(250); return true; } } else if (url.isLocalFile()) { QString path = url.toLocalFile(); if (loadFromFile(path)) { tc->start(); connect(&timer,SIGNAL(timeout()),this,SLOT(update())); timer.start(250); return true; } } else { Out(SYS_GEN|LOG_IMPORTANT) << "Non local files not supported" << endl; } return false; } QString KTCLI::tempDir() { QDir tmpdir = QDir(parser.value("tmpdir")); int i = 0; while (tmpdir.exists(QString("tor%1").arg(i))) i++; QString sd = QString("tor%1").arg(i); if (!tmpdir.mkdir(sd)) throw bt::Error(QString("Failed to create temporary directory %1").arg(sd)); tmpdir.cd(sd); return tmpdir.absolutePath(); } bool KTCLI::loadFromFile(const QString & path) { try { tc->init(this, bt::LoadFile(path), tempDir(), QDir::currentPath()); tc->setLoadUrl(QUrl(path)); tc->createFiles(); return true; } catch (bt::Error & err) { Out(SYS_GEN|LOG_IMPORTANT) << err.toString() << endl; return false; } } bool KTCLI::loadFromDir(const QString& path) { try { tc->init(this, bt::LoadFile(path + "/torrent"), path, QString::null); tc->createFiles(); return true; } catch (bt::Error & err) { Out(SYS_GEN|LOG_IMPORTANT) << err.toString() << endl; return false; } } bool KTCLI::notify(QObject* obj, QEvent* ev) { try { return QCoreApplication::notify(obj,ev); } catch (bt::Error & err) { Out(SYS_GEN|LOG_DEBUG) << "Error: " << err.toString() << endl; } catch (std::exception & err) { Out(SYS_GEN|LOG_DEBUG) << "Error: " << err.what() << endl; } return true; } bool KTCLI::alreadyLoaded(const bt::SHA1Hash& ih) const { Q_UNUSED(ih); return false; } void KTCLI::mergeAnnounceList(const bt::SHA1Hash& ih, const bt::TrackerTier* trk) { Q_UNUSED(ih); Q_UNUSED(trk); } void KTCLI::update() { bt::UpdateCurrentTime(); AuthenticationMonitor::instance().update(); tc->update(); updates++; if (updates % 20 == 0) { Out(SYS_GEN|LOG_DEBUG) << "Speed down " << bt::BytesPerSecToString(tc->getStats().download_rate) << endl; Out(SYS_GEN|LOG_DEBUG) << "Speed up " << bt::BytesPerSecToString(tc->getStats().upload_rate) << endl; } } void KTCLI::finished(bt::TorrentInterface* tor) { Q_UNUSED(tor); Out(SYS_GEN|LOG_NOTICE) << "Torrent fully downloaded" << endl; QTimer::singleShot(0,this,SLOT(shutdown())); } void KTCLI::shutdown() { AuthenticationMonitor::instance().shutdown(); WaitJob* j = new WaitJob(2000); tc->stop(j); if (j->needToWait()) j->exec(); j->deleteLater(); Globals::instance().shutdownTCPServer(); Globals::instance().shutdownUTPServer(); quit(); } diff --git a/src/bcodec/bnode.h b/src/bcodec/bnode.h index 7ae70b0..216b519 100644 --- a/src/bcodec/bnode.h +++ b/src/bcodec/bnode.h @@ -1,233 +1,232 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BTBNODE_H #define BTBNODE_H #include #include -#include #include #include #include #include "value.h" namespace bt { class BListNode; /** * @author Joris Guisson * @brief Base class for a node in a b-encoded piece of data * * There are 3 possible pieces of data in b-encoded piece of data. * This is the base class for all those 3 things. */ class KTORRENT_EXPORT BNode { public: enum Type { VALUE,DICT,LIST }; /** * Constructor, sets the Type, and the offset into * the data. * @param type Type of node * @param off The offset into the data */ BNode(Type type,Uint32 off); virtual ~BNode(); /// Get the type of node Type getType() const {return type;} /// Get the offset in the bytearray where this node starts. Uint32 getOffset() const {return off;} /// Get the length this node takes up in the bytearray. Uint32 getLength() const {return len;} /// Set the length void setLength(Uint32 l) {len = l;} /// Print some debugging info virtual void printDebugInfo() = 0; private: Type type; Uint32 off,len; }; /** * @author Joris Guisson * @brief Represents a value (string,bytearray or int) in bencoded data * * @todo Use QVariant */ class KTORRENT_EXPORT BValueNode : public BNode { Value value; public: BValueNode(const Value & v,Uint32 off); virtual ~BValueNode(); const Value & data() const {return value;} void printDebugInfo(); }; /** * @author Joris Guisson * @brief Represents a dictionary in bencoded data * */ class KTORRENT_EXPORT BDictNode : public BNode { struct DictEntry { QByteArray key; BNode* node; }; QList children; public: BDictNode(Uint32 off); virtual ~BDictNode(); /// Get a list of keys QList keys() const; /** * Insert a BNode in the dictionary. * @param key The key * @param node The node */ void insert(const QByteArray & key,BNode* node); /** * Get a BNode. * @param key The key * @return The node or 0 if there is no node with has key @a key */ BNode* getData(const QByteArray & key); /** * Get a BListNode. * @param key The key * @return The node or 0 if there is no list node with has key @a key */ BListNode* getList(const QByteArray& key); /** * Get a BDictNode. * @param key The key * @return The node or 0 if there is no dict node with has key @a key */ BDictNode* getDict(const QByteArray& key); /** * Get a BValueNode. * @param key The key * @return The node or 0 if there is no value node with has key @a key */ BValueNode* getValue(const QByteArray& key); /// Same as getValue, except directly returns an int, if something goes wrong, an error will be thrown int getInt(const QByteArray& key); /// Same as getValue, except directly returns a qint64, if something goes wrong, an error will be thrown qint64 getInt64(const QByteArray& key); /// Same as getValue, except directly returns a QString, if something goes wrong, an error will be thrown QString getString(const QByteArray& key,QTextCodec* tc); /// Same as getValue, except directly returns an QByteArray, if something goes wrong, an error will be thrown QByteArray getByteArray(const QByteArray& key); void printDebugInfo(); }; /** * @author Joris Guisson * @brief Represents a list in bencoded data * */ class KTORRENT_EXPORT BListNode : public BNode { QList children; public: BListNode(Uint32 off); virtual ~BListNode(); /** * Append a node to the list. * @param node The node */ void append(BNode* node); void printDebugInfo(); /// Get the number of nodes in the list. Uint32 getNumChildren() const {return children.count();} /** * Get a node from the list * @param idx The index * @return The node or 0 if idx is out of bounds */ BNode* getChild(Uint32 idx) {return children.at(idx);} /** * Get a BListNode. * @param idx The index * @return The node or 0 if the index is out of bounds or the element * at postion @a idx isn't a BListNode. */ BListNode* getList(Uint32 idx); /** * Get a BDictNode. * @param idx The index * @return The node or 0 if the index is out of bounds or the element * at postion @a idx isn't a BDictNode. */ BDictNode* getDict(Uint32 idx); /** * Get a BValueNode. * @param idx The index * @return The node or 0 if the index is out of bounds or the element * at postion @a idx isn't a BValueNode. */ BValueNode* getValue(Uint32 idx); /// Same as getValue, except directly returns an int, if something goes wrong, an error will be thrown int getInt(Uint32 idx); /// Same as getValue, except directly returns a qint64, if something goes wrong, an error will be thrown qint64 getInt64(Uint32 idx); /// Same as getValue, except directly returns a QString, if something goes wrong, an error will be thrown QString getString(Uint32 idx,QTextCodec* tc); /// Same as getValue, except directly returns an QByteArray, if something goes wrong, an error will be thrown QByteArray getByteArray(Uint32 idx); }; } #endif diff --git a/src/dht/kbucket.h b/src/dht/kbucket.h index 151a069..18f734b 100644 --- a/src/dht/kbucket.h +++ b/src/dht/kbucket.h @@ -1,158 +1,158 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef DHTKBUCKET_H #define DHTKBUCKET_H #include #include #include #include #include "key.h" #include "rpccall.h" #include "kbucketentry.h" namespace bt { class BEncoder; } namespace dht { class RPCServerInterface; class KClosestNodesSearch; class Task; const bt::Uint32 K = 20; const bt::Uint32 BUCKET_MAGIC_NUMBER = 0xB0C4B0C4; const bt::Uint32 BUCKET_REFRESH_INTERVAL = 15 * 60 * 1000; /** * @author Joris Guisson * * A KBucket is just a list of KBucketEntry objects. * The list is sorted by time last seen : * The first element is the least recently seen, the last * the most recently seen. */ class KBucket : public RPCCallListener { Q_OBJECT public: KBucket(RPCServerInterface* srv, const Key & our_id); KBucket(const dht::Key & min_key, const dht::Key & max_key, RPCServerInterface* srv, const Key & our_id); virtual ~KBucket(); typedef QSharedPointer Ptr; /// Get the min key const dht::Key & minKey() const {return min_key;} /// Get the max key const dht::Key & maxKey() const {return max_key;} /// Does the key k lies in in the range of this bucket bool keyInRange(const dht::Key & k) const; /// Are we allowed to split bool splitAllowed() const; class UnableToSplit {}; /** * Split the bucket in two new buckets, each containing half the range of the original one. * @return A pair of KBucket's * @throw UnableToSplit if something goes wrong */ std::pair split() throw (UnableToSplit); /** * Inserts an entry into the bucket. * @param entry The entry to insert - * @return true If the bucket needs to be splitted, false otherwise + * @return true If the bucket needs to be split, false otherwise */ bool insert(const KBucketEntry & entry); /// Get the least recently seen node const KBucketEntry & leastRecentlySeen() const {return entries[0];} /// Get the number of entries bt::Uint32 getNumEntries() const {return entries.count();} /// See if this bucket contains an entry bool contains(const KBucketEntry & entry) const; /** * Find the K closest entries to a key and store them in the KClosestNodesSearch * object. * @param kns The object to storre the search results */ void findKClosestNodes(KClosestNodesSearch & kns); /** * A peer failed to respond * @param addr Address of the peer */ bool onTimeout(const net::Address & addr); /// Check if the bucket needs to be refreshed bool needsToBeRefreshed() const; /// save the bucket to a file void save(bt::BEncoder & enc); /// Load the bucket from a file void load(bt::BDictNode* dict); /// Update the refresh timer of the bucket void updateRefreshTimer(); /// Set the refresh task void setRefreshTask(Task* t); private: virtual void onResponse(RPCCall* c, RPCMsg::Ptr rsp); virtual void onTimeout(RPCCall* c); void pingQuestionable(const KBucketEntry & replacement_entry); bool replaceBadEntry(const KBucketEntry & entry); private Q_SLOTS: void onFinished(Task* t); private: dht::Key min_key, max_key; QList entries, pending_entries; RPCServerInterface* srv; Key our_id; QMap pending_entries_busy_pinging; mutable bt::TimeStamp last_modified; Task* refresh_task; }; } template inline uint qHash(const T & e) { return e.hash(); } #endif diff --git a/src/dht/kbuckettable.cpp b/src/dht/kbuckettable.cpp index 5fdf9bc..ea8e8f7 100644 --- a/src/dht/kbuckettable.cpp +++ b/src/dht/kbuckettable.cpp @@ -1,217 +1,217 @@ /*************************************************************************** * Copyright (C) 2012 by * * Joris Guisson * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "kbuckettable.h" #include #include #include #include #include #include #include #include "nodelookup.h" #include "dht.h" using namespace bt; namespace dht { KBucketTable::KBucketTable(const Key & our_id) : our_id(our_id) { } KBucketTable::~KBucketTable() { } void KBucketTable::insert(const dht::KBucketEntry& entry, dht::RPCServerInterface* srv) { if(buckets.empty()) { KBucket::Ptr initial(new KBucket(srv, our_id)); buckets.push_back(initial); } KBucketList::iterator kb = findBucket(entry.getID()); // return if we can't find a bucket, should never happen' if(kb == buckets.end()) { Out(SYS_DHT | LOG_IMPORTANT) << "Unable to find bucket !" << endl; return; } // insert it into the bucket try { if((*kb)->insert(entry)) { - // Bucket needs to be splitted + // Bucket needs to be split std::pair result = (*kb)->split(); /* Out(SYS_DHT | LOG_DEBUG) << "Splitting bucket " << (*kb)->minKey().toString() << "-" << (*kb)->maxKey().toString() << endl; Out(SYS_DHT | LOG_DEBUG) << "L: " << result.first->minKey().toString() << "-" << result.first->maxKey().toString() << endl; Out(SYS_DHT | LOG_DEBUG) << "L range: " << (result.first->maxKey() - result.first->minKey()).toString() << endl; Out(SYS_DHT | LOG_DEBUG) << "R: " << result.second->minKey().toString() << "-" << result.second->maxKey().toString() << endl; Out(SYS_DHT | LOG_DEBUG) << "R range: " << (result.second->maxKey() - result.second->minKey()).toString() << endl; */ buckets.insert(kb, result.first); buckets.insert(kb, result.second); buckets.erase(kb); if(result.first->keyInRange(entry.getID())) result.first->insert(entry); else result.second->insert(entry); } } catch(const KBucket::UnableToSplit &) { // Can't split, so stop this Out(SYS_DHT | LOG_IMPORTANT) << "Unable to split buckets further !" << endl; return; } } int KBucketTable::numEntries() const { int count = 0; for(KBucketList::const_iterator i = buckets.begin(); i != buckets.end(); ++i) { count += (*i)->getNumEntries(); } return count; } KBucketTable::KBucketList::iterator KBucketTable::findBucket(const dht::Key& id) { for(KBucketList::iterator i = buckets.begin(); i != buckets.end(); ++i) { if((*i)->keyInRange(id)) return i; } return buckets.end(); } void KBucketTable::refreshBuckets(DHT* dh_table) { for(KBucketList::iterator i = buckets.begin(); i != buckets.end(); ++i) { KBucket::Ptr b = *i; if(b->needsToBeRefreshed()) { // the key needs to be the refreshed dht::Key m = dht::Key::mid(b->minKey(), b->maxKey()); NodeLookup* nl = dh_table->refreshBucket(m, *b); if(nl) b->setRefreshTask(nl); } } } void KBucketTable::onTimeout(const net::Address& addr) { for(KBucketList::iterator i = buckets.begin(); i != buckets.end(); ++i) { KBucket::Ptr b = *i; if(b->onTimeout(addr)) return; } } void KBucketTable::loadTable(const QString& file, RPCServerInterface* srv) { QFile fptr(file); if(!fptr.open(QIODevice::ReadOnly)) { Out(SYS_DHT | LOG_IMPORTANT) << "DHT: Cannot open file " << file << " : " << fptr.errorString() << endl; return; } try { QByteArray data = fptr.readAll(); bt::BDecoder dec(data, false, 0); QScopedPointer bucket_list(dec.decodeList()); if(!bucket_list) return; for(bt::Uint32 i = 0; i < bucket_list->getNumChildren(); i++) { BDictNode* dict = bucket_list->getDict(i); if(!dict) continue; KBucket::Ptr bucket(new KBucket(srv, our_id)); bucket->load(dict); buckets.push_back(bucket); } } catch(bt::Error & e) { Out(SYS_DHT | LOG_IMPORTANT) << "DHT: Failed to load bucket table: " << e.toString() << endl; } } void KBucketTable::saveTable(const QString& file) { bt::File fptr; if(!fptr.open(file, "wb")) { Out(SYS_DHT | LOG_IMPORTANT) << "DHT: Cannot open file " << file << " : " << fptr.errorString() << endl; return; } BEncoder enc(&fptr); try { enc.beginList(); for(KBucketList::iterator i = buckets.begin(); i != buckets.end(); ++i) { KBucket::Ptr b = *i; b->save(enc); } enc.end(); } catch(bt::Error & err) { Out(SYS_DHT | LOG_IMPORTANT) << "DHT: Failed to save table to " << file << " : " << err.toString() << endl; } } void KBucketTable::findKClosestNodes(KClosestNodesSearch& kns) { for(KBucketList::iterator i = buckets.begin(); i != buckets.end(); ++i) { KBucket::Ptr b = *i; b->findKClosestNodes(kns); } } } diff --git a/src/diskio/chunk.cpp b/src/diskio/chunk.cpp index 2006ebe..1a95642 100644 --- a/src/diskio/chunk.cpp +++ b/src/diskio/chunk.cpp @@ -1,72 +1,72 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "chunk.h" +#include #include "cache.h" #include "piecedata.h" #ifndef Q_WS_WIN #include #endif namespace bt { Chunk::Chunk(Uint32 index,Uint32 size,Cache* cache) : status(Chunk::NOT_DOWNLOADED),index(index),size(size),priority(NORMAL_PRIORITY),cache(cache) { } Chunk::~Chunk() { } bool Chunk::readPiece(Uint32 off,Uint32 len,Uint8* data) { PieceData::Ptr d = cache->loadPiece(this,off,len); if (d && d->ok()) return d->read(data,len) == len; else return false; } bool Chunk::checkHash(const SHA1Hash & h) { PieceData::Ptr d = getPiece(0,size,true); if (!d || !d->ok()) return false; else return d->generateHash() == h; } PieceData::Ptr Chunk::getPiece(Uint32 off,Uint32 len,bool read_only) { if (read_only) return cache->loadPiece(this,off,len); else return cache->preparePiece(this,off,len); } void Chunk::savePiece(PieceData::Ptr piece) { cache->savePiece(piece); } } diff --git a/src/diskio/deletedatafilesjob.cpp b/src/diskio/deletedatafilesjob.cpp index 2f589ed..b415042 100644 --- a/src/diskio/deletedatafilesjob.cpp +++ b/src/diskio/deletedatafilesjob.cpp @@ -1,133 +1,133 @@ /*************************************************************************** * Copyright (C) 2008 by Joris Guisson and Ivan Vasic * * joris.guisson@gmail.com * * ivasic@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "deletedatafilesjob.h" #include #include #include #include #include #include -#include "deletedatafilesjob.h" namespace bt { DeleteDataFilesJob::DeleteDataFilesJob(const QString & base) : Job(true,0),base(base),directory_tree(0) { } DeleteDataFilesJob::~DeleteDataFilesJob() { delete directory_tree; } void DeleteDataFilesJob::addFile(const QString & file) { files.append(QUrl::fromLocalFile(file)); } void DeleteDataFilesJob::addEmptyDirectoryCheck(const QString & fpath) { if (!directory_tree) directory_tree = new DirTree(base); directory_tree->insert(fpath); } void DeleteDataFilesJob::start() { active_job = KIO::del(files,KIO::HideProgressInfo); connect(active_job, &KIO::Job::result, this, &DeleteDataFilesJob::onDeleteJobDone); } void DeleteDataFilesJob::onDeleteJobDone(KJob* j) { if (j != active_job) return; if (active_job->error()) active_job->ui()->showErrorMessage(); active_job = 0; if (directory_tree) directory_tree->doDeleteOnEmpty(base); setError(0); emitResult(); } void DeleteDataFilesJob::kill(bool quietly) { Q_UNUSED(quietly); } DeleteDataFilesJob::DirTree::DirTree(const QString & name) : name(name) { subdirs.setAutoDelete(true); } DeleteDataFilesJob::DirTree::~DirTree() { } void DeleteDataFilesJob::DirTree::insert(const QString & fpath) { int i = fpath.indexOf(bt::DirSeparator()); if (i == -1) // last part of fpath is a file, so we need to ignore that return; QString dn = fpath.left(i); DirTree* d = subdirs.find(dn); if (!d) { d = new DirTree(dn); subdirs.insert(dn,d); } d->insert(fpath.mid(i+1)); } void DeleteDataFilesJob::DirTree::doDeleteOnEmpty(const QString & base) { bt::PtrMap::iterator i = subdirs.begin(); while (i != subdirs.end()) { i->second->doDeleteOnEmpty(base + i->first + bt::DirSeparator()); ++i; } QDir dir(base); QStringList el = dir.entryList(QDir::AllEntries|QDir::System|QDir::Hidden); el.removeAll("."); el.removeAll(".."); if (el.count() == 0) { // no childern so delete the directory Out(SYS_DIO|LOG_DEBUG) << "Deleting empty directory : " << base << endl; bt::Delete(base,true); } } } diff --git a/src/diskio/multifilecache.cpp b/src/diskio/multifilecache.cpp index 7170ae7..0a513ad 100644 --- a/src/diskio/multifilecache.cpp +++ b/src/diskio/multifilecache.cpp @@ -1,945 +1,945 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "multifilecache.h" +#include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_WS_WIN #include #endif #include #include "cache.h" #include "chunk.h" #include "cachefile.h" #include "dndfile.h" #include "preallocationthread.h" #include "movedatafilesjob.h" #include "deletedatafilesjob.h" #include "piecedata.h" namespace bt { static Uint64 FileOffset(Chunk* c, const TorrentFile & f, Uint64 chunk_size); static Uint64 FileOffset(Uint32 cindex, const TorrentFile & f, Uint64 chunk_size); static void DeleteEmptyDirs(const QString & output_dir, const QString & fpath); MultiFileCache::MultiFileCache(Torrent& tor, const QString & tmpdir, const QString & datadir, bool custom_output_name) : Cache(tor, tmpdir, datadir), opened(false) { cache_dir = tmpdir + "cache" + bt::DirSeparator(); if(!custom_output_name) output_dir = this->datadir + tor.getNameSuggestion() + bt::DirSeparator(); else output_dir = this->datadir; } MultiFileCache::~MultiFileCache() { cleanupPieceCache(); } void MultiFileCache::loadFileMap() { QString file_map = tmpdir + "file_map"; if(!bt::Exists(file_map)) { // file map doesn't exist, so just set the path on disk if it has not happened yet Uint32 num = tor.getNumFiles(); for(Uint32 i = 0; i < num; i++) { TorrentFile & tf = tor.getFile(i); if(tf.getPathOnDisk().isEmpty()) tf.setPathOnDisk(output_dir + tf.getUserModifiedPath()); } saveFileMap(); } else { QFile fptr(tmpdir + "file_map"); if(!fptr.open(QIODevice::ReadOnly)) throw Error(i18n("Failed to open %1: %2", file_map, fptr.errorString())); Uint32 idx = 0; while(!fptr.atEnd() && idx < tor.getNumFiles()) { QString path = QString::fromLocal8Bit(fptr.readLine().trimmed()); tor.getFile(idx).setPathOnDisk(path); idx++; } // now the user modified paths must come idx = 0; while(!fptr.atEnd() && idx < tor.getNumFiles()) { QString path = QString::fromLocal8Bit(fptr.readLine().trimmed()); if(!path.isEmpty()) tor.getFile(idx).setUserModifiedPath(path); idx++; } } } void MultiFileCache::saveFileMap() { QString file_map = tmpdir + "file_map"; QFile fptr(file_map); if(!fptr.open(QIODevice::WriteOnly)) throw Error(i18n("Failed to create %1: %2", file_map, fptr.errorString())); QTextStream out(&fptr); // file map doesn't exist, so create it based upon the output_dir Uint32 num = tor.getNumFiles(); for(Uint32 i = 0; i < num; i++) { TorrentFile & tf = tor.getFile(i); out << tf.getPathOnDisk() << ::endl; } // After the actual paths on disk, save the user modified path names for(Uint32 i = 0; i < num; i++) { TorrentFile & tf = tor.getFile(i); out << tf.getUserModifiedPath() << ::endl; } } QString MultiFileCache::getOutputPath() const { return output_dir; } void MultiFileCache::close() { clearPieceCache(); if(piece_cache.isEmpty()) files.clear(); opened = false; } void MultiFileCache::open() { // Check if the cache is not yet open if(opened) return; QString dnd_dir = tmpdir + "dnd" + bt::DirSeparator(); // open all files for(Uint32 i = 0; i < tor.getNumFiles(); i++) { TorrentFile & tf = tor.getFile(i); if(!tf.doNotDownload()) { if(files.contains(i)) files.remove(i); CacheFile::Ptr fd(new CacheFile()); fd->open(tf.getPathOnDisk(), tf.getSize()); files.insert(i, fd); } else { if(dnd_files.contains(i)) dnd_files.remove(i); QString dnd_path = QString("file%1.dnd").arg(tf.getIndex()); QString dnd_file = dnd_dir + dnd_path; if(bt::Exists(dnd_dir + tf.getUserModifiedPath() + ".dnd")) { // old style dnd dir, move the file so that we can keep working // with the old file bt::Move(dnd_dir + tf.getUserModifiedPath() + ".dnd", dnd_file, true, true); } DNDFile::Ptr dfd(new DNDFile(dnd_file, &tf, tor.getChunkSize())); dfd->checkIntegrity(); dnd_files.insert(i, dfd); } } opened = true; } void MultiFileCache::changeTmpDir(const QString& ndir) { Cache::changeTmpDir(ndir); QString dnd_dir = tmpdir + "dnd" + bt::DirSeparator(); // change paths for individual files, it should not // be a problem to move these files when they are open for(Uint32 i = 0; i < tor.getNumFiles(); i++) { TorrentFile & tf = tor.getFile(i); if(tf.doNotDownload()) { DNDFile::Ptr dfd = dnd_files[i]; if(dfd) { QString dnd_path = QString("file%1.dnd").arg(tf.getIndex()); dfd->changePath(dnd_dir + dnd_path); } } } } void MultiFileCache::changeOutputPath(const QString & outputpath) { output_dir = outputpath; if(!output_dir.endsWith(bt::DirSeparator())) output_dir += bt::DirSeparator(); datadir = output_dir; Uint32 num = tor.getNumFiles(); for(Uint32 i = 0; i < num; i++) { TorrentFile & tf = tor.getFile(i); tf.setPathOnDisk(output_dir + tf.getUserModifiedPath()); CacheFile::Ptr cf = files[tf.getIndex()]; if(cf) cf->changePath(tf.getPathOnDisk()); } saveFileMap(); } Job* MultiFileCache::moveDataFiles(const QString & ndir) { if(!bt::Exists(ndir)) bt::MakeDir(ndir); QString nd = ndir; if(!nd.endsWith(bt::DirSeparator())) nd += bt::DirSeparator(); new_output_dir = nd; MoveDataFilesJob* job = new MoveDataFilesJob(); int nmoves = 0; for(Uint32 i = 0; i < tor.getNumFiles(); i++) { TorrentFile & tf = tor.getFile(i); if(tf.doNotDownload()) continue; // check if every directory along the path exists, and if it doesn't // create it MakeFilePath(nd + tf.getUserModifiedPath()); if(QFileInfo(tf.getPathOnDisk()).canonicalPath() != QFileInfo(nd + tf.getUserModifiedPath()).canonicalPath()) { job->addMove(tf.getPathOnDisk(), nd + tf.getUserModifiedPath()); nmoves++; } } if(nmoves == 0) { delete job; return 0; } else { return job; } } void MultiFileCache::moveDataFilesFinished(Job* job) { if(job->error()) return; for(Uint32 i = 0; i < tor.getNumFiles(); i++) { TorrentFile & tf = tor.getFile(i); tf.setPathOnDisk(new_output_dir + tf.getUserModifiedPath()); CacheFile::Ptr cf = files[tf.getIndex()]; if(cf) cf->changePath(tf.getPathOnDisk()); // check for empty directories and delete them DeleteEmptyDirs(output_dir, tf.getUserModifiedPath()); } } Job* MultiFileCache::moveDataFiles(const QMap & files) { if(files.count() == 0) return 0; MoveDataFilesJob* job = new MoveDataFilesJob(files); return job; } void MultiFileCache::moveDataFilesFinished(const QMap & fmap, Job* job) { if(job->error()) return; QMap::const_iterator i = fmap.begin(); while(i != fmap.end()) { TorrentFileInterface* tf = i.key(); QString path = tf->getPathOnDisk(); QString dest = i.value(); if(QFileInfo(dest).isDir()) { QString path = tf->getUserModifiedPath(); if(!dest.endsWith(bt::DirSeparator())) dest += bt::DirSeparator(); int last = path.lastIndexOf(bt::DirSeparator()); tf->setPathOnDisk(dest + path.mid(last + 1)); } else tf->setPathOnDisk(i.value()); CacheFile::Ptr cf = files[tf->getIndex()]; if(cf) cf->changePath(tf->getPathOnDisk()); ++i; } Cache::moveDataFilesFinished(fmap, job); saveFileMap(); } void MultiFileCache::create() { if(!bt::Exists(output_dir)) MakeDir(output_dir); if(!bt::Exists(tmpdir + "dnd")) bt::MakeDir(tmpdir + "dnd"); QSet mount_points; QSet shortened_names; for(Uint32 i = 0; i < tor.getNumFiles(); i++) { TorrentFile & tf = tor.getFile(i); #ifndef Q_WS_WIN // check if the filename is to long if(FileNameToLong(tf.getPathOnDisk())) { QString s = ShortenFileName(tf.getPathOnDisk()); Out(SYS_DIO | LOG_DEBUG) << "Path to long " << tf.getPathOnDisk() << endl; // make sure there are no dupes int cnt = 1; while(shortened_names.contains(s)) { s = ShortenFileName(tf.getPathOnDisk(), cnt++); } Out(SYS_DIO | LOG_DEBUG) << "Shortened to " << s << endl; tf.setPathOnDisk(s); shortened_names.insert(s); } #endif touch(tf); if(!tf.doNotDownload()) mount_points.insert(tf.getMountPoint()); } saveMountPoints(mount_points); saveFileMap(); } void MultiFileCache::touch(TorrentFile & tf) { QString fpath = tf.getUserModifiedPath(); bool dnd = tf.doNotDownload(); // first split fpath by / separator QStringList sl = fpath.split(bt::DirSeparator()); // then make the file if(!dnd) { MakeFilePath(tf.getPathOnDisk()); if(!bt::Exists(tf.getPathOnDisk())) { bt::Touch(tf.getPathOnDisk()); } else { preexisting_files = true; tf.setPreExisting(true); // mark the file as preexisting } } } PieceData::Ptr MultiFileCache::createPiece(Chunk* c, Uint32 off, Uint32 length, bool read_only) { open(); QList tflist; tor.calcChunkPos(c->getIndex(), tflist); // one file so try to map it if(tflist.count() == 1) { const TorrentFile & f = tor.getFile(tflist.first()); CacheFile::Ptr fd = files[tflist.first()]; if(!fd) return PieceData::Ptr(); if(Cache::mappedModeAllowed() && mmap_failures < 3) { Uint64 offset = FileOffset(c, f, tor.getChunkSize()) + off; PieceData::Ptr piece(new PieceData(c, off, length, 0, fd, read_only)); Uint8* buf = (Uint8*)fd->map(piece.data(), offset, length, read_only ? CacheFile::READ : CacheFile::RW); if(buf) { piece->setData(buf); insertPiece(c, piece); return piece; } else { mmap_failures++; } } } // mmap failed or there are multiple files, so just do buffered Uint8* buf = new Uint8[length]; PieceData::Ptr piece(new PieceData(c, off, length, buf, CacheFile::Ptr(), read_only)); insertPiece(c, piece); return piece; } PieceData::Ptr MultiFileCache::preparePiece(Chunk* c, Uint32 off, Uint32 length) { PieceData::Ptr piece = findPiece(c, off, length, false); if(piece) return piece; return createPiece(c, off, length, false); } void MultiFileCache::calculateOffsetAndLength( Uint32 piece_off, Uint32 piece_len, Uint64 file_off, Uint32 chunk_off, Uint32 chunk_len, Uint64 & off, Uint32 & len) { // if the piece offset lies in the range of the current chunk, we need to read data if(piece_off >= chunk_off && piece_off + piece_len <= chunk_off + chunk_len) { // The piece lies entirely in the current file off = file_off + (piece_off - chunk_off); len = piece_len; } else if(piece_off >= chunk_off && piece_len < chunk_off + chunk_len) { // The start of the piece lies partially in the current file off = file_off + (piece_off - chunk_off); len = chunk_len - (piece_off - chunk_off); } else if(piece_off < chunk_off && piece_off + piece_len > chunk_off && piece_off + piece_len <= chunk_off + chunk_len) { // The end of the piece lies in the file off = file_off; len = piece_len - (chunk_off - piece_off); } else if(chunk_off >= piece_off && chunk_off + chunk_len < piece_off + piece_len) { // the current file lies entirely in the piece off = file_off; len = chunk_len; } } PieceData::Ptr MultiFileCache::loadPiece(Chunk* c, Uint32 off, Uint32 length) { open(); PieceData::Ptr piece = findPiece(c, off, length, true); if(piece) return piece; // create piece and return it if it is mapped or the create failed piece = createPiece(c, off, length, true); if(!piece || piece->mapped()) return piece; // Now we need to load it QList tflist; tor.calcChunkPos(c->getIndex(), tflist); // The chunk lies in one file, so it is easy if(tflist.count() == 1) { const TorrentFile & f = tor.getFile(tflist[0]); CacheFile::Ptr fd = files[tflist[0]]; Uint64 piece_off = FileOffset(c, f, tor.getChunkSize()) + off; fd->read(piece->data(), length, piece_off); return piece; } // multiple files Uint8* data = piece->data(); Uint32 chunk_off = 0; // number of bytes passed of the chunk Uint32 piece_off = 0; // how many bytes read to the piece for(int i = 0; i < tflist.count(); i++) { const TorrentFile & f = tor.getFile(tflist[i]); CacheFile::Ptr fd = files[tflist[i]]; DNDFile::Ptr dfd = dnd_files[tflist[i]]; // first calculate offset into file // only the first file can have an offset // the following files will start at the beginning Uint64 file_off = 0; if(i == 0) file_off = FileOffset(c, f, tor.getChunkSize()); Uint32 cdata = 0; // then the amount of data of the chunk which is located in this file if(tflist.count() == 1) cdata = c->getSize(); else if(i == 0) cdata = f.getLastChunkSize(); else if(i == tflist.count() - 1) cdata = c->getSize() - chunk_off; else cdata = f.getSize(); // if the piece does not lie in this part of the chunk, move on if(off + length <= chunk_off || off >= chunk_off + cdata) { chunk_off += cdata; continue; } Uint64 read_offset = 0; // The read offset in the file Uint32 read_length = 0; // how many bytes to read calculateOffsetAndLength(off, length, file_off, chunk_off, cdata, read_offset, read_length); Uint8* ptr = data + piece_off; // location to write to piece_off += read_length; if(fd) { fd->read(ptr, read_length, read_offset); } else if(dfd) { Uint32 ret = 0; if(i == 0) ret = dfd->readLastChunk(ptr, read_offset - file_off, read_length); else ret = dfd->readFirstChunk(ptr, read_offset, read_length); if(ret > 0 && ret != read_length) Out(SYS_DIO | LOG_DEBUG) << "Warning : MultiFileCache::loadPiece ret != to_read" << endl; } chunk_off += cdata; } return piece; } void MultiFileCache::savePiece(PieceData::Ptr piece) { open(); // in mapped mode unload the piece if not in use if(piece->mapped()) return; Uint8* data = piece->data(); if(!data) // this should not happen but just in case return; Chunk* c = piece->parentChunk(); QList tflist; tor.calcChunkPos(c->getIndex(), tflist); Uint32 chunk_off = 0; // number of bytes passed of the chunk Uint32 piece_off = 0; // how many bytes written from the piece Uint32 off = piece->offset(); Uint32 length = piece->length(); for(int i = 0; i < tflist.count(); i++) { const TorrentFile & f = tor.getFile(tflist[i]); CacheFile::Ptr fd = files[tflist[i]]; DNDFile::Ptr dfd = dnd_files[tflist[i]]; // first calculate offset into file // only the first file can have an offset // the following files will start at the beginning Uint64 file_off = 0; if(i == 0) file_off = FileOffset(c, f, tor.getChunkSize()); Uint32 cdata = 0; // then the amount of data of the chunk which is located in this file if(tflist.count() == 1) cdata = c->getSize(); else if(i == 0) cdata = f.getLastChunkSize(); else if(i == tflist.count() - 1) cdata = c->getSize() - chunk_off; else cdata = f.getSize(); // if the piece does not lie in this part of the chunk, move on if(off + length <= chunk_off || off >= chunk_off + cdata) { chunk_off += cdata; continue; } Uint64 write_offset = 0; // The write offset in the file Uint32 write_length = 0; // how many bytes to write calculateOffsetAndLength(off, length, file_off, chunk_off, cdata, write_offset, write_length); Uint8* ptr = data + piece_off; // location to read from piece_off += write_length; if(fd) { fd->write(ptr, write_length, write_offset); } else if(dfd) { if(i == 0) dfd->writeLastChunk(ptr, write_offset - file_off, write_length); else dfd->writeFirstChunk(ptr, write_offset, write_length); } chunk_off += cdata; } } void MultiFileCache::downloadStatusChanged(TorrentFile* tf, bool download) { bool dnd = !download; QString dnd_dir = tmpdir + "dnd" + bt::DirSeparator(); QString dnd_path = QString("file%1.dnd").arg(tf->getIndex()); QString dnd_file = dnd_dir + dnd_path; // if it is dnd and it is already in the dnd tree do nothing if(dnd && bt::Exists(dnd_dir + dnd_path)) return; else if(dnd && bt::Exists(dnd_dir + tf->getUserModifiedPath() + ".dnd")) { // old style dnd dir, move the file so that we can keep working // with the old file bt::Move(dnd_dir + tf->getUserModifiedPath() + ".dnd", dnd_file, true, true); return; } // if it is !dnd and it is already in the output_dir tree do nothing if(!dnd && bt::Exists(tf->getPathOnDisk())) return; try { if(dnd) { // save first and last chunk of the file if(bt::Exists(tf->getPathOnDisk())) saveFirstAndLastChunk(tf, tf->getPathOnDisk(), dnd_file); // delete data file if(bt::Exists(tf->getPathOnDisk())) bt::Delete(tf->getPathOnDisk(), true); files.remove(tf->getIndex()); DNDFile::Ptr dfd(new DNDFile(dnd_file, tf, tor.getChunkSize())); dfd->checkIntegrity(); dnd_files.insert(tf->getIndex(), dfd); } else { // recreate the file recreateFile(tf, dnd_dir + dnd_path, tf->getPathOnDisk()); bt::Delete(dnd_dir + dnd_path); dnd_files.remove(tf->getIndex()); CacheFile::Ptr fd(new CacheFile()); fd->open(tf->getPathOnDisk(), tf->getSize()); files.insert(tf->getIndex(), fd); } } catch(bt::Error & err) { Out(SYS_DIO | LOG_DEBUG) << err.toString() << endl; } } void MultiFileCache::saveFirstAndLastChunk(TorrentFile* tf, const QString & src_file, const QString & dst_file) { DNDFile out(dst_file, tf, tor.getChunkSize()); File fptr; if(!fptr.open(src_file, "rb")) throw Error(i18n("Cannot open file %1: %2", src_file, fptr.errorString())); Uint32 cs = (tf->getFirstChunk() == tor.getNumChunks() - 1) ? tor.getLastChunkSize() : tor.getChunkSize(); Uint8* tmp = new Uint8[tor.getChunkSize()]; try { fptr.read(tmp, cs - tf->getFirstChunkOffset()); out.writeFirstChunk(tmp, 0, cs - tf->getFirstChunkOffset()); if(tf->getFirstChunk() != tf->getLastChunk()) { Uint64 off = FileOffset(tf->getLastChunk(), *tf, tor.getChunkSize()); fptr.seek(File::BEGIN, off); fptr.read(tmp, tf->getLastChunkSize()); out.writeLastChunk(tmp, 0, tf->getLastChunkSize()); } delete [] tmp; } catch(...) { delete [] tmp; throw; } } void MultiFileCache::recreateFile(TorrentFile* tf, const QString & dnd_file, const QString & output_file) { DNDFile dnd(dnd_file, tf, tor.getChunkSize()); // make sure path exists MakeFilePath(output_file); // create the output file bt::Touch(output_file); Uint32 cs = (tf->getFirstChunk() == tor.getNumChunks() - 1) ? tor.getLastChunkSize() : tor.getChunkSize(); File fptr; if(!fptr.open(output_file, "r+b")) throw Error(i18n("Cannot open file %1: %2", output_file, fptr.errorString())); Uint32 ts = cs - tf->getFirstChunkOffset() > tf->getLastChunkSize() ? cs - tf->getFirstChunkOffset() : tf->getLastChunkSize(); Uint8* tmp = new Uint8[ts]; try { Uint32 to_read = cs - tf->getFirstChunkOffset(); if(to_read > tf->getSize()) // check for files which are smaller then a chunk to_read = tf->getSize(); to_read = dnd.readFirstChunk(tmp, 0, to_read); if(to_read > 0) fptr.write(tmp, to_read); if(tf->getFirstChunk() != tf->getLastChunk()) { Uint64 off = FileOffset(tf->getLastChunk(), *tf, tor.getChunkSize()); fptr.seek(File::BEGIN, off); to_read = dnd.readLastChunk(tmp, 0, tf->getLastChunkSize()); if(to_read > 0) fptr.write(tmp, to_read); } delete [] tmp; } catch(...) { delete [] tmp; throw; } } void MultiFileCache::preparePreallocation(PreallocationThread* prealloc) { QMap::iterator i = files.begin(); while(i != files.end()) { CacheFile::Ptr cf = i.value(); if(cf) prealloc->add(cf); ++i; } } bool MultiFileCache::hasMissingFiles(QStringList & sl) { bool ret = false; for(Uint32 i = 0; i < tor.getNumFiles(); i++) { TorrentFile & tf = tor.getFile(i); if(tf.doNotDownload()) continue; QString p = tf.getPathOnDisk(); if(!bt::Exists(p)) { ret = true; tf.setMissing(true); sl.append(p); } else tf.setMissing(false); } return ret; } static void DeleteEmptyDirs(const QString & output_dir, const QString & fpath) { QStringList sl = fpath.split(bt::DirSeparator()); // remove the last, which is just the filename sl.pop_back(); while(sl.count() > 0) { QString path = output_dir; // reassemble the full directory path for(QStringList::iterator itr = sl.begin(); itr != sl.end(); ++itr) path += *itr + bt::DirSeparator(); QDir dir(path); QStringList el = dir.entryList(QDir::AllEntries | QDir::System | QDir::Hidden); el.removeAll("."); el.removeAll(".."); if(el.count() == 0 && dir.exists()) { // no childern so delete the directory Out(SYS_GEN | LOG_DEBUG) << "Deleting empty directory : " << path << endl; bt::Delete(path, true); sl.pop_back(); // remove the last so we can go one higher } else { // children, so we cannot delete any more directories higher up return; } } // now the output_dir itself QDir dir(output_dir); QStringList el = dir.entryList(QDir::AllEntries | QDir::System | QDir::Hidden); el.removeAll("."); el.removeAll(".."); if(el.count() == 0 && dir.exists()) { Out(SYS_GEN | LOG_DEBUG) << "Deleting empty directory : " << output_dir << endl; bt::Delete(output_dir, true); } } Job* MultiFileCache::deleteDataFiles() { DeleteDataFilesJob* job = new DeleteDataFilesJob(output_dir); for(Uint32 i = 0; i < tor.getNumFiles(); i++) { TorrentFile & tf = tor.getFile(i); QString fpath = tf.getPathOnDisk(); if(!tf.doNotDownload()) { // first delete the file job->addFile(fpath); } // check for subdirectories job->addEmptyDirectoryCheck(tf.getUserModifiedPath()); } return job; } Uint64 MultiFileCache::diskUsage() { Uint64 sum = 0; for(Uint32 i = 0; i < tor.getNumFiles(); i++) { TorrentFile & tf = tor.getFile(i); if(tf.doNotDownload()) continue; try { CacheFile::Ptr cf = files[i]; if(cf) { sum += cf->diskUsage(); } else if(bt::Exists(tf.getPathOnDisk())) { // doesn't exist yet, must be before open is called // so create one and delete it right after CacheFile::Ptr cf(new CacheFile()); cf->open(tf.getPathOnDisk(), tf.getSize()); sum += cf->diskUsage(); } } catch(bt::Error & err) // make sure we catch any exceptions { Out(SYS_DIO | LOG_DEBUG) << "Error: " << err.toString() << endl; } } return sum; } bool MultiFileCache::getMountPoints(QSet& mps) { for(Uint32 i = 0; i < tor.getNumFiles(); i++) { TorrentFile & tf = tor.getFile(i); if(tf.doNotDownload()) continue; QString mp = tf.getMountPoint(); if(mp.isEmpty()) return false; mps.insert(mp); } return true; } /////////////////////////////// Uint64 FileOffset(Chunk* c, const TorrentFile & f, Uint64 chunk_size) { return FileOffset(c->getIndex(), f, chunk_size); } Uint64 FileOffset(Uint32 cindex, const TorrentFile & f, Uint64 chunk_size) { return f.fileOffset(cindex, chunk_size); } } diff --git a/src/diskio/piecedata.cpp b/src/diskio/piecedata.cpp index acd4036..4d40034 100644 --- a/src/diskio/piecedata.cpp +++ b/src/diskio/piecedata.cpp @@ -1,144 +1,144 @@ /*************************************************************************** * Copyright (C) 2008 by Joris Guisson and Ivan Vasic * * joris.guisson@gmail.com * * ivasic@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "piecedata.h" #include #include #ifndef Q_WS_WIN #include #endif -#include "piecedata.h" #include "chunk.h" #include #include #include namespace bt { PieceData::PieceData(bt::Chunk* chunk, bt::Uint32 off, bt::Uint32 len, bt::Uint8* ptr, bt::CacheFile::Ptr cache_file, bool read_only) : chunk(chunk), off(off), len(len), ptr(ptr), cache_file(cache_file), read_only(read_only) { } PieceData::~PieceData() { unload(); } void PieceData::unload() { if(!ptr) return; if(!mapped()) delete [] ptr; else cache_file->unmap(ptr, len); ptr = 0; } Uint32 PieceData::write(const bt::Uint8* buf, Uint32 buf_size, Uint32 off) { if(off + buf_size > len || !ptr) return 0; if(read_only) throw bt::Error(i18n("Unable to write to a piece mapped read only")); #ifndef Q_WS_WIN BUS_ERROR_WPROTECT(); #endif memcpy(ptr + off, buf, buf_size); return buf_size; } Uint32 PieceData::read(Uint8* buf, Uint32 to_read, Uint32 off) { if(off + to_read > len || !ptr) return 0; #ifndef Q_WS_WIN BUS_ERROR_RPROTECT(); #endif memcpy(buf, ptr + off, to_read); return to_read; } Uint32 PieceData::writeToFile(File& file, Uint32 size, Uint32 off) { if(off + size > len || !ptr) return 0; #ifndef Q_WS_WIN BUS_ERROR_RPROTECT(); #endif return file.write(ptr + off, size); } Uint32 PieceData::readFromFile(File& file, Uint32 size, Uint32 off) { if(off + size > len || !ptr) return 0; if(read_only) throw bt::Error(i18n("Unable to write to a piece mapped read only")); #ifndef Q_WS_WIN BUS_ERROR_WPROTECT(); #endif return file.read(ptr + off, size); } void PieceData::updateHash(SHA1HashGen& hg) { if(!ptr) return; #ifndef Q_WS_WIN BUS_ERROR_RPROTECT(); #endif hg.update(ptr, len); } SHA1Hash PieceData::generateHash() const { if(!ptr) return SHA1Hash(); #ifndef Q_WS_WIN BUS_ERROR_RPROTECT(); #endif return SHA1Hash::generate(ptr, len); } void PieceData::unmapped() { ptr = 0; } } diff --git a/src/download/chunkdownload.h b/src/download/chunkdownload.h index a021f36..39fbd79 100644 --- a/src/download/chunkdownload.h +++ b/src/download/chunkdownload.h @@ -1,234 +1,234 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BTCHUNKDOWNLOAD_H #define BTCHUNKDOWNLOAD_H #include #include #include #include #include #include #include #include #include namespace bt { class File; class Chunk; class Piece; class Peer; class Request; class PieceDownloader; struct ChunkDownloadHeader { Uint32 index; Uint32 num_bits; Uint32 buffered; }; struct PieceHeader { Uint32 piece; Uint32 size; Uint32 mapped; }; class DownloadStatus { public: DownloadStatus(); ~DownloadStatus(); void add(Uint32 p); void remove(Uint32 p); bool contains(Uint32 p); void clear(); void timeout() {timeouts++;} Uint32 numTimeouts() const {return timeouts;} typedef QSet::iterator iterator; iterator begin() {return status.begin();} iterator end() {return status.end();} private: Uint32 timeouts; QSet status; }; /** * @author Joris Guisson * @brief Handles the download off one Chunk off a Peer * * This class handles the download of one Chunk. */ class KTORRENT_EXPORT ChunkDownload : public QObject,public ChunkDownloadInterface { Q_OBJECT public: /** * Constructor, set the chunk and the PeerManager. * @param chunk The Chunk */ ChunkDownload(Chunk* chunk); virtual ~ChunkDownload(); /// Get the chunk Chunk* getChunk() {return chunk;} /// Get the total number of pieces Uint32 getTotalPieces() const {return num;} /// Get the number of pieces downloaded Uint32 getPiecesDownloaded() const {return num_downloaded;} /// Get the number of bytes downloaded. Uint32 bytesDownloaded() const; /// Get the index of the chunk Uint32 getChunkIndex() const; /// Get the PeerID of the current peer QString getPieceDownloaderName() const; /// Get the download speed Uint32 getDownloadSpeed() const; /// Get download stats void getStats(Stats & s); /// See if a chunkdownload is idle (i.e. has no downloaders) bool isIdle() const {return pdown.count() == 0;} /** * A Piece has arived. * @param p The Piece * @param ok Whether or not the piece was needed * @return true If Chunk is complete */ bool piece(const Piece & p,bool & ok); /** * Assign the downloader to download from. * @param pd The downloader * @return true if the peer was asigned, false if not */ bool assign(PieceDownloader* pd); /** * Release a downloader * @param pd The downloader */ void release(PieceDownloader* pd); /** * A PieceDownloader has been killed. We need to remove it. * @param pd The PieceDownloader */ void killed(PieceDownloader* pd); /** * Save to a File * @param file The File */ void save(File & file); /** * Load from a File * @param file The File * @param hdr Header for the chunk - * @param update_hash Wether or not to update the hash + * @param update_hash Whether or not to update the hash */ bool load(File & file,ChunkDownloadHeader & hdr,bool update_hash = true); /** * Cancel all requests. */ void cancelAll(); /** * When a Chunk is downloaded, this function checks if all * pieces are delivered by the same peer and if so returns it. * @return The PieceDownloader or 0 if there is no only peer */ PieceDownloader* getOnlyDownloader(); /// See if a PieceDownloader is assigned to this chunk bool containsPeer(PieceDownloader *pd) {return pdown.contains(pd);} /// See if the download is choked (i.e. all downloaders are choked) bool isChoked() const; /// Release all PD's and clear the requested chunks void releaseAllPDs(); /// Send requests to peers void update(); /// See if this CD hasn't been active in the last update bool needsToBeUpdated() const {return timer.getElapsedSinceUpdate() > 60 * 1000;} /// Get the SHA1 hash of the downloaded chunk SHA1Hash getHash() const {return hash_gen.get();} /// Get the number of downloaders Uint32 getNumDownloaders() const {return pdown.count();} private Q_SLOTS: void onTimeout(const bt::Request & r); void onRejected(const bt::Request & r); private: void notDownloaded(const Request & r,bool reject); void updateHash(); void sendRequests(); bool sendRequest(PieceDownloader* pd); void sendCancels(PieceDownloader* pd); void endgameCancel(const Piece & p); Uint32 bestPiece(PieceDownloader* pd); private: BitSet pieces; Chunk* chunk; Uint32 num; Uint32 num_downloaded; Uint32 last_size; Timer timer; QList pdown; PtrMap dstatus; QSet piece_providers; PieceData::Ptr* piece_data; SHA1HashGen hash_gen; Uint32 num_pieces_in_hash; friend File & operator << (File & out,const ChunkDownload & cd); friend File & operator >> (File & in,ChunkDownload & cd); }; } #endif diff --git a/src/download/httpresponseheader.h b/src/download/httpresponseheader.h index ec65eb1..0e62387 100644 --- a/src/download/httpresponseheader.h +++ b/src/download/httpresponseheader.h @@ -1,41 +1,41 @@ /*************************************************************************** * Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). * * Contact: http://www.qt-project.org/legal * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include class HttpResponseHeader { public: HttpResponseHeader(const QString &str){parse(str);} int statusCode(){return _statCode;} QString reasonPhrase(){return _reasonPhr;} QString value(const QString &key)const {return values[key.toLower()];} bool hasKey(const QString &key)const {return values.contains(key.toLower());} private: bool parse(const QString &); bool parseLine(const QString &line, int number); QMap values; int _majVer; int _minVer; - int _statCode; + int _statCode; QString _reasonPhr; -}; \ No newline at end of file +}; diff --git a/src/download/piece.cpp b/src/download/piece.cpp index 2ccac58..cba5fc4 100644 --- a/src/download/piece.cpp +++ b/src/download/piece.cpp @@ -1,35 +1,35 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "piece.h" #if 0 namespace bt { Piece::Piece(Uint32 index, Uint32 off, Uint32 len, PieceDownloader* pd,const Uint8* data) : Request(index, off, len, pd),data(data) {} Piece::~Piece() {} } -#endif \ No newline at end of file +#endif diff --git a/src/download/request.cpp b/src/download/request.cpp index 6277ef3..f4854bc 100644 --- a/src/download/request.cpp +++ b/src/download/request.cpp @@ -1,50 +1,50 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "request.h" #include #if 0 namespace bt { Request::Request() : index(0),off(0),len(0),pd(0) {} Request::Request(Uint32 index,Uint32 off,Uint32 len,PieceDownloader* pd) : index(index),off(off),len(len),pd(pd) {} Request::Request(const Request & r) : index(r.index),off(r.off),len(r.len),pd(r.pd) {} Request::~Request() {} Request & Request::operator = (const Request & r) { index = r.index; off = r.off; len = r.len; pd = r.pd; return *this; } } -#endif \ No newline at end of file +#endif diff --git a/src/interfaces/chunkselectorinterface.cpp b/src/interfaces/chunkselectorinterface.cpp index 953c83f..8680d5f 100644 --- a/src/interfaces/chunkselectorinterface.cpp +++ b/src/interfaces/chunkselectorinterface.cpp @@ -1,53 +1,52 @@ /*************************************************************************** * Copyright (C) 2008 by Joris Guisson and Ivan Vasic * * joris.guisson@gmail.com * * ivasic@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "chunkselectorinterface.h" #include -#include namespace bt { ChunkSelectorInterface::ChunkSelectorInterface() : cman(0),downer(0),pman(0) { } ChunkSelectorInterface::~ChunkSelectorInterface() { } void ChunkSelectorInterface::init(bt::ChunkManager* cman, bt::Downloader* downer, bt::PeerManager* pman) { this->cman = cman; this->downer = downer; this->pman = pman; } bool ChunkSelectorInterface::selectRange(Uint32 & from,Uint32 & to,Uint32 max_len) { Q_UNUSED(from); Q_UNUSED(to); Q_UNUSED(max_len); return false; } } diff --git a/src/interfaces/monitorinterface.h b/src/interfaces/monitorinterface.h index 863ebc5..dc86931 100644 --- a/src/interfaces/monitorinterface.h +++ b/src/interfaces/monitorinterface.h @@ -1,95 +1,95 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BTMONITORINTERFACE_H #define BTMONITORINTERFACE_H #include namespace bt { class ChunkDownloadInterface; class PeerInterface; class TorrentFileInterface; /** * @author Joris Guisson * @brief Interface for classes who want to monitor a TorrentInterface * * Classes who want to keep track of all peers currently connected for a given * torrent and all chunks who are currently downloading can implement this interface. */ class KTORRENT_EXPORT MonitorInterface { public: MonitorInterface(); virtual ~MonitorInterface(); /** * A peer has been added. * @param peer The peer */ virtual void peerAdded(PeerInterface* peer) = 0; /** * A peer has been removed. * @param peer The peer */ virtual void peerRemoved(PeerInterface* peer) = 0; /** * The download of a chunk has been started. * @param cd The ChunkDownload */ virtual void downloadStarted(ChunkDownloadInterface* cd) = 0; /** * The download of a chunk has been stopped. * @param cd The ChunkDownload */ virtual void downloadRemoved(ChunkDownloadInterface* cd) = 0; /** * The download has been stopped. */ virtual void stopped() = 0; /** * The download has been deleted. */ virtual void destroyed() = 0; /** * The download percentage of a file has changed. * @param file The file * @param percentage The percentage */ virtual void filePercentageChanged(TorrentFileInterface* file,float percentage) = 0; /** * Preview status of a file has changed. * @param file The file - * @param preview Wether or not it is available + * @param preview Whether or not it is available */ virtual void filePreviewChanged(TorrentFileInterface* file,bool preview) = 0; }; } #endif diff --git a/src/interfaces/peerinterface.cpp b/src/interfaces/peerinterface.cpp index 2786afa..cf9145e 100644 --- a/src/interfaces/peerinterface.cpp +++ b/src/interfaces/peerinterface.cpp @@ -1,59 +1,59 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "peerinterface.h" +#include namespace bt { PeerInterface::PeerInterface(const PeerID & peer_id, Uint32 num_chunks) : peer_id(peer_id),pieces(num_chunks) { stats.interested = false; stats.am_interested = false; stats.choked = true; stats.interested = false; stats.am_interested = false; stats.download_rate = 0; stats.upload_rate = 0; stats.perc_of_file = 0; stats.snubbed = false; stats.dht_support = false; stats.fast_extensions = false; stats.extension_protocol = false; stats.bytes_downloaded = stats.bytes_uploaded = 0; stats.aca_score = 0.0; stats.has_upload_slot = false; stats.num_up_requests = stats.num_down_requests = 0; stats.encrypted = false; stats.local = false; stats.max_request_queue = 0; stats.time_choked = CurrentTime(); stats.time_unchoked = 0; stats.partial_seed = false; killed = false; paused = false; } PeerInterface::~PeerInterface() {} } diff --git a/src/interfaces/peerinterface.h b/src/interfaces/peerinterface.h index 478142d..7361968 100644 --- a/src/interfaces/peerinterface.h +++ b/src/interfaces/peerinterface.h @@ -1,172 +1,172 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BTPEERINTERFACE_H #define BTPEERINTERFACE_H #include #include #include #include namespace bt { /** * @author Joris Guisson * @brief Interface for a Peer * * This is the interface for a Peer, it allows other classes to - * get statistics about a Peer, and provides some basic funtionality provided by a Peer. + * get statistics about a Peer, and provides some basic functionality provided by a Peer. */ class KTORRENT_EXPORT PeerInterface { public: /** Constructor, initialize the PeerID and the number of chunks @param peer_id The PeerID @param num_chunks The number of chunks */ PeerInterface(const PeerID & peer_id, Uint32 num_chunks); virtual ~PeerInterface(); struct Stats { /// IP address of peer (dotted notation) QString ip_address; /// Host name of the peer QString hostname; /// The client (Azureus, BitComet, ...) QString client; /// Download rate (bytes/s) bt::Uint32 download_rate; /// Upload rate (bytes/s) bt::Uint32 upload_rate; /// Choked or not bool choked; /// Snubbed or not (i.e. we haven't received a piece for a minute) bool snubbed; /// Percentage of file which the peer has float perc_of_file; /// Does this peer support DHT bool dht_support; /// Amount of data uploaded bt::Uint64 bytes_uploaded; /// Amount of data downloaded bt::Uint64 bytes_downloaded; /// Advanced choke algorithm score double aca_score; /// Flag to indicate if this peer has an upload slot bool has_upload_slot; /// Is the peer interested bool interested; /// Am I interested in the peer bool am_interested; /// Whether or not this connection is encrypted bool encrypted; /// Number of upload requests queued bt::Uint32 num_up_requests; /// Number of outstanding download requests queued bt::Uint32 num_down_requests; /// Supports the fast extensions bool fast_extensions; /// Is this a peer on the local network bool local; /// Whether or not the peer supports the extension protocol bool extension_protocol; /// Max number of outstanding requests (reqq in extended protocol handshake) bt::Uint32 max_request_queue; /// Time the peer choked us TimeStamp time_choked; /// Time the peer unchoked us TimeStamp time_unchoked; /// The transport protocol used by the peer bt::TransportProtocol transport_protocol; /// Is this a partial seed bool partial_seed; /// Get the address of the peer (hostname if it is valid, IP otherwise) QString address() const {return hostname.isEmpty() ? ip_address : hostname;} }; /// Get the Peer's statistics const Stats & getStats() const {return stats;} /** Kill the Peer, will ensure the PeerManager closes the connection, and cleans things up. */ virtual void kill() = 0; /// See if the peer has been killed. bool isKilled() const {return killed;} /** Get the average download speed since the last unchoke in bytes/sec */ virtual bt::Uint32 averageDownloadSpeed() const = 0; /// Get the Peer's BitSet const BitSet & getBitSet() const {return pieces;} /// Get the Peer's ID const PeerID & getPeerID() const {return peer_id;} /// Is the Peer choked bool isChoked() const {return stats.choked;} /// Is the Peer interested bool isInterested() const {return stats.interested;} /// Are we interested in the Peer bool areWeInterested() const {return stats.am_interested;} /// Are we choked for the Peer bool areWeChoked() const {return !stats.has_upload_slot || paused;} /// See if the peer supports DHT bool isDHTSupported() const {return stats.dht_support;} /// Get the time when this Peer choked us TimeStamp getChokeTime() const {return stats.time_choked;} /// Get the time when this Peer unchoked us TimeStamp getUnchokeTime() const {return stats.time_unchoked;} /// See if the peer is a seeder. bool isSeeder() const {return pieces.allOn();} /// Peer is allowed to download chunk (used for superseeding) virtual void chunkAllowed(bt::Uint32 chunk) = 0; /// Handle a received packet virtual void handlePacket(const bt::Uint8* packet, bt::Uint32 size) = 0; protected: mutable PeerInterface::Stats stats; bool paused; bool killed; PeerID peer_id; BitSet pieces; }; } #endif diff --git a/src/interfaces/serverinterface.cpp b/src/interfaces/serverinterface.cpp index 6bcdf9b..f55f8f1 100644 --- a/src/interfaces/serverinterface.cpp +++ b/src/interfaces/serverinterface.cpp @@ -1,170 +1,170 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "serverinterface.h" #include #include #include #include #include #include #include #include #include #include #include namespace bt { QList ServerInterface::peer_managers; bool ServerInterface::encryption = false; bool ServerInterface::allow_unencrypted = true; Uint16 ServerInterface::port = 6881; bool ServerInterface::utp_enabled = false; bool ServerInterface::only_use_utp = false; TransportProtocol ServerInterface::primary_transport_protocol = TCP; ServerInterface::ServerInterface(QObject* parent): QObject(parent) { } ServerInterface::~ServerInterface() { } void ServerInterface::addPeerManager(PeerManager* pman) { peer_managers.append(pman); } void ServerInterface::removePeerManager(PeerManager* pman) { peer_managers.removeAll(pman); } PeerManager* ServerInterface::findPeerManager(const bt::SHA1Hash& hash) { QList::iterator i = peer_managers.begin(); while (i != peer_managers.end()) { PeerManager* pm = *i; if (pm && pm->getTorrent().getInfoHash() == hash) { if (!pm->isStarted()) return 0; else return pm; } ++i; } return 0; } bool ServerInterface::findInfoHash(const bt::SHA1Hash& skey, SHA1Hash& info_hash) { Uint8 buf[24]; memcpy(buf,"req2",4); QList::iterator i = peer_managers.begin(); while (i != peer_managers.end()) { PeerManager* pm = *i; memcpy(buf+4,pm->getTorrent().getInfoHash().getData(),20); if (SHA1Hash::generate(buf,24) == skey) { info_hash = pm->getTorrent().getInfoHash(); return true; } ++i; } return false; } void ServerInterface::disableEncryption() { encryption = false; } void ServerInterface::enableEncryption(bool unencrypted_allowed) { encryption = true; allow_unencrypted = unencrypted_allowed; } QStringList ServerInterface::bindAddresses() { QString iface = NetworkInterface(); QStringList ips = NetworkInterfaceIPAddresses(iface); if (ips.count() == 0) { - // Interface does not exist, so add any adresses + // Interface does not exist, so add any addresses ips << QHostAddress(QHostAddress::AnyIPv6).toString() << QHostAddress(QHostAddress::Any).toString(); } return ips; } void ServerInterface::newConnection(mse::EncryptedPacketSocket::Ptr s) { if (peer_managers.count() == 0) { s->close(); } else { if (!AccessManager::instance().allowed(s->getRemoteAddress())) { Out(SYS_CON|LOG_DEBUG) << "A client with a blocked IP address ("<< s->getRemoteIPAddress() << ") tried to connect !" << endl; return; } // Not enough free file descriptors if (!OpenFileAllowed()) return; ServerAuthenticate* auth = 0; if (encryption) auth = new mse::EncryptedServerAuthenticate(s); else auth = new ServerAuthenticate(s); AuthenticationMonitor::instance().add(auth); } } void ServerInterface::setPrimaryTransportProtocol(TransportProtocol proto) { primary_transport_protocol = proto; } void ServerInterface::setUtpEnabled(bool on, bool only_utp) { utp_enabled = on; only_use_utp = only_utp; } } diff --git a/src/interfaces/trackerinterface.cpp b/src/interfaces/trackerinterface.cpp index 35d0817..0e548b1 100644 --- a/src/interfaces/trackerinterface.cpp +++ b/src/interfaces/trackerinterface.cpp @@ -1,67 +1,67 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "trackerinterface.h" +#include namespace bt { TrackerInterface::TrackerInterface(const QUrl &url) : url(url) { // default 5 minute interval interval = 5 * 60 * 1000; seeders = leechers = total_downloaded = -1; enabled = true; started = false; status = TRACKER_IDLE; } TrackerInterface::~TrackerInterface() { } void TrackerInterface::reset() { interval = 5 * 60 * 1000; status = TRACKER_IDLE; } Uint32 TrackerInterface::timeToNextUpdate() const { if (!enabled || !isStarted()) return 0; else return interval - request_time.secsTo(QDateTime::currentDateTime()); } QString TrackerInterface::trackerStatusString() const { switch (status) { case TRACKER_OK: return warning.isEmpty() ? i18n("OK") : i18n("Warning: %1", warning); case TRACKER_ANNOUNCING: return i18n("Announcing"); case TRACKER_ERROR: return i18n("Error: %1",error); case TRACKER_IDLE: default: return QString(); } } } diff --git a/src/interfaces/webseedinterface.h b/src/interfaces/webseedinterface.h index 8198cc8..8b121ca 100644 --- a/src/interfaces/webseedinterface.h +++ b/src/interfaces/webseedinterface.h @@ -1,71 +1,71 @@ /*************************************************************************** * Copyright (C) 2008 by Joris Guisson and Ivan Vasic * * joris.guisson@gmail.com * * ivasic@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BTWEBSEEDINTERFACE_H #define BTWEBSEEDINTERFACE_H #include #include #include namespace bt { /** Interface for WebSeeds */ class KTORRENT_EXPORT WebSeedInterface { public: WebSeedInterface(const QUrl &url,bool user); virtual ~WebSeedInterface(); /// Disable or enable the webseed virtual void setEnabled(bool on); - /// Wether or not the webseed is enabled + /// Whether or not the webseed is enabled bool isEnabled() const {return enabled;} /// Get the URL of the webseed const QUrl &getUrl() const {return url;} /// Get how much data was downloaded Uint64 getTotalDownloaded() const {return total_downloaded;} /// Get the present status in string form QString getStatus() const {return status;} /// Get the current download rate in bytes per sec virtual Uint32 getDownloadRate() const = 0; /// Whether or not this webseed was user created bool isUserCreated() const {return user;} protected: QUrl url; Uint64 total_downloaded; QString status; bool user; bool enabled; }; } #endif diff --git a/src/magnet/magnetlink.h b/src/magnet/magnetlink.h index 7868adc..5fc3b6c 100644 --- a/src/magnet/magnetlink.h +++ b/src/magnet/magnetlink.h @@ -1,93 +1,93 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BT_MAGNETLINK_H #define BT_MAGNETLINK_H #include #include #include namespace bt { /** MagnetLink class magnet links have the format: magnet:?xt=urn:btih:info_hash&dn=name&tr=tracker-url[,tracker-url...] - note: a comma-seperated list will not work with other clients likely + note: a comma-separated list will not work with other clients likely optional parameters are to=torrent-file-url (need not be valid) pt=path-to-download-in-torrent */ class KTORRENT_EXPORT MagnetLink { friend class MagnetDownloader; public: MagnetLink(); MagnetLink(const MagnetLink & mlink); MagnetLink(const QUrl& mlink); MagnetLink(const QString & mlink); ~MagnetLink(); /// Assignment operator MagnetLink & operator = (const MagnetLink & mlink); /// Equality operator bool operator == (const MagnetLink & mlink) const; /// Is this a valid magnet link bool isValid() const {return !magnet_string.isEmpty();} /// Convert it to a string QString toString() const {return magnet_string;} /// Get the display name (can be empty) QString displayName() const {return name;} /// Get the path of addressed file(s) inside the torrent QString subPath() const {return path;} /// Get the torrent URL (can be empty) QString torrent() const {return torrent_url;} /// Get all possible trackers (can be empty) QList trackers() const {return tracker_urls;} /// Get the info hash const SHA1Hash & infoHash() const {return info_hash;} private: void parse(const QUrl& mlink); Uint8 charToHex(const QChar & ch); QString base32ToHexString(const QString &s); private: QString magnet_string; SHA1Hash info_hash; QString torrent_url; QList tracker_urls; QString path; QString name; }; } #endif // BT_MAGNETLINK_H diff --git a/src/mse/rc4encryptor.cpp b/src/mse/rc4encryptor.cpp index 9710857..ecce0d4 100644 --- a/src/mse/rc4encryptor.cpp +++ b/src/mse/rc4encryptor.cpp @@ -1,67 +1,67 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "rc4encryptor.h" #include #include -#include "rc4encryptor.h" using namespace bt; namespace mse { static Uint8 rc4_enc_buffer[bt::MAX_MSGLEN]; RC4Encryptor::RC4Encryptor(const bt::SHA1Hash & dk,const bt::SHA1Hash & ek) { gcry_cipher_open(&enc,GCRY_CIPHER_ARCFOUR,GCRY_CIPHER_MODE_STREAM,0); gcry_cipher_setkey(enc,ek.getData(),20); gcry_cipher_open(&dec,GCRY_CIPHER_ARCFOUR,GCRY_CIPHER_MODE_STREAM,0); gcry_cipher_setkey(dec,dk.getData(),20); Uint8 tmp[1024]; gcry_cipher_encrypt(enc,tmp,1024,tmp,1024); gcry_cipher_decrypt(dec,tmp,1024,tmp,1024); } RC4Encryptor::~RC4Encryptor() { gcry_cipher_close(enc); gcry_cipher_close(dec); } void RC4Encryptor::decrypt(Uint8* data,Uint32 len) { gcry_cipher_decrypt(dec,data,len,data,len); } const Uint8* RC4Encryptor::encrypt(const Uint8* data,Uint32 len) { gcry_cipher_encrypt(enc,rc4_enc_buffer,len,data,len); return rc4_enc_buffer; } void RC4Encryptor::encryptReplace(Uint8* data,Uint32 len) { gcry_cipher_encrypt(enc,data,len,data,len); } } diff --git a/src/net/reverseresolver.cpp b/src/net/reverseresolver.cpp index 45497d1..f6f7e6f 100644 --- a/src/net/reverseresolver.cpp +++ b/src/net/reverseresolver.cpp @@ -1,144 +1,144 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "reverseresolver.h" #include #include -#include "reverseresolver.h" namespace net { ReverseResolverThread* ReverseResolver::worker = 0; ReverseResolver::ReverseResolver(QObject* parent): QObject(parent) { } ReverseResolver::~ReverseResolver() { } void ReverseResolver::resolveAsync(const net::Address& addr) { addr_to_resolve = addr; if (!worker) { worker = new ReverseResolverThread(); worker->add(this); worker->start(); } else { worker->add(this); } } QString ReverseResolver::resolve(const net::Address& addr) { struct sockaddr_storage ss; int slen = 0; addr.toSocketAddress(&ss, slen); char host[200]; char service[200]; memset(host,0,200); memset(service,0,200); if (getnameinfo((struct sockaddr*)&ss,slen,host,199,service,199,NI_NAMEREQD) == 0) return QString::fromUtf8(host); else return QString(); } void ReverseResolver::run() { QString res = resolve(addr_to_resolve); emit resolved(res); } void ReverseResolver::shutdown() { if (worker) { worker->stop(); worker->wait(); delete worker; worker = 0; } } ReverseResolverThread::ReverseResolverThread() : stopped(false) { } ReverseResolverThread::~ReverseResolverThread() { } void ReverseResolverThread::add(ReverseResolver* rr) { mutex.lock(); todo_list.append(rr); mutex.unlock(); more_data.wakeOne(); } void ReverseResolverThread::run() { while (!stopped) { mutex.lock(); if (!todo_list.empty()) { ReverseResolver* rr = todo_list.first(); todo_list.pop_front(); mutex.unlock(); rr->run(); rr->deleteLater(); } else { more_data.wait(&mutex); mutex.unlock(); } } // cleanup if necessary foreach (ReverseResolver* rr,todo_list) rr->deleteLater(); todo_list.clear(); } void ReverseResolverThread::stop() { stopped = true; // wake up the thread if it is sleeping more_data.wakeOne(); } } diff --git a/src/net/streamsocket.cpp b/src/net/streamsocket.cpp index befd491..b69a609 100644 --- a/src/net/streamsocket.cpp +++ b/src/net/streamsocket.cpp @@ -1,87 +1,87 @@ /*************************************************************************** * Copyright (C) 2011 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "streamsocket.h" #include "socketmonitor.h" namespace net { StreamSocket::StreamSocket(bool tcp, int ip_version, StreamSocketListener* listener) : TrafficShapedSocket(tcp, ip_version), listener(listener) { } StreamSocket::~StreamSocket() { } void StreamSocket::addData(const QByteArray& data) { QMutexLocker lock(&mutex); buffer.append(data); net::SocketMonitor::instance().signalPacketReady(); } bool StreamSocket::bytesReadyToWrite() const { QMutexLocker lock(&mutex); return !buffer.isEmpty() || sock->state() == net::SocketDevice::CONNECTING; } bt::Uint32 StreamSocket::write(bt::Uint32 max, bt::TimeStamp now) { Q_UNUSED(now); QMutexLocker lock(&mutex); if (sock->state() == net::SocketDevice::CONNECTING) { bool ok = sock->connectSuccesFull(); if (listener) listener->connectFinished(ok); if (!ok) return 0; } if (buffer.isEmpty()) return 0; int to_send = qMin(buffer.size(), max); int ret = sock->send((const bt::Uint8*)buffer.data(), to_send); if (ret == to_send) { buffer.clear(); if (listener) listener->dataSent(); return ret; } else if (ret > 0) { buffer = buffer.mid(ret); return ret; } else { return 0; } } -} \ No newline at end of file +} diff --git a/src/net/wakeuppipe.cpp b/src/net/wakeuppipe.cpp index eff8d82..88c17e6 100644 --- a/src/net/wakeuppipe.cpp +++ b/src/net/wakeuppipe.cpp @@ -1,68 +1,68 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "net/wakeuppipe.h" #include #include -#include "net/wakeuppipe.h" using namespace bt; namespace net { WakeUpPipe::WakeUpPipe() : woken_up(false) { } WakeUpPipe::~WakeUpPipe() { } void WakeUpPipe::wakeUp() { QMutexLocker lock(&mutex); if (woken_up) return; char data[] = "d"; if (bt::Pipe::write((const bt::Uint8*)data,1) != 1) Out(SYS_GEN|LOG_DEBUG) << "WakeUpPipe: wake up failed " << endl; else woken_up = true; } void WakeUpPipe::handleData() { QMutexLocker lock(&mutex); bt::Uint8 buf[20]; int ret = bt::Pipe::read(buf,20); if (ret < 0) Out(SYS_GEN|LOG_DEBUG) << "WakeUpPipe: read failed " << endl; woken_up = false; } void WakeUpPipe::reset() { // Do nothing } } diff --git a/src/peer/accessmanager.h b/src/peer/accessmanager.h index 1bcb693..224b826 100644 --- a/src/peer/accessmanager.h +++ b/src/peer/accessmanager.h @@ -1,75 +1,75 @@ /*************************************************************************** * Copyright (C) 2008 by Joris Guisson and Ivan Vasic * * joris.guisson@gmail.com * * ivasic@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BTACCESSMANAGER_H #define BTACCESSMANAGER_H #include #include #include namespace bt { class BlockListInterface; class BadPeersList; /** @author Joris Guisson - Class which determines wether or not we allow an IP to connect to us. + Class which determines whether or not we allow an IP to connect to us. It uses blocklists to do this. Blocklists should register with this class. By default it has one blocklist, the banned peers list. */ class KTORRENT_EXPORT AccessManager { AccessManager(); public: virtual ~AccessManager(); /// Get the singleton instance static AccessManager& instance(); /// Add a blocklist (AccessManager takes ownership unless list is explicitly remove with removeBlockList) void addBlockList(BlockListInterface* bl); /// Remove a blocklist void removeBlockList(BlockListInterface* bl); /// Are we allowed to have a connection with a peer bool allowed(const net::Address& addr) const; /// Ban a peer (i.e. add it to the banned list) void banPeer(const QString& addr); /// Add an external IP throuch which we are reacheable void addExternalIP(const QString& addr); private: bool isOurOwnAddress(const net::Address& addr) const; private: QList blocklists; BadPeersList* banned; QStringList external_addresses; }; } #endif diff --git a/src/peer/badpeerslist.cpp b/src/peer/badpeerslist.cpp index 3bfea26..a7e8e84 100644 --- a/src/peer/badpeerslist.cpp +++ b/src/peer/badpeerslist.cpp @@ -1,47 +1,47 @@ /*************************************************************************** * Copyright (C) 2008 by Joris Guisson and Ivan Vasic * * joris.guisson@gmail.com * * ivasic@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "badpeerslist.h" +#include namespace bt { BadPeersList::BadPeersList() { } BadPeersList::~BadPeersList() { } bool BadPeersList::blocked(const net::Address & addr) const { return bad_peers.contains(addr.toString()); } void BadPeersList::addBadPeer(const QString & ip) { bad_peers << ip; } } diff --git a/src/peer/peer.cpp b/src/peer/peer.cpp index 72d7a38..4c6b181 100644 --- a/src/peer/peer.cpp +++ b/src/peer/peer.cpp @@ -1,942 +1,942 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "peer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "packetreader.h" #include "peerdownloader.h" #include "peeruploader.h" #include "utpex.h" #include "peermanager.h" #include #include "utmetadata.h" using namespace net; namespace bt { static const int MAX_METADATA_SIZE = 4 * 1024 * 1024; // maximum allowed metadata_size (up to 4 MiB) static Uint32 peer_id_counter = 1; bool Peer::resolve_hostname = true; Peer::Peer(mse::EncryptedPacketSocket::Ptr sock, const PeerID & peer_id, Uint32 num_chunks, Uint32 chunk_size, Uint32 support, bool local, ConnectionLimit::Token::Ptr token, PeerManager* pman) : PeerInterface(peer_id, num_chunks), sock(sock), token(token), pman(pman) { id = peer_id_counter; peer_id_counter++; ut_pex_id = 0; Uint32 max_packet_len = 9 + MAX_PIECE_LEN; // size of piece packet Uint32 bitfield_length = num_chunks / 8 + 1 + (num_chunks % 8 == 0 ? 0 : 1); if (bitfield_length > max_packet_len) // bitfield can be longer max_packet_len = bitfield_length; // to be future proof use 10 times the max packet length, // in case some extension message comes along which is larger preader = new PacketReader(10*max_packet_len); downloader = new PeerDownloader(this, chunk_size); uploader = new PeerUploader(this); stalled_timer.update(); connect_time = QTime::currentTime(); stats.client = peer_id.identifyClient(); stats.ip_address = getIPAddresss(); stats.dht_support = support & DHT_SUPPORT; stats.fast_extensions = support & FAST_EXT_SUPPORT; stats.extension_protocol = support & EXT_PROT_SUPPORT; stats.encrypted = sock->encrypted(); stats.local = local; stats.transport_protocol = sock->socketDevice()->transportProtocol(); if (stats.ip_address == QStringLiteral("0.0.0.0")) { Out(SYS_CON | LOG_DEBUG) << "No more 0.0.0.0" << endl; kill(); } else { sock->startMonitoring(preader); } pex_allowed = stats.extension_protocol; extensions.setAutoDelete(true); if (resolve_hostname) { net::ReverseResolver* res = new net::ReverseResolver(); connect(res, &net::ReverseResolver::resolved, this, &Peer::resolved, Qt::QueuedConnection); res->resolveAsync(sock->getRemoteAddress()); } } Peer::~Peer() { // Seeing that we are going to delete the preader object, // call stopMonitoring, in some situations it is possible that the socket // is only deleted later because the authenticate object still has a reference to it sock->stopMonitoring(); delete uploader; delete downloader; delete preader; } void Peer::closeConnection() { sock->close(); } void Peer::kill() { sock->close(); killed = true; token.clear(); } void Peer::handleChoke(Uint32 len) { if (len != 1) { kill(); return; } if (!stats.choked) stats.time_choked = CurrentTime(); stats.choked = true; downloader->choked(); } void Peer::handleUnchoke(Uint32 len) { if (len != 1) { Out(SYS_CON | LOG_DEBUG) << "len err UNCHOKE" << endl; kill(); return; } if (stats.choked) { stats.time_unchoked = CurrentTime(); bytes_downloaded_since_unchoke = 0; } stats.choked = false; } void Peer::handleInterested(Uint32 len) { if (len != 1) { Out(SYS_CON | LOG_DEBUG) << "len err INTERESTED" << endl; kill(); return; } if (!stats.interested) { stats.interested = true; pman->rerunChoker(); } } void Peer::handleNotInterested(Uint32 len) { if (len != 1) { kill(); return; } if (stats.interested) { stats.interested = false; pman->rerunChoker(); } } void Peer::handleHave(const bt::Uint8* packet, Uint32 len) { if (len != 5) { kill(); } else { Uint32 ch = ReadUint32(packet, 1); if (ch < pieces.getNumBits()) { pman->have(this, ch); pieces.set(ch, true); } else if (pman->getTorrent().isLoaded()) { Out(SYS_CON | LOG_NOTICE) << "Received invalid have value, kicking peer" << endl; kill(); } } } void Peer::handleHaveAll(Uint32 len) { if (len != 1) { kill(); } else { pieces.setAll(true); pman->bitSetReceived(this, pieces); } } void Peer::handleHaveNone(Uint32 len) { if (len != 1) { kill(); } else { pieces.setAll(false); pman->bitSetReceived(this, pieces); } } void Peer::handleBitField(const bt::Uint8* packet, Uint32 len) { if (len != 1 + pieces.getNumBytes()) { if (pman->getTorrent().isLoaded()) kill(); } else { pieces = BitSet(packet + 1, pieces.getNumBits()); pman->bitSetReceived(this, pieces); } } void Peer::handleRequest(const bt::Uint8* packet, Uint32 len) { if (len != 13) { kill(); return; } Request r( ReadUint32(packet, 1), ReadUint32(packet, 5), ReadUint32(packet, 9), downloader); if (stats.has_upload_slot) uploader->addRequest(r); else if (stats.fast_extensions) sendReject(r); } void Peer::handlePiece(const bt::Uint8* packet, Uint32 len) { if (paused) return; if (len < 9) { kill(); return; } snub_timer.update(); stats.bytes_downloaded += (len - 9); bytes_downloaded_since_unchoke += (len - 9); Piece p(ReadUint32(packet, 1), ReadUint32(packet, 5), len - 9, downloader, packet + 9); downloader->piece(p); pman->pieceReceived(p); downloader->update(); } void Peer::handleCancel(const bt::Uint8* packet, Uint32 len) { if (len != 13) { kill(); } else { Request r(ReadUint32(packet, 1), ReadUint32(packet, 5), ReadUint32(packet, 9), downloader); uploader->removeRequest(r); sock->doNotSendPiece(r, stats.fast_extensions); } } void Peer::handleReject(const bt::Uint8* packet, Uint32 len) { if (len != 13) { kill(); } else { Request r(ReadUint32(packet, 1), ReadUint32(packet, 5), ReadUint32(packet, 9), downloader); downloader->onRejected(r); } } void Peer::handlePort(const bt::Uint8* packet, Uint32 len) { if (len != 3) { kill(); } else { Uint16 port = ReadUint16(packet, 1); // Out(SYS_CON|LOG_DEBUG) << "Got PORT packet : " << port << endl; pman->portPacketReceived(getIPAddresss(), port); } } void Peer::handlePacket(const bt::Uint8* packet, Uint32 size) { if (killed || size == 0) return; switch (packet[0]) { case CHOKE: handleChoke(size); break; case UNCHOKE: handleUnchoke(size); break; case INTERESTED: handleInterested(size); break; case NOT_INTERESTED: handleNotInterested(size); break; case HAVE: handleHave(packet, size); break; case BITFIELD: handleBitField(packet, size); break; case REQUEST: handleRequest(packet, size); break; case PIECE: handlePiece(packet, size); break; case CANCEL: handleCancel(packet, size); break; case REJECT_REQUEST: handleReject(packet, size); break; case PORT: handlePort(packet, size); break; case HAVE_ALL: handleHaveAll(size); break; case HAVE_NONE: handleHaveNone(size); break; case SUGGEST_PIECE: // ignore suggestions for the moment break; case ALLOWED_FAST: // we no longer support this, so do nothing break; case EXTENDED: handleExtendedPacket(packet, size); break; } } void Peer::pause() { if (paused) return; downloader->cancelAll(); // choke the peer and tell it we are not interested choke(); sendNotInterested(); paused = true; } void Peer::unpause() { paused = false; } void Peer::handleExtendedPacket(const Uint8* packet, Uint32 size) { if (size <= 2) return; PeerProtocolExtension* ext = extensions.find(packet[1]); if (ext) { ext->handlePacket(packet, size); } else if (packet[1] == 0) { handleExtendedHandshake(packet, size); } } void Peer::handleExtendedHandshake(const Uint8* packet, Uint32 size) { QByteArray tmp = QByteArray::fromRawData((const char*)packet, size); BNode* node = 0; try { BDecoder dec(tmp, false, 2); node = dec.decode(); if (!node || node->getType() != BNode::DICT) { delete node; return; } BDictNode* dict = (BDictNode*)node; BDictNode* mdict = dict->getDict(QByteArrayLiteral("m")); if (!mdict) { delete node; return; } BValueNode* val = 0; if ((val = mdict->getValue(QByteArrayLiteral("ut_pex"))) && UTPex::isEnabled()) { // ut_pex packet ut_pex_id = val->data().toInt(); if (ut_pex_id == 0) { extensions.erase(UT_PEX_ID); } else { PeerProtocolExtension* ext = extensions.find(UT_PEX_ID); if (ext) ext->changeID(ut_pex_id); else if (pex_allowed) extensions.insert(UT_PEX_ID, new UTPex(this, ut_pex_id)); } } if ((val = mdict->getValue(QByteArrayLiteral("ut_metadata")))) { // meta data Uint32 ut_metadata_id = val->data().toInt(); if (ut_metadata_id == 0) // disabled by other side { extensions.erase(UT_METADATA_ID); } else { PeerProtocolExtension* ext = extensions.find(UT_METADATA_ID); if (ext) { ext->changeID(ut_metadata_id); } else { int metadata_size = 0; if (dict->getValue(QByteArrayLiteral("metadata_size"))) metadata_size = dict->getInt(QByteArrayLiteral("metadata_size")); if (metadata_size > MAX_METADATA_SIZE) // check for wrong metadata_size values { Out(SYS_GEN | LOG_NOTICE) << "Wrong metadata size: " << metadata_size << ". Killing peer... " << endl; kill(); delete node; return; } UTMetaData* md = new UTMetaData(pman->getTorrent(), ut_metadata_id, this); md->setReportedMetadataSize(metadata_size); extensions.insert(UT_METADATA_ID, md); } } } if ((val = dict->getValue(QByteArrayLiteral("reqq")))) { stats.max_request_queue = val->data().toInt(); } if ((val = dict->getValue(QByteArrayLiteral("upload_only")))) { stats.partial_seed = val->data().toInt() == 1; } } catch (...) { // just ignore invalid packets Out(SYS_CON | LOG_DEBUG) << "Invalid extended packet" << endl; } delete node; } Uint32 Peer::sendData(const Uint8* data, Uint32 len) { if (killed) return 0; Uint32 ret = sock->sendData(data, len); if (!sock->ok()) kill(); return ret; } Uint32 Peer::readData(Uint8* buf, Uint32 len) { if (killed) return 0; Uint32 ret = sock->readData(buf, len); if (!sock->ok()) kill(); return ret; } Uint32 Peer::bytesAvailable() const { return sock->bytesAvailable(); } Uint32 Peer::getUploadRate() const { if (sock) return sock->getUploadRate(); else return 0; } Uint32 Peer::getDownloadRate() const { if (sock) return sock->getDownloadRate(); else return 0; } void Peer::update() { if (killed) return; if (!sock->ok() || !preader->ok()) { Out(SYS_CON | LOG_DEBUG) << "Connection closed" << endl; kill(); return; } sock->updateSpeeds(bt::CurrentTime()); preader->update(*this); Uint32 data_bytes = sock->dataBytesUploaded(); if (data_bytes > 0) { stats.bytes_uploaded += data_bytes; uploader->addUploadedBytes(data_bytes); } if (!paused) { PtrMap::iterator i = extensions.begin(); while (i != extensions.end()) { if (i->second->needsUpdate()) i->second->update(); ++i; } } - // if no data is being sent or recieved, and there are pending requests + // if no data is being sent or received, and there are pending requests // increment the connection stalled timer if (getUploadRate() > 100 || getDownloadRate() > 100 || (uploader->getNumRequests() == 0 && sock->numPendingPieceUploads() == 0 && downloader->getNumRequests() == 0)) stalled_timer.update(); stats.download_rate = this->getDownloadRate(); stats.upload_rate = this->getUploadRate(); stats.perc_of_file = this->percentAvailable(); stats.snubbed = this->isSnubbed(); stats.num_up_requests = uploader->getNumRequests() + sock->numPendingPieceUploads(); stats.num_down_requests = downloader->getNumRequests(); } bool Peer::isStalled() const { return stalled_timer.getElapsedSinceUpdate() >= 2*60*1000; } bool Peer::isSnubbed() const { // 4 minutes return snub_timer.getElapsedSinceUpdate() >= 2*60*1000 && stats.num_down_requests > 0; } QString Peer::getIPAddresss() const { if (sock) return sock->getRemoteIPAddress(); else return QString(); } Uint16 Peer::getPort() const { if (!sock) return 0; else return sock->getRemotePort(); } net::Address Peer::getAddress() const { if (!sock) return net::Address(); else return sock->getRemoteAddress(); } Uint32 Peer::getTimeSinceLastPiece() const { return snub_timer.getElapsedSinceUpdate(); } float Peer::percentAvailable() const { // calculation needs to use bytes, instead of chunks, because // the last chunk can have a different size const Torrent & tor = pman->getTorrent(); Uint64 bytes = 0; if (pieces.get(tor.getNumChunks() - 1)) bytes = tor.getChunkSize() * (pieces.numOnBits() - 1) + tor.getLastChunkSize(); else bytes = tor.getChunkSize() * pieces.numOnBits(); Uint64 tbytes = tor.getChunkSize() * (pieces.getNumBits() - 1) + tor.getLastChunkSize(); return (float)bytes / (float)tbytes * 100.0; } void Peer::setACAScore(double s) { stats.aca_score = s; } void Peer::choke() { if (!stats.has_upload_slot) return; sendChoke(); uploader->clearAllRequests(); } void Peer::emitPortPacket() { pman->portPacketReceived(sock->getRemoteIPAddress(), sock->getRemotePort()); } void Peer::emitPex(const QByteArray & data) { pman->pex(data); } void Peer::setPexEnabled(bool on) { if (!stats.extension_protocol) return; PeerProtocolExtension* ext = extensions.find(UT_PEX_ID); if (ext && (!on || !UTPex::isEnabled())) { extensions.erase(UT_PEX_ID); } else if (!ext && on && ut_pex_id > 0 && UTPex::isEnabled()) { // if the other side has enabled it to, create a new UTPex object extensions.insert(UT_PEX_ID, new UTPex(this, ut_pex_id)); } pex_allowed = on; } void Peer::sendExtProtHandshake(Uint16 port, Uint32 metadata_size, bool partial_seed) { if (!stats.extension_protocol) return; QByteArray arr; BEncoder enc(new BEncoderBufferOutput(arr)); enc.beginDict(); enc.write(QByteArrayLiteral("m")); // supported messages enc.beginDict(); enc.write(QByteArrayLiteral("ut_pex"));enc.write((Uint32)(pex_allowed ? UT_PEX_ID : 0)); enc.write(QByteArrayLiteral("ut_metadata")); enc.write(UT_METADATA_ID); enc.end(); if (port > 0) { enc.write(QByteArrayLiteral("p")); enc.write((Uint32)port); } enc.write(QByteArrayLiteral("reqq")); enc.write((bt::Uint32)250); if (metadata_size) { enc.write(QByteArrayLiteral("metadata_size")); enc.write(metadata_size); } enc.write(QByteArrayLiteral("upload_only"), partial_seed ? QByteArrayLiteral("1") : QByteArrayLiteral("0")); enc.write(QByteArrayLiteral("v")); enc.write(bt::GetVersionString().toLatin1()); enc.end(); sendExtProtMsg(0, arr); } void Peer::setGroupIDs(Uint32 up_gid, Uint32 down_gid) { sock->setGroupID(up_gid, true); sock->setGroupID(down_gid, false); } void Peer::resolved(const QString & hinfo) { stats.hostname = hinfo; } void Peer::setResolveHostnames(bool on) { resolve_hostname = on; } void Peer::emitMetadataDownloaded(const QByteArray& data) { emit metadataDownloaded(data); } bool Peer::hasWantedChunks(const bt::BitSet& wanted_chunks) const { BitSet bs = pieces; bs.andBitSet(wanted_chunks); return bs.numOnBits() > 0; } Uint32 Peer::averageDownloadSpeed() const { if (stats.choked) return 0; TimeStamp now = CurrentTime(); if (now >= getUnchokeTime()) return 0; else return bytes_downloaded_since_unchoke / (getUnchokeTime() - now); } void Peer::sendChoke() { if (!stats.has_upload_slot) return; sock->addPacket(Packet::Ptr(new Packet(CHOKE))); stats.has_upload_slot = false; } void Peer::sendUnchoke() { if (stats.has_upload_slot) return; sock->addPacket(Packet::Ptr(new Packet(UNCHOKE))); stats.has_upload_slot = true; } void Peer::sendEvilUnchoke() { sock->addPacket(Packet::Ptr(new Packet(UNCHOKE))); stats.has_upload_slot = false; } void Peer::sendInterested() { if (stats.am_interested == true) return; sock->addPacket(Packet::Ptr(new Packet(INTERESTED))); stats.am_interested = true; } void Peer::sendNotInterested() { if (stats.am_interested == false) return; sock->addPacket(Packet::Ptr(new Packet(NOT_INTERESTED))); stats.am_interested = false; } void Peer::sendRequest(const Request & r) { sock->addPacket(Packet::Ptr(new Packet(r, bt::REQUEST))); } void Peer::sendCancel(const Request & r) { sock->addPacket(Packet::Ptr(new Packet(r, bt::CANCEL))); } void Peer::sendReject(const Request & r) { sock->addPacket(Packet::Ptr(new Packet(r, bt::REJECT_REQUEST))); } void Peer::sendHave(Uint32 index) { sock->addPacket(Packet::Ptr(new Packet(index, bt::HAVE))); } void Peer::sendPort(Uint16 port) { sock->addPacket(Packet::Ptr(new Packet(port))); } void Peer::sendBitSet(const BitSet & bs) { sock->addPacket(Packet::Ptr(new Packet(bs))); } void Peer::sendHaveAll() { sock->addPacket(Packet::Ptr(new Packet(bt::HAVE_ALL))); } void Peer::sendHaveNone() { sock->addPacket(Packet::Ptr(new Packet(bt::HAVE_NONE))); } void Peer::sendSuggestPiece(Uint32 index) { sock->addPacket(Packet::Ptr(new Packet(index, bt::SUGGEST_PIECE))); } void Peer::sendAllowedFast(Uint32 index) { sock->addPacket(Packet::Ptr(new Packet(index, bt::ALLOWED_FAST))); } bool Peer::sendChunk(Uint32 index, Uint32 begin, Uint32 len, Chunk * ch) { // Out() << "sendChunk " << index << " " << begin << " " << len << endl; #ifndef NDEBUG Q_ASSERT(ch); if (!ch) { Out(SYS_CON | LOG_NOTICE) << "Warning : attempted to upload an invalid chunk" << endl; return false; } #endif if (begin >= ch->getSize() || begin + len > ch->getSize()) { Out(SYS_CON | LOG_NOTICE) << "Warning : Illegal piece request" << endl; Out(SYS_CON | LOG_NOTICE) << "\tChunk : index " << index << " size = " << ch->getSize() << endl; Out(SYS_CON | LOG_NOTICE) << "\tPiece : begin = " << begin << " len = " << len << endl; return false; } /* Out(SYS_CON|LOG_DEBUG) << QString("Uploading %1 %2 %3 %4 %5") * .arg(index).arg(begin).arg(len).arg((quint64)ch,0,16).arg((quint64)ch->getData(),0,16) * << endl;; */ sock->addPacket(Packet::Ptr(new Packet(index, begin, len, ch))); return true; } void Peer::sendExtProtMsg(Uint8 id, const QByteArray & data) { sock->addPacket(Packet::Ptr(new Packet(id, data))); } void Peer::clearPendingPieceUploads() { sock->clearPieces(stats.fast_extensions); } void Peer::chunkAllowed(Uint32 chunk) { sendHave(chunk); } } diff --git a/src/peer/peerprotocolextension.cpp b/src/peer/peerprotocolextension.cpp index fda22c4..7f7a1bd 100644 --- a/src/peer/peerprotocolextension.cpp +++ b/src/peer/peerprotocolextension.cpp @@ -1,52 +1,52 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "peerprotocolextension.h" +#include #include "peer.h" namespace bt { PeerProtocolExtension::PeerProtocolExtension(Uint32 id, Peer* peer) : id(id),peer(peer) { } PeerProtocolExtension::~PeerProtocolExtension() { } void PeerProtocolExtension::sendPacket(const QByteArray& data) { peer->sendExtProtMsg(id,data); } void PeerProtocolExtension::update() { } void PeerProtocolExtension::changeID(Uint32 id) { this->id = id; } } diff --git a/src/peer/peerprotocolextension.h b/src/peer/peerprotocolextension.h index 171f445..5328c8f 100644 --- a/src/peer/peerprotocolextension.h +++ b/src/peer/peerprotocolextension.h @@ -1,65 +1,65 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BT_PEERPROTOCOLEXTENSION_H #define BT_PEERPROTOCOLEXTENSION_H #include #include namespace bt { class Peer; const Uint32 UT_PEX_ID = 1; const Uint32 UT_METADATA_ID = 2; /** Base class for protocol extensions */ class KTORRENT_EXPORT PeerProtocolExtension { public: PeerProtocolExtension(bt::Uint32 id,Peer* peer); virtual ~PeerProtocolExtension(); - /// Virtual update function does nothing, needs to be overriden if update + /// Virtual update function does nothing, needs to be overridden if update virtual void update(); /// Does this needs to be update virtual bool needsUpdate() const {return false;} /// Handle a packet virtual void handlePacket(const bt::Uint8* packet, Uint32 size) = 0; /// Send an extension protocol packet void sendPacket(const QByteArray & data); /// Change the ID void changeID(Uint32 id); protected: bt::Uint32 id; Peer* peer; }; } #endif // BT_PEERPROTOCOLEXTENSION_H diff --git a/src/torrent/jobqueue.cpp b/src/torrent/jobqueue.cpp index 8077ed6..2ea59ea 100644 --- a/src/torrent/jobqueue.cpp +++ b/src/torrent/jobqueue.cpp @@ -1,109 +1,109 @@ /*************************************************************************** * Copyright (C) 2009 by * * Joris Guisson * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "jobqueue.h" +#include #include "job.h" #include "torrentcontrol.h" namespace bt { JobQueue::JobQueue(bt::TorrentControl* parent): QObject(parent),tc(parent),restart(false) { } JobQueue::~JobQueue() { killAll(); } void JobQueue::enqueue(Job* job) { queue.append(job); if (queue.count() == 1) startNextJob(); } bool JobQueue::runningJobs() const { return queue.count() > 0; } Job* JobQueue::currentJob() { return queue.isEmpty() ? 0 : queue.front(); } void JobQueue::startNextJob() { if (queue.isEmpty()) return; Job* j = queue.front(); connect(j,SIGNAL(result(KJob*)),this,SLOT(jobDone(KJob*))); if (j->stopTorrent() && tc->getStats().running) { // stop the torrent if the job requires it tc->pause(); restart = true; } j->start(); } void JobQueue::jobDone(KJob* job) { if (queue.isEmpty() || queue.front() != job) return; // remove the job and start the next queue.pop_front(); if (!queue.isEmpty()) { startNextJob(); } else { if (restart) { tc->unpause(); tc->allJobsDone(); restart = false; } else tc->allJobsDone(); } } void JobQueue::killAll() { if (queue.isEmpty()) return; queue.front()->kill(); qDeleteAll(queue); queue.clear(); } } diff --git a/src/torrent/torrentstats.cpp b/src/torrent/torrentstats.cpp index e55b337..d5d0490 100644 --- a/src/torrent/torrentstats.cpp +++ b/src/torrent/torrentstats.cpp @@ -1,121 +1,121 @@ /*************************************************************************** * Copyright (C) 2009 by * * Joris Guisson * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "torrentstats.h" +#include #include namespace bt { TorrentStats::TorrentStats() : imported_bytes(0) , bytes_downloaded(0) , bytes_uploaded(0) , bytes_left(0) , bytes_left_to_download(0) , total_bytes(0) , total_bytes_to_download(0) , download_rate(0) , upload_rate(0) , num_peers(0) , num_chunks_downloading(0) , total_chunks(0) , num_chunks_downloaded(0) , num_chunks_excluded(0) , num_chunks_left(0) , chunk_size(0) , seeders_total(0) , seeders_connected_to(0) , leechers_total(0) , leechers_connected_to(0) , status(NOT_STARTED) , session_bytes_downloaded(0) , session_bytes_uploaded(0) , running(false) , started(false) , queued(false) , autostart(false) , stopped_by_error(false) , completed(false) , paused(false) , auto_stopped(false) , superseeding(false) , qm_can_start(false) , multi_file_torrent(false) , priv_torrent(false) , max_share_ratio(0.0f) , max_seed_time(0.0f) , num_corrupted_chunks(0) { last_download_activity_time = last_upload_activity_time = bt::CurrentTime(); } float TorrentStats::shareRatio() const { if (bytes_downloaded == 0) return 0.0f; else return (float) bytes_uploaded / (bytes_downloaded /*+ stats.imported_bytes*/); } bool TorrentStats::overMaxRatio() const { return (completed && max_share_ratio > 0) && (shareRatio() - max_share_ratio > 0.00001); } QString TorrentStats::statusToString() const { switch (status) { case NOT_STARTED : return i18n("Not started"); case DOWNLOAD_COMPLETE : return i18n("Download completed"); case SEEDING_COMPLETE : return i18n("Seeding completed"); case SEEDING : return i18nc("Status of a torrent file", "Seeding"); case DOWNLOADING: return i18n("Downloading"); case STALLED: return i18n("Stalled"); case STOPPED: return i18n("Stopped"); case ERROR : return i18n("Error: %1",error_msg); case ALLOCATING_DISKSPACE: return i18n("Allocating diskspace"); case QUEUED: return completed ? i18n("Queued for seeding") : i18n("Queued for downloading"); case CHECKING_DATA: return i18n("Checking data"); case NO_SPACE_LEFT: return i18n("Stopped. No space left on device."); case PAUSED: return i18n("Paused"); case SUPERSEEDING: return i18n("Superseeding"); default: return QString(); } } } diff --git a/src/tracker/httptracker.cpp b/src/tracker/httptracker.cpp index 3d29b71..e8e8c1a 100644 --- a/src/tracker/httptracker.cpp +++ b/src/tracker/httptracker.cpp @@ -1,586 +1,586 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "httptracker.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "version.h" #include "kioannouncejob.h" #ifdef HAVE_HTTPANNOUNEJOB #include "httpannouncejob.h" #endif namespace bt { bool HTTPTracker::proxy_on = false; QString HTTPTracker::proxy = QString(); Uint16 HTTPTracker::proxy_port = 8080; bool HTTPTracker::use_qhttp = false; HTTPTracker::HTTPTracker(const QUrl & url, TrackerDataSource* tds, const PeerID & id, int tier) : Tracker(url, tds, id, tier) , active_job(NULL) , failures(0) , supports_partial_seed_extension(false) { interval = 5 * 60; // default interval 5 minutes connect(&timer, &QTimer::timeout, this, &HTTPTracker::onTimeout); } HTTPTracker::~HTTPTracker() { } void HTTPTracker::start() { event = QStringLiteral("started"); resetTrackerStats(); doRequest(); } void HTTPTracker::stop(WaitJob* wjob) { if (!started) { announce_queue.clear(); reannounce_timer.stop(); if (active_job) { active_job->kill(); active_job = 0; status = TRACKER_IDLE; requestOK(); } } else { reannounce_timer.stop(); event = QStringLiteral("stopped"); doRequest(wjob); started = false; } } void HTTPTracker::completed() { event = QStringLiteral("completed"); doRequest(); event = QString(); } void HTTPTracker::manualUpdate() { if (!started) start(); else doRequest(); } void HTTPTracker::scrape() { if (!url.isValid()) { Out(SYS_TRK | LOG_NOTICE) << "Invalid tracker url, canceling scrape" << endl; return; } if (!url.fileName().startsWith(QLatin1String("announce"))) { Out(SYS_TRK | LOG_NOTICE) << "Tracker " << url << " does not support scraping" << endl; return; } QUrl scrape_url = url; scrape_url.setPath(url.path(QUrl::FullyEncoded).replace(QStringLiteral("announce"), QStringLiteral("scrape")), QUrl::StrictMode); QString epq = scrape_url.query(QUrl::FullyEncoded); const SHA1Hash & info_hash = tds->infoHash(); if (epq.length()) epq += '&'; epq += QLatin1String("info_hash=") + info_hash.toURLString(); scrape_url.setQuery(epq, QUrl::StrictMode); Out(SYS_TRK | LOG_NOTICE) << "Doing scrape request to url : " << scrape_url << endl; KIO::MetaData md; setupMetaData(md); KIO::StoredTransferJob* j = KIO::storedGet(scrape_url, KIO::NoReload, KIO::HideProgressInfo); // set the meta data j->setMetaData(md); KIO::Scheduler::setJobPriority(j, 1); connect(j, &KIO::StoredTransferJob::result, this, &HTTPTracker::onScrapeResult); } void HTTPTracker::onScrapeResult(KJob* j) { if (j->error()) { Out(SYS_TRK | LOG_IMPORTANT) << "Scrape failed : " << j->errorString() << endl; return; } KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j; BDecoder dec(st->data(), false, 0); QScopedPointer n; try { n.reset(dec.decode()); } catch (bt::Error & err) { Out(SYS_TRK | LOG_IMPORTANT) << "Invalid scrape data " << err.toString() << endl; return; } if (n && n->getType() == BNode::DICT) { BDictNode* d = (BDictNode*)n.data(); d = d->getDict(QByteArrayLiteral("files")); if (d) { d = d->getDict(tds->infoHash().toByteArray()); if (d) { try { seeders = d->getInt(QByteArrayLiteral("complete")); leechers = d->getInt(QByteArrayLiteral("incomplete")); total_downloaded = d->getInt(QByteArrayLiteral("downloaded")); supports_partial_seed_extension = d->getValue(QByteArrayLiteral("downloaders")) != 0; Out(SYS_TRK | LOG_DEBUG) << "Scrape : leechers = " << leechers << ", seeders = " << seeders << ", downloaded = " << total_downloaded << endl; } catch (...) {} scrapeDone(); if (status == bt::TRACKER_ERROR) { status = bt::TRACKER_OK; failures = 0; } } } } } void HTTPTracker::doRequest(WaitJob* wjob) { if (!url.isValid()) { requestPending(); QTimer::singleShot(500, this, SLOT(emitInvalidURLFailure())); return; } Uint16 port = ServerInterface::getPort(); QUrlQuery query(url); query.addQueryItem(QStringLiteral("peer_id"), peer_id.toString()); query.addQueryItem(QStringLiteral("port"), QString::number(port)); query.addQueryItem(QStringLiteral("uploaded"), QString::number(bytesUploaded())); query.addQueryItem(QStringLiteral("downloaded"), QString::number(bytesDownloaded())); if (event == QLatin1String("completed")) query.addQueryItem(QStringLiteral("left"), QStringLiteral("0")); // need to send 0 when we are completed else query.addQueryItem(QStringLiteral("left"), QString::number(tds->bytesLeft())); query.addQueryItem(QStringLiteral("compact"), QStringLiteral("1")); if (event != QLatin1String("stopped")) query.addQueryItem(QStringLiteral("numwant"), QStringLiteral("200")); else query.addQueryItem(QStringLiteral("numwant"), QStringLiteral("0")); query.addQueryItem(QStringLiteral("key"), QString::number(key)); QString cip = Tracker::getCustomIP(); if (cip.isNull()) cip = CurrentIPv6Address(); if (!cip.isEmpty()) query.addQueryItem(QStringLiteral("ip"), cip); if (event.isEmpty() && supports_partial_seed_extension && tds->isPartialSeed()) event = QStringLiteral("paused"); if (!event.isEmpty()) query.addQueryItem(QStringLiteral("event"), event); const SHA1Hash & info_hash = tds->infoHash(); QString epq = query.toString(QUrl::FullyEncoded) + QLatin1String("&info_hash=") + info_hash.toURLString(); QUrl u = url; u.setQuery(epq, QUrl::StrictMode); if (active_job) { announce_queue.append(u); Out(SYS_TRK | LOG_NOTICE) << "Announce ongoing, queueing announce" << endl; } else { doAnnounce(u); // if there is a wait job, add this job to the waitjob if (wjob) wjob->addExitOperation(new ExitJobOperation(active_job)); } } bool HTTPTracker::updateData(const QByteArray & data) { //#define DEBUG_PRINT_RESPONSE #ifdef DEBUG_PRINT_RESPONSE Out(SYS_TRK | LOG_DEBUG) << "Data : " << endl; Out(SYS_TRK | LOG_DEBUG) << QString(data) << endl; #endif // search for dictionary, there might be random garbage infront of the data int i = 0; while (i < data.size()) { if (data[i] == 'd') break; i++; } if (i == data.size()) { failures++; failed(i18n("Invalid response from tracker")); return false; } BDecoder dec(data, false, i); BNode* n = 0; try { n = dec.decode(); } catch (...) { failures++; failed(i18n("Invalid data from tracker")); return false; } if (!n || n->getType() != BNode::DICT) { failures++; failed(i18n("Invalid response from tracker")); delete n; return false; } BDictNode* dict = (BDictNode*)n; if (dict->getData(QByteArrayLiteral("failure reason"))) { BValueNode* vn = dict->getValue(QByteArrayLiteral("failure reason")); error = vn->data().toString(); failures++; failed(error); delete n; return false; } if (dict->getData(QByteArrayLiteral("warning message"))) { BValueNode* vn = dict->getValue(QByteArrayLiteral("warning message")); warning = vn->data().toString(); } else warning.clear(); BValueNode* vn = dict->getValue(QByteArrayLiteral("interval")); // if no interval is specified, use 5 minutes if (vn) interval = vn->data().toInt(); else interval = 5 * 60; vn = dict->getValue(QByteArrayLiteral("incomplete")); if (vn) leechers = vn->data().toInt(); vn = dict->getValue(QByteArrayLiteral("complete")); if (vn) seeders = vn->data().toInt(); BListNode* ln = dict->getList(QByteArrayLiteral("peers")); if (!ln) { // no list, it might however be a compact response vn = dict->getValue(QByteArrayLiteral("peers")); if (vn && vn->data().getType() == Value::STRING) { QByteArray arr = vn->data().toByteArray(); for (int i = 0;i < arr.size();i += 6) { Uint8 buf[6]; for (int j = 0;j < 6;j++) buf[j] = arr[i + j]; Uint32 ip = ReadUint32(buf, 0); addPeer(net::Address(ip, ReadUint16(buf, 4)), false); } } } else { for (Uint32 i = 0;i < ln->getNumChildren();i++) { BDictNode* dict = dynamic_cast(ln->getChild(i)); if (!dict) continue; BValueNode* ip_node = dict->getValue(QByteArrayLiteral("ip")); BValueNode* port_node = dict->getValue(QByteArrayLiteral("port")); if (!ip_node || !port_node) continue; net::Address addr(ip_node->data().toString(), port_node->data().toInt()); addPeer(addr, false); } } // Check for IPv6 compact peers vn = dict->getValue(QByteArrayLiteral("peers6")); if (vn && vn->data().getType() == Value::STRING) { QByteArray arr = vn->data().toByteArray(); for (int i = 0;i < arr.size();i += 18) { Q_IPV6ADDR ip; memcpy(ip.c, arr.data() + i, 16); quint16 port = ReadUint16((const Uint8*)arr.data() + i, 16); addPeer(net::Address(ip, port), false); } } delete n; return true; } void HTTPTracker::onKIOAnnounceResult(KJob* j) { KIOAnnounceJob* st = (KIOAnnounceJob*)j; onAnnounceResult(st->announceUrl(), st->replyData(), j); } #ifdef HAVE_HTTPANNOUNEJOB void HTTPTracker::onQHttpAnnounceResult(KJob* j) { HTTPAnnounceJob* st = (HTTPAnnounceJob*)j; onAnnounceResult(st->announceUrl(), st->replyData(), j); } #endif void HTTPTracker::onAnnounceResult(const QUrl& url, const QByteArray& data, KJob* j) { timer.stop(); active_job = 0; if (j->error() && data.size() == 0) { QString err = error; error.clear(); if (err.isEmpty()) err = j->errorString(); Out(SYS_TRK | LOG_IMPORTANT) << "Error : " << err << endl; if (QUrlQuery(url).queryItemValue(QStringLiteral("event")) != QLatin1String("stopped")) { failures++; failed(err); } else { status = TRACKER_IDLE; stopDone(); } } else { if (QUrlQuery(url).queryItemValue(QStringLiteral("event")) != QLatin1String("stopped")) { try { if (updateData(data)) { failures = 0; peersReady(this); request_time = QDateTime::currentDateTime(); status = TRACKER_OK; if (QUrlQuery(url).queryItemValue(QStringLiteral("event")) == QLatin1String("started")) started = true; if (started) reannounce_timer.start(interval * 1000); requestOK(); } } catch (bt::Error & err) { failures++; failed(i18n("Invalid response from tracker")); } event = QString(); } else { status = TRACKER_IDLE; failures = 0; stopDone(); } } doAnnounceQueue(); } void HTTPTracker::emitInvalidURLFailure() { failures++; failed(i18n("Invalid tracker URL")); } void HTTPTracker::setupMetaData(KIO::MetaData & md) { md["UserAgent"] = bt::GetVersionString(); md["SendLanguageSettings"] = "false"; md["cookies"] = "none"; // md["accept"] = "text/plain"; md["accept"] = "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"; if (proxy_on) { QString p = QString("%1:%2").arg(proxy).arg(proxy_port); - if (!p.startsWith("http://")) + if (!p.startsWith(QLatin1String("http://"))) p = "http://" + p; // set the proxy if the doNotUseKDEProxy ix enabled (URL must be valid to) QUrl url(p); if (url.isValid() && proxy.trimmed().length() > 0) { md["UseProxy"] = p; md["ProxyUrls"] = p; } else { md["UseProxy"] = QString(); md["ProxyUrls"] = QString(); } Out(SYS_TRK | LOG_DEBUG) << "Using proxy : " << md["UseProxy"] << endl; } } void HTTPTracker::doAnnounceQueue() { if (announce_queue.empty()) return; QUrl u = announce_queue.front(); announce_queue.pop_front(); doAnnounce(u); } void HTTPTracker::doAnnounce(const QUrl & u) { Out(SYS_TRK | LOG_NOTICE) << "Doing tracker request to url (via " << (use_qhttp ? "QHttp" : "KIO") << "): " << u.toString() << endl; if (!use_qhttp) { KIO::MetaData md; setupMetaData(md); KIOAnnounceJob* j = new KIOAnnounceJob(u, md); connect(j, &KIOAnnounceJob::result, this, &HTTPTracker::onKIOAnnounceResult); active_job = j; } #ifdef HAVE_HTTPANNOUNEJOB else { HTTPAnnounceJob* j = new HTTPAnnounceJob(u); connect(j, &HTTPAnnounceJob::result, this, &HTTPTracker::onQHttpAnnounceResult); if (!proxy_on) { QString proxy = KProtocolManager::proxyForUrl(u); // Use KDE settings if (!proxy.isNull() && proxy != QLatin1String("DIRECT")) { QUrl proxy_url(proxy); j->setProxy(proxy_url.host(), proxy_url.port() <= 0 ? 80 : proxy_url.port()); } } else if (!proxy.isNull()) { j->setProxy(proxy, proxy_port); } active_job = j; j->start(); } #endif timer.start(60*1000); status = TRACKER_ANNOUNCING; requestPending(); } void HTTPTracker::onTimeout() { if (active_job) { error = i18n("Timeout contacting tracker %1", url.toString()); active_job->kill(KJob::EmitResult); } } void HTTPTracker::setProxy(const QString & p, const bt::Uint16 port) { proxy = p; proxy_port = port; } void HTTPTracker::setProxyEnabled(bool on) { proxy_on = on; } void HTTPTracker::setUseQHttp(bool on) { use_qhttp = false; } } diff --git a/src/tracker/tracker.cpp b/src/tracker/tracker.cpp index df98175..6671cd0 100644 --- a/src/tracker/tracker.cpp +++ b/src/tracker/tracker.cpp @@ -1,152 +1,152 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "tracker.h" #include #include #include #include #include #include #include #include #include "udptracker.h" #include "httptracker.h" namespace bt { static QString custom_ip; static QString custom_ip_resolved; const Uint32 INITIAL_WAIT_TIME = 30; const Uint32 LONGER_WAIT_TIME = 300; const Uint32 FINAL_WAIT_TIME = 1800; Tracker::Tracker(const QUrl &url,TrackerDataSource* tds,const PeerID & id,int tier) : TrackerInterface(url),tier(tier),peer_id(id),tds(tds) { srand(time(0)); key = rand(); connect(&reannounce_timer, &QTimer::timeout, this, &Tracker::manualUpdate); reannounce_timer.setSingleShot(true); bytes_downloaded_at_start = bytes_uploaded_at_start = 0; } Tracker::~Tracker() { } void Tracker::setCustomIP(const QString & ip) { if (custom_ip == ip) return; Out(SYS_TRK|LOG_NOTICE) << "Setting custom ip to " << ip << endl; custom_ip = ip; custom_ip_resolved = QString(); if (ip.isNull()) return; - if (custom_ip.endsWith(".i2p")) + if (custom_ip.endsWith(QLatin1String(".i2p"))) { custom_ip_resolved = custom_ip; } else { net::Address addr; if (addr.setAddress(ip)) custom_ip_resolved = custom_ip; else custom_ip_resolved = net::AddressResolver::resolve(custom_ip, 7777).toString(); } } QString Tracker::getCustomIP() { return custom_ip_resolved; } void Tracker::timedDelete(int ms) { QTimer::singleShot(ms,this,SLOT(deleteLater())); connect(this, &Tracker::stopDone, this, &Tracker::deleteLater); } void Tracker::failed(const QString& err) { error = err; status = TRACKER_ERROR; requestFailed(err); } void Tracker::handleFailure() { if (failureCount() > 5) { // we failed to contact the tracker 5 times in a row, so try again in // 30 minutes setInterval(FINAL_WAIT_TIME); reannounce_timer.start(FINAL_WAIT_TIME * 1000); request_time = QDateTime::currentDateTime(); } else if (failureCount() > 2) { // we failed to contact the only tracker 3 times in a row, so try again in // a minute or 5, no need for hammering every 30 seconds setInterval(LONGER_WAIT_TIME); reannounce_timer.start(LONGER_WAIT_TIME * 1000); request_time = QDateTime::currentDateTime(); } else { // lets not hammer and wait 30 seconds setInterval(INITIAL_WAIT_TIME); reannounce_timer.start(INITIAL_WAIT_TIME * 1000); request_time = QDateTime::currentDateTime(); } } void Tracker::resetTrackerStats() { bytes_downloaded_at_start = tds->bytesDownloaded(); bytes_uploaded_at_start = tds->bytesUploaded(); } Uint64 Tracker::bytesDownloaded() const { Uint64 bd = tds->bytesDownloaded(); if (bd > bytes_downloaded_at_start) return bd - bytes_downloaded_at_start; else return 0; } Uint64 Tracker::bytesUploaded() const { Uint64 bu = tds->bytesUploaded(); if (bu > bytes_uploaded_at_start) return bu - bytes_uploaded_at_start; else return 0; } } diff --git a/src/tracker/trackermanager.cpp b/src/tracker/trackermanager.cpp index 6548686..71fa5e6 100644 --- a/src/tracker/trackermanager.cpp +++ b/src/tracker/trackermanager.cpp @@ -1,607 +1,607 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "trackermanager.h" #include #include #include #include #include #include #include #include #include #include -#include "trackermanager.h" namespace bt { TrackerManager::TrackerManager(bt::TorrentControl* tor,PeerManager* pman) : tor(tor),pman(pman),curr(0),started(false) { trackers.setAutoDelete(true); no_save_custom_trackers = false; const TrackerTier* t = tor->getTorrent().getTrackerList(); int tier = 1; while (t) { // add all standard trackers const QList & tr = t->urls; QList::const_iterator i = tr.begin(); while (i != tr.end()) { addTracker(*i,false,tier); ++i; } tier++; t = t->next; } //load custom trackers loadCustomURLs(); // Load status of each tracker loadTrackerStatus(); if (tor->getStats().priv_torrent) switchTracker(selectTracker()); } TrackerManager::~TrackerManager() { saveCustomURLs(); saveTrackerStatus(); } TrackerInterface* TrackerManager::getCurrentTracker() const { return curr; } bool TrackerManager::noTrackersReachable() const { if (tor->getStats().priv_torrent) { return curr ? curr->trackerStatus() == TRACKER_ERROR : false; } else { int enabled = 0; // If all trackers have an ERROR status, and there is at least one // enabled, we must return true; for (PtrMap::const_iterator i = trackers.begin();i != trackers.end();++i) { if (i->second->isEnabled()) { if (i->second->trackerStatus() != TRACKER_ERROR) return false; enabled++; } } return enabled > 0; } } void TrackerManager::setCurrentTracker(bt::TrackerInterface* t) { if (!tor->getStats().priv_torrent) return; Tracker* trk = (Tracker*)t; if (!trk) return; if (curr != trk) { if (curr) curr->stop(); switchTracker(trk); trk->start(); } } void TrackerManager::setCurrentTracker(const QUrl &url) { Tracker* trk = trackers.find(url); if (trk) setCurrentTracker(trk); } QList TrackerManager::getTrackers() { QList ret; for (PtrMap::iterator i = trackers.begin();i != trackers.end();++i) { ret.append(i->second); } return ret; } TrackerInterface* TrackerManager::addTracker(const QUrl &url, bool custom, int tier) { if (trackers.contains(url)) return 0; Tracker* trk = 0; if (url.scheme() == QLatin1String("udp")) trk = new UDPTracker(url,this,tor->getTorrent().getPeerID(),tier); else if (url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")) trk = new HTTPTracker(url,this,tor->getTorrent().getPeerID(),tier); else return 0; addTracker(trk); if (custom) { custom_trackers.append(url); if (!no_save_custom_trackers) { saveCustomURLs(); saveTrackerStatus(); } } return trk; } bool TrackerManager::removeTracker(bt::TrackerInterface* t) { return removeTracker(t->trackerURL()); } bool TrackerManager::removeTracker(const QUrl &url) { if (!custom_trackers.contains(url)) return false; custom_trackers.removeAll(url); Tracker* trk = trackers.find(url); if (trk && curr == trk && tor->getStats().priv_torrent) { // do a timed delete on the tracker, so the stop signal // has plenty of time to reach it trk->stop(); trk->timedDelete(10 * 1000); trackers.setAutoDelete(false); trackers.erase(url); trackers.setAutoDelete(true); if (trackers.count() > 0) { switchTracker(selectTracker()); if (curr) curr->start(); } } else { // just delete if not the current one trackers.erase(url); } saveCustomURLs(); return true; } bool TrackerManager::canRemoveTracker(bt::TrackerInterface* t) { return custom_trackers.contains(t->trackerURL()); } void TrackerManager::restoreDefault() { QList::iterator i = custom_trackers.begin(); while (i != custom_trackers.end()) { Tracker* t = trackers.find(*i); if (t) { if (t->isStarted()) t->stop(); if (curr == t && tor->getStats().priv_torrent) { curr = 0; trackers.erase(*i); } else { trackers.erase(*i); } } ++i; } custom_trackers.clear(); saveCustomURLs(); if (tor->getStats().priv_torrent && curr == 0) switchTracker(selectTracker()); } void TrackerManager::addTracker(Tracker* trk) { trackers.insert(trk->trackerURL(),trk); connect(trk, &Tracker::peersReady, pman, &PeerManager::peerSourceReady); connect(trk, &Tracker::scrapeDone, tor, &TorrentControl::trackerScrapeDone); connect(trk, &Tracker::requestOK, this, &TrackerManager::onTrackerOK); connect(trk, &Tracker::requestFailed, this, &TrackerManager::onTrackerError); } void TrackerManager::start() { if (started) return; if (tor->getStats().priv_torrent) { if (!curr) { if (trackers.count() > 0) { switchTracker(selectTracker()); if (curr) curr->start(); } } else { curr->start(); } } else { for (PtrMap::iterator i = trackers.begin();i != trackers.end();++i) { if (i->second->isEnabled()) i->second->start(); } } started = true; } void TrackerManager::stop(bt::WaitJob* wjob) { if (!started) return; started = false; if (tor->getStats().priv_torrent) { if (curr) curr->stop(wjob); for (PtrMap::iterator i = trackers.begin();i != trackers.end();++i) { i->second->reset(); } } else { for (PtrMap::iterator i = trackers.begin();i != trackers.end();++i) { i->second->stop(wjob); i->second->reset(); } } } void TrackerManager::completed() { if (tor->getStats().priv_torrent) { if (curr) curr->completed(); } else { for (PtrMap::iterator i = trackers.begin();i != trackers.end();++i) { i->second->completed(); } } } void TrackerManager::scrape() { for (PtrMap::iterator i = trackers.begin();i != trackers.end();++i) { i->second->scrape(); } } void TrackerManager::manualUpdate() { if (tor->getStats().priv_torrent) { if (curr) { curr->manualUpdate(); } } else { for (PtrMap::iterator i = trackers.begin();i != trackers.end();++i) { if (i->second->isEnabled()) i->second->manualUpdate(); } } } void TrackerManager::saveCustomURLs() { QString trackers_file = tor->getTorDir() + QLatin1String("trackers"); QFile file(trackers_file); if(!file.open(QIODevice::WriteOnly)) return; QTextStream stream(&file); for (QList::iterator i = custom_trackers.begin();i != custom_trackers.end();++i) stream << (*i).toDisplayString() << ::endl; } void TrackerManager::loadCustomURLs() { QString trackers_file = tor->getTorDir() + QLatin1String("trackers"); QFile file(trackers_file); if(!file.open( QIODevice::ReadOnly)) return; no_save_custom_trackers = true; QTextStream stream(&file); while (!stream.atEnd()) { addTracker(QUrl(stream.readLine()),true); } no_save_custom_trackers = false; } void TrackerManager::saveTrackerStatus() { QString status_file = tor->getTorDir() + QLatin1String("tracker_status"); QFile file(status_file); if(!file.open(QIODevice::WriteOnly)) return; QTextStream stream(&file); PtrMap::iterator i = trackers.begin(); while (i != trackers.end()) { QUrl url = i->first; Tracker* trk = i->second; stream << (trk->isEnabled() ? "1:" : "0:") << url.toDisplayString() << ::endl; ++i; } } void TrackerManager::loadTrackerStatus() { QString status_file = tor->getTorDir() + QLatin1String("tracker_status"); QFile file(status_file); if(!file.open( QIODevice::ReadOnly)) return; QTextStream stream(&file); while (!stream.atEnd()) { QString line = stream.readLine(); if (line.size() < 2) continue; if (line[0] == '0') { Tracker* trk = trackers.find(QUrl(line.mid(2))); // url starts at the second char if (trk) trk->setEnabled(false); } } } Tracker* TrackerManager::selectTracker() { Tracker* n = 0; PtrMap::iterator i = trackers.begin(); while (i != trackers.end()) { Tracker* t = i->second; if (t->isEnabled()) { if (!n) n = t; else if (t->failureCount() < n->failureCount()) n = t; else if (t->failureCount() == n->failureCount()) n = t->getTier() < n->getTier() ? t : n; } ++i; } if (n) { Out(SYS_TRK|LOG_DEBUG) << "Selected tracker " << n->trackerURL().toString() << " (tier = " << n->getTier() << ")" << endl; } return n; } void TrackerManager::onTrackerError(const QString & err) { Q_UNUSED(err); if (!started) return; if (!tor->getStats().priv_torrent) { Tracker* trk = (Tracker*)sender(); trk->handleFailure(); } else { Tracker* trk = (Tracker*)sender(); if (trk == curr) { // select an other tracker trk = selectTracker(); if (trk == curr) // if we can't find another handle the failure { trk->handleFailure(); } else { curr->stop(); switchTracker(trk); if (curr->failureCount() > 0) curr->handleFailure(); else curr->start(); } } else trk->handleFailure(); } } void TrackerManager::onTrackerOK() { Tracker* tracker = (Tracker*)sender(); if (tracker->isStarted()) tracker->scrape(); } void TrackerManager::updateCurrentManually() { if (!curr) return; curr->manualUpdate(); } void TrackerManager::switchTracker(Tracker* trk) { if (curr == trk) return; curr = trk; if (curr) Out(SYS_TRK|LOG_NOTICE) << "Switching to tracker " << trk->trackerURL() << endl; } Uint32 TrackerManager::getNumSeeders() const { if (tor->getStats().priv_torrent) { return curr && curr->getNumSeeders() > 0 ? curr->getNumSeeders() : 0; } int r = 0; for (PtrMap::const_iterator i = trackers.begin();i != trackers.end();++i) { int v = i->second->getNumSeeders(); if (v > r) r = v; } return r; } Uint32 TrackerManager::getNumLeechers() const { if (tor->getStats().priv_torrent) return curr && curr->getNumLeechers() > 0 ? curr->getNumLeechers() : 0; int r = 0; for (PtrMap::const_iterator i = trackers.begin();i != trackers.end();++i) { int v = i->second->getNumLeechers(); if (v > r) r = v; } return r; } void TrackerManager::setTrackerEnabled(const QUrl &url,bool enabled) { Tracker* trk = trackers.find(url); if (!trk) return; trk->setEnabled(enabled); if (!enabled) { trk->stop(); if (curr == trk) // if the current tracker is disabled, switch to another one { switchTracker(selectTracker()); if (curr) curr->start(); } } else { // start tracker if necessary if (!tor->getStats().priv_torrent && started) trk->start(); } saveTrackerStatus(); } Uint64 TrackerManager::bytesDownloaded() const { const TorrentStats & s = tor->getStats(); if (s.imported_bytes > s.bytes_downloaded) return 0; else return s.bytes_downloaded - s.imported_bytes; } Uint64 TrackerManager::bytesUploaded() const { return tor->getStats().bytes_uploaded; } Uint64 TrackerManager::bytesLeft() const { return tor->getStats().bytes_left; } const bt::SHA1Hash& TrackerManager::infoHash() const { return tor->getInfoHash(); } bool TrackerManager::isPartialSeed() const { return pman->isPartialSeed(); } } diff --git a/src/upnp/httprequest.cpp b/src/upnp/httprequest.cpp index a472b4b..71c4578 100644 --- a/src/upnp/httprequest.cpp +++ b/src/upnp/httprequest.cpp @@ -1,82 +1,82 @@ /*************************************************************************** * Copyright (C) 2005-2007 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "httprequest.h" #include #include #include #include #include #include -#include "httprequest.h" namespace bt { HTTPRequest::HTTPRequest(const QNetworkRequest & hdr,const QString & payload,const QString & host,Uint16 port,bool verbose) : hdr(hdr),m_payload(payload),verbose(verbose),host(host),port(port),success(false) { networkAccessManager = new QNetworkAccessManager(this); connect(networkAccessManager, &QNetworkAccessManager::finished, this, &HTTPRequest::replyFinished); networkAccessManager->connectToHost(host,port); QTcpSocket socket; QString localAddress; socket.connectToHost(host, port); if (socket.waitForConnected()) { localAddress = socket.localAddress().toString(); socket.close(); } else { Out(SYS_PNP|LOG_DEBUG) << "TCP connection timeout" << endl; socket.close(); error = i18n("Operation timed out"); success = false; emit result(this); operationFinished(this); return; } m_payload = m_payload.replace(QLatin1String("$LOCAL_IP"), localAddress); } void HTTPRequest::start() { networkAccessManager->post(hdr, m_payload.toLatin1()); } void HTTPRequest::replyFinished(QNetworkReply *networkReply) { if(networkReply->error()) { error = networkReply->errorString(); success = false; emit result(this); operationFinished(this); return; } reply = networkReply->readAll(); success = true; emit result(this); operationFinished(this); } } diff --git a/src/upnp/upnpdescriptionparser.cpp b/src/upnp/upnpdescriptionparser.cpp index c439f7f..f9b6e40 100644 --- a/src/upnp/upnpdescriptionparser.cpp +++ b/src/upnp/upnpdescriptionparser.cpp @@ -1,238 +1,238 @@ /*************************************************************************** * Copyright (C) 2005-2007 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "upnpdescriptionparser.h" #include #include #include #include #include "upnprouter.h" -#include "upnpdescriptionparser.h" using namespace bt; namespace bt { class XMLContentHandler : public QXmlDefaultHandler { enum Status { TOPLEVEL,ROOT,DEVICE,SERVICE,FIELD,OTHER }; QString tmp; UPnPRouter* router; UPnPService curr_service; QStack status_stack; public: XMLContentHandler(UPnPRouter* router); virtual ~XMLContentHandler(); bool startDocument(); bool endDocument(); bool startElement(const QString &, const QString & localName, const QString &, const QXmlAttributes & atts); bool endElement(const QString & , const QString & localName, const QString & ); bool characters(const QString & ch); bool interestingDeviceField(const QString & name); bool interestingServiceField(const QString & name); }; UPnPDescriptionParser::UPnPDescriptionParser() {} UPnPDescriptionParser::~UPnPDescriptionParser() {} bool UPnPDescriptionParser::parse(const QString & file,UPnPRouter* router) { bool ret = true; QFile fptr(file); if (!fptr.open(QIODevice::ReadOnly)) return false; QXmlInputSource input(&fptr); XMLContentHandler chandler(router); QXmlSimpleReader reader; reader.setContentHandler(&chandler); ret = reader.parse(&input,false); if (!ret) { Out(SYS_PNP|LOG_IMPORTANT) << "Error parsing XML" << endl; return false; } return true; } bool UPnPDescriptionParser::parse(const QByteArray & data,UPnPRouter* router) { bool ret = true; QXmlInputSource input; input.setData(data); XMLContentHandler chandler(router); QXmlSimpleReader reader; reader.setContentHandler(&chandler); ret = reader.parse(&input,false); if (!ret) { Out(SYS_PNP|LOG_IMPORTANT) << "Error parsing XML" << endl; return false; } return true; } ///////////////////////////////////////////////////////////////////////////////// XMLContentHandler::XMLContentHandler(UPnPRouter* router) : router(router) {} XMLContentHandler::~XMLContentHandler() {} bool XMLContentHandler::startDocument() { status_stack.push(TOPLEVEL); return true; } bool XMLContentHandler::endDocument() { status_stack.pop(); return true; } bool XMLContentHandler::interestingDeviceField(const QString & name) { return name == "friendlyName" || name == "manufacturer" || name == "modelDescription" || name == "modelName" || name == "modelNumber"; } bool XMLContentHandler::interestingServiceField(const QString & name) { return name == "serviceType" || name == "serviceId" || name == "SCPDURL" || name == "controlURL" || name == "eventSubURL"; } bool XMLContentHandler::startElement(const QString &, const QString & localName, const QString &, const QXmlAttributes & ) { tmp = ""; switch (status_stack.top()) { case TOPLEVEL: // from toplevel we can only go to root if (localName == "root") status_stack.push(ROOT); else return false; break; case ROOT: // from the root we can go to device or specVersion // we are not interested in the specVersion if (localName == "device") status_stack.push(DEVICE); else status_stack.push(OTHER); break; case DEVICE: // see if it is a field we are interested in if (interestingDeviceField(localName)) status_stack.push(FIELD); else status_stack.push(OTHER); break; case SERVICE: if (interestingServiceField(localName)) status_stack.push(FIELD); else status_stack.push(OTHER); break; case OTHER: if (localName == "service") status_stack.push(SERVICE); else if (localName == "device") status_stack.push(DEVICE); else status_stack.push(OTHER); break; case FIELD: break; } return true; } bool XMLContentHandler::endElement(const QString & , const QString & localName, const QString & ) { switch (status_stack.top()) { case FIELD: // we have a field so set it status_stack.pop(); if (status_stack.top() == DEVICE) { // if we are in a device router->getDescription().setProperty(localName,tmp); } else if (status_stack.top() == SERVICE) { // set a property of a service curr_service.setProperty(localName,tmp); } break; case SERVICE: // add the service router->addService(curr_service); curr_service.clear(); // pop the stack status_stack.pop(); break; default: status_stack.pop(); break; } // reset tmp tmp = ""; return true; } bool XMLContentHandler::characters(const QString & ch) { if (ch.length() > 0) { tmp += ch; } return true; } } diff --git a/src/upnp/upnpmcastsocket.cpp b/src/upnp/upnpmcastsocket.cpp index bd3e6de..6aedbb0 100644 --- a/src/upnp/upnpmcastsocket.cpp +++ b/src/upnp/upnpmcastsocket.cpp @@ -1,390 +1,390 @@ /*************************************************************************** * Copyright (C) 2005-2007 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "upnpmcastsocket.h" #include #include #include #include #include #include #include #ifndef Q_WS_WIN #include #include #endif #include #include -#include "upnpmcastsocket.h" namespace bt { static bool UrlCompare(const QUrl &a, const QUrl & b) { if (a == b) return true; return a.scheme() == b.scheme() && a.host() == b.host() && a.password() == b.password() && a.port(80) == b.port(80) && a.path() == b.path() && a.query() == b.query(); //TODO check if ported correctly } class UPnPMCastSocket::UPnPMCastSocketPrivate { public: UPnPMCastSocketPrivate(bool verbose); ~UPnPMCastSocketPrivate(); UPnPRouter* parseResponse(const QByteArray & arr); void joinUPnPMCastGroup(int fd); void leaveUPnPMCastGroup(int fd); void onXmlFileDownloaded(UPnPRouter* r,bool success); UPnPRouter* findDevice(const QUrl &location); QSet routers; QSet pending_routers; // routers which we are downloading the XML file from bool verbose; }; UPnPMCastSocket::UPnPMCastSocket(bool verbose) : d(new UPnPMCastSocketPrivate(verbose)) { QObject::connect(this,SIGNAL(readyRead()),this,SLOT(onReadyRead())); QObject::connect(this,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error(QAbstractSocket::SocketError))); for (Uint32 i = 0;i < 10;i++) { if (!bind(1900 + i,QUdpSocket::ShareAddress)) Out(SYS_PNP|LOG_IMPORTANT) << "Cannot bind to UDP port 1900 : " << errorString() << endl; else break; } d->joinUPnPMCastGroup(socketDescriptor()); } UPnPMCastSocket::~UPnPMCastSocket() { d->leaveUPnPMCastGroup(socketDescriptor()); delete d; } void UPnPMCastSocket::discover() { Out(SYS_PNP|LOG_NOTICE) << "Trying to find UPnP devices on the local network" << endl; // send a HTTP M-SEARCH message to 239.255.255.250:1900 const char* upnp_data = "M-SEARCH * HTTP/1.1\r\n" "HOST: 239.255.255.250:1900\r\n" "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n" "MAN:\"ssdp:discover\"\r\n" "MX:3\r\n" "\r\n\0"; const char* tr64_data = "M-SEARCH * HTTP/1.1\r\n" "HOST: 239.255.255.250:1900\r\n" "ST:urn:dslforum-org:device:InternetGatewayDevice:1\r\n" "MAN:\"ssdp:discover\"\r\n" "MX:3\r\n" "\r\n\0"; if (d->verbose) { Out(SYS_PNP|LOG_NOTICE) << "Sending : " << endl; Out(SYS_PNP|LOG_NOTICE) << upnp_data << endl; Out(SYS_PNP|LOG_NOTICE) << "Sending : " << endl; Out(SYS_PNP|LOG_NOTICE) << tr64_data << endl; } writeDatagram(upnp_data,strlen(upnp_data),QHostAddress("239.255.255.250"),1900); writeDatagram(tr64_data,strlen(tr64_data),QHostAddress("239.255.255.250"),1900); } void UPnPMCastSocket::onXmlFileDownloaded(UPnPRouter* r,bool success) { d->pending_routers.remove(r); if (!success) { // we couldn't download and parse the XML file so // get rid of it r->deleteLater(); } else { // add it to the list and emit the signal QUrl location = r->getLocation(); if (d->findDevice(location)) { r->deleteLater(); } else { d->routers.insert(r); discovered(r); } } } void UPnPMCastSocket::onReadyRead() { if (pendingDatagramSize() == 0) { Out(SYS_PNP|LOG_NOTICE) << "0 byte UDP packet " << endl; // KDatagramSocket wrongly handles UDP packets with no payload // so we need to deal with it oursleves int fd = socketDescriptor(); char tmp; ::read(fd,&tmp,1); return; } QByteArray data(pendingDatagramSize(),0); if (readDatagram(data.data(),pendingDatagramSize()) == -1) return; if (d->verbose) { Out(SYS_PNP|LOG_NOTICE) << "Received : " << endl; Out(SYS_PNP|LOG_NOTICE) << QString(data) << endl; } // try to make a router of it UPnPRouter* r = d->parseResponse(data); if (r) { QObject::connect(r,SIGNAL(xmlFileDownloaded(UPnPRouter*,bool)), this,SLOT(onXmlFileDownloaded(UPnPRouter*,bool))); // download it's xml file r->downloadXMLFile(); d->pending_routers.insert(r); } } void UPnPMCastSocket::error(QAbstractSocket::SocketError ) { Out(SYS_PNP|LOG_IMPORTANT) << "UPnPMCastSocket Error : " << errorString() << endl; } void UPnPMCastSocket::saveRouters(const QString & file) { QFile fptr(file); if (!fptr.open(QIODevice::WriteOnly)) { Out(SYS_PNP|LOG_IMPORTANT) << "Cannot open file " << file << " : " << fptr.errorString() << endl; return; } // file format is simple : 2 lines per router, // one containing the server, the other the location QTextStream fout(&fptr); foreach (UPnPRouter* r,d->routers) { fout << r->getServer() << ::endl; fout << r->getLocation().toString() << ::endl; } } void UPnPMCastSocket::loadRouters(const QString & file) { QFile fptr(file); if (!fptr.open(QIODevice::ReadOnly)) { Out(SYS_PNP|LOG_IMPORTANT) << "Cannot open file " << file << " : " << fptr.errorString() << endl; return; } // file format is simple : 2 lines per router, // one containing the server, the other the location QTextStream fin(&fptr); while (!fin.atEnd()) { QString server, location; server = fin.readLine(); location = fin.readLine(); UPnPRouter* r = new UPnPRouter(server,QUrl(location)); // download it's xml file QObject::connect(r,SIGNAL(xmlFileDownloaded(UPnPRouter*,bool)),this,SLOT(onXmlFileDownloaded(UPnPRouter*,bool))); r->downloadXMLFile(); d->pending_routers.insert(r); } } Uint32 UPnPMCastSocket::getNumDevicesDiscovered() const { return d->routers.count(); } UPnPRouter* UPnPMCastSocket::findDevice(const QString & name) { QUrl location(name); return d->findDevice(location); } void UPnPMCastSocket::setVerbose(bool v) { d->verbose = v; } ///////////////////////////////////////////////////////////// UPnPMCastSocket::UPnPMCastSocketPrivate::UPnPMCastSocketPrivate(bool verbose) : verbose(verbose) { } UPnPMCastSocket::UPnPMCastSocketPrivate::~UPnPMCastSocketPrivate() { qDeleteAll(pending_routers); qDeleteAll(routers); } void UPnPMCastSocket::UPnPMCastSocketPrivate::joinUPnPMCastGroup(int fd) { struct ip_mreq mreq; memset(&mreq,0,sizeof(struct ip_mreq)); inet_aton("239.255.255.250",&mreq.imr_multiaddr); mreq.imr_interface.s_addr = htonl(INADDR_ANY); #ifndef Q_WS_WIN if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(struct ip_mreq)) < 0) #else if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&mreq,sizeof(struct ip_mreq)) < 0) #endif { Out(SYS_PNP|LOG_NOTICE) << "Failed to join multicast group 239.255.255.250" << endl; } } void UPnPMCastSocket::UPnPMCastSocketPrivate::leaveUPnPMCastGroup(int fd) { struct ip_mreq mreq; memset(&mreq,0,sizeof(struct ip_mreq)); inet_aton("239.255.255.250",&mreq.imr_multiaddr); mreq.imr_interface.s_addr = htonl(INADDR_ANY); #ifndef Q_WS_WIN if (setsockopt(fd,IPPROTO_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(struct ip_mreq)) < 0) #else if (setsockopt(fd,IPPROTO_IP,IP_DROP_MEMBERSHIP,(char *)&mreq,sizeof(struct ip_mreq)) < 0) #endif { Out(SYS_PNP|LOG_NOTICE) << "Failed to leave multicast group 239.255.255.250" << endl; } } UPnPRouter* UPnPMCastSocket::UPnPMCastSocketPrivate::parseResponse(const QByteArray & arr) { const QString response = QString::fromLatin1(arr); QVector lines = response.splitRef("\r\n"); QString server; QUrl location; /* Out(SYS_PNP|LOG_DEBUG) << "Received : " << endl; for (Uint32 idx = 0;idx < lines.count(); idx++) Out(SYS_PNP|LOG_DEBUG) << lines[idx] << endl; */ // first read first line and see if contains a HTTP 200 OK message QStringRef line = lines.first(); if (!line.contains(QLatin1String("HTTP"))) { // it is either a 200 OK or a NOTIFY if (!line.contains(QLatin1String("NOTIFY")) && !line.contains(QLatin1String("200"))) return 0; } else if (line.contains(QLatin1String("M-SEARCH"))) // ignore M-SEARCH return 0; // quick check that the response being parsed is valid bool validDevice = false; for (int idx = 0;idx < lines.count() && !validDevice; idx++) { line = lines[idx]; if ((line.contains(QLatin1String("ST:")) || line.contains(QLatin1String("NT:"))) && line.contains(QLatin1String("InternetGatewayDevice"))) { validDevice = true; } } if (!validDevice) { // Out(SYS_PNP|LOG_IMPORTANT) << "Not a valid Internet Gateway Device" << endl; return 0; } // read all lines and try to find the server and location fields for (int i = 1;i < lines.count();i++) { line = lines[i]; if (line.startsWith(QLatin1String("location"), Qt::CaseInsensitive)) { location = QUrl(line.mid(line.indexOf(':') + 1).trimmed().toString()); //TODO fromLocalFile()? if (!location.isValid()) return 0; } else if (line.startsWith(QLatin1String("server"), Qt::CaseInsensitive)) { server = line.mid(line.indexOf(':') + 1).trimmed().toString(); if (server.length() == 0) return 0; } } if (findDevice(location)) { return 0; } else { Out(SYS_PNP|LOG_NOTICE) << "Detected IGD " << server << endl; // everything OK, make a new UPnPRouter return new UPnPRouter(server,location,verbose); } } UPnPRouter* UPnPMCastSocket::UPnPMCastSocketPrivate::findDevice(const QUrl &location) { foreach (UPnPRouter* r, routers) { if (UrlCompare(r->getLocation(),location)) return r; } return 0; } } diff --git a/src/util/decompressfilejob.cpp b/src/util/decompressfilejob.cpp index 086ae50..ea25a1a 100644 --- a/src/util/decompressfilejob.cpp +++ b/src/util/decompressfilejob.cpp @@ -1,132 +1,132 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "decompressfilejob.h" #include #include #include #include #include #include -#include "decompressfilejob.h" namespace bt { DecompressThread::DecompressThread(const QString & file,const QString & dest_file) : file(file),dest_file(dest_file),canceled(false),err(0) { } DecompressThread::~DecompressThread() {} void DecompressThread::run() { QFile out(dest_file); // open input file readonly if (!out.open(QIODevice::WriteOnly)) { err = KIO::ERR_CANNOT_OPEN_FOR_WRITING; Out(SYS_GEN|LOG_NOTICE) << "Failed to open " << dest_file << " : " << out.errorString() << endl; return; } // open output file KCompressionDevice dev(file, KFilterDev::compressionTypeForMimeType(QMimeDatabase().mimeTypeForFile(file).name())); if (!dev.open(QIODevice::ReadOnly)) { err = KIO::ERR_CANNOT_OPEN_FOR_READING; Out(SYS_GEN|LOG_NOTICE) << "Failed to open " << file << " : " << dev.errorString() << endl; return; } // copy the data char buf[4096]; while (!canceled && !dev.atEnd()) { int len = dev.read(buf,4096); if (len <= 0 || len > 4096) break; out.write(buf,len); } out.close(); if (canceled) { // delete output file when canceled bt::Delete(dest_file,true); } else { // delete the input file upon success bt::Delete(file,true); } } void DecompressThread::cancel() { canceled = true; } /////////////////////////////////////////// DecompressFileJob::DecompressFileJob(const QString& file, const QString& dest) : file(file),dest(dest),decompress_thread(0) { } DecompressFileJob::~DecompressFileJob() { } void DecompressFileJob::start() { decompress_thread = new DecompressThread(file,dest); connect(decompress_thread,SIGNAL(finished()),this,SLOT(decompressThreadFinished()),Qt::QueuedConnection); decompress_thread->start(); } void DecompressFileJob::kill(bool quietly) { if (decompress_thread) { decompress_thread->cancel(); decompress_thread->wait(); delete decompress_thread; decompress_thread = 0; } setError(KIO::ERR_USER_CANCELED); if (!quietly) emitResult(); } void DecompressFileJob::decompressThreadFinished() { setError(decompress_thread->error()); decompress_thread->wait(); delete decompress_thread; decompress_thread = 0; emitResult(); } } diff --git a/src/util/error.cpp b/src/util/error.cpp index 62aa939..941356f 100644 --- a/src/util/error.cpp +++ b/src/util/error.cpp @@ -1,40 +1,40 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "error.h" +#include namespace bt { Error::Error(const QString & msg) : msg(msg) { //Out(SYS_GEN|LOG_DEBUG) << "Error thrown: " << msg << endl; } Error::~Error() {} Warning::Warning(const QString& msg) : msg(msg) { //Out(SYS_GEN|LOG_DEBUG) << "Warning thrown: " << msg << endl; } Warning::~Warning() {} } diff --git a/src/util/extractfilejob.cpp b/src/util/extractfilejob.cpp index 4caeefe..48f7f52 100644 --- a/src/util/extractfilejob.cpp +++ b/src/util/extractfilejob.cpp @@ -1,147 +1,147 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "extractfilejob.h" +#include #include namespace bt { class ExtractFileThread : public QThread { public: ExtractFileThread(QIODevice* in_dev,QIODevice* out_dev) : in_dev(in_dev),out_dev(out_dev),canceled(false) { } virtual ~ExtractFileThread() { delete in_dev; delete out_dev; } virtual void run() { char buf[4096]; qint64 ret = 0; while ((ret = in_dev->read(buf,4096)) != 0 && !canceled) { out_dev->write(buf,ret); } } QIODevice* in_dev; QIODevice* out_dev; bool canceled; }; ExtractFileJob::ExtractFileJob(KArchive* archive, const QString& path, const QString& dest) : archive(archive),path(path),dest(dest),extract_thread(0) { } ExtractFileJob::~ExtractFileJob() { delete archive; } void ExtractFileJob::start() { // first find the file in the archive QStringList path_components = path.split(QLatin1Char('/'), QString::SkipEmptyParts); const KArchiveDirectory* dir = archive->directory(); for (int i = 0;i < path_components.count();i++) { // if we can't find it give back an error QString pc = path_components.at(i); if (!dir->entries().contains(pc)) { setError(KIO::ERR_DOES_NOT_EXIST); emitResult(); return; } const KArchiveEntry* e = dir->entry(pc); if (i < path_components.count() - 1) { // if we are not the last entry in the path, e must be a directory if (!e->isDirectory()) { setError(KIO::ERR_DOES_NOT_EXIST); emitResult(); return; } dir = (const KArchiveDirectory*)e; } else { // last in the path, must be a file if (!e->isFile()) { setError(KIO::ERR_DOES_NOT_EXIST); emitResult(); return; } // create a device to read the file and start a thread to do the reading KArchiveFile* file = (KArchiveFile*)e; QFile* out_dev = new QFile(dest); if (!out_dev->open(QIODevice::WriteOnly)) { delete out_dev; setError(KIO::ERR_CANNOT_OPEN_FOR_WRITING); emitResult(); return; } QIODevice* in_dev = file->createDevice(); extract_thread = new ExtractFileThread(in_dev,out_dev); connect(extract_thread,SIGNAL(finished()),this,SLOT(extractThreadDone()),Qt::QueuedConnection); extract_thread->start(); } } } void ExtractFileJob::kill(bool quietly) { if (extract_thread) { extract_thread->canceled = true; extract_thread->wait(); delete extract_thread; extract_thread = 0; } setError(KIO::ERR_USER_CANCELED); if (!quietly) emitResult(); } void ExtractFileJob::extractThreadDone() { extract_thread->wait(); delete extract_thread; extract_thread = 0; setError(0); emitResult(); } } diff --git a/src/util/fileops.cpp b/src/util/fileops.cpp index 928a316..c76b0e3 100644 --- a/src/util/fileops.cpp +++ b/src/util/fileops.cpp @@ -1,698 +1,698 @@ /*************************************************************************** * Copyright (C) 2005 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "fileops.h" #include "config-ktorrent.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "log.h" #include "file.h" #include "array.h" #include "functions.h" #ifdef Q_WS_WIN #include "win32.h" #endif #include "limits.h" #ifndef NAME_MAX #define NAME_MAX 255 #endif #ifndef PATH_MAX #define PATH_MAX 4096 #endif #ifdef HAVE_XFS_XFS_H #if !defined(HAVE___S64) || !defined(HAVE___U64) #include #endif #ifndef HAVE___U64 typedef uint64_t __u64; #endif #ifndef HAVE___S64 typedef int64_t __s64; #endif #include #endif #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif #ifndef Q_WS_WIN # ifdef Q_OS_LINUX # include # endif #include #endif #ifdef CopyFile #undef CopyFile #endif namespace bt { void MakeDir(const QString & dir,bool nothrow) { QDir d(dir); if (d.exists()) return; QString n = d.dirName(); if (!d.cdUp() || !d.mkdir(n)) { QString error = i18n("Cannot create directory %1",dir); Out(SYS_DIO|LOG_NOTICE) << error << endl; if (!nothrow) throw Error(error); } } void MakePath(const QString & dir,bool nothrow) { QStringList sl = dir.split(bt::DirSeparator(),QString::SkipEmptyParts); QString ctmp; #ifndef Q_WS_WIN ctmp += bt::DirSeparator(); #endif for (int i = 0;i < sl.count();i++) { ctmp += sl[i]; if (!bt::Exists(ctmp)) { try { MakeDir(ctmp,false); } catch (...) { if (!nothrow) throw; return; } } ctmp += bt::DirSeparator(); } } void MakeFilePath(const QString & file,bool nothrow) { QStringList sl = file.split(bt::DirSeparator()); QString ctmp; #ifndef Q_WS_WIN ctmp += bt::DirSeparator(); #endif for (int i = 0;i < sl.count() - 1;i++) { ctmp += sl[i]; if (!bt::Exists(ctmp)) try { MakeDir(ctmp,false); } catch (...) { if (!nothrow) throw; return; } ctmp += bt::DirSeparator(); } } void SymLink(const QString & link_to,const QString & link_url,bool nothrow) { if (symlink(QFile::encodeName(link_to).constData(), QFile::encodeName(link_url).constData()) != 0) { if (!nothrow) throw Error(i18n("Cannot symlink %1 to %2: %3" ,link_url,link_to ,QString::fromUtf8(strerror(errno)))); else Out(SYS_DIO|LOG_NOTICE) << QStringLiteral("Error : Cannot symlink %1 to %2: %3") .arg(link_url).arg(link_to) .arg(QString::fromUtf8(strerror(errno))) << endl; } } void Move(const QString & src,const QString & dst,bool nothrow,bool silent) { // Out() << "Moving " << src << " -> " << dst << endl; KIO::CopyJob *mv = KIO::move(QUrl::fromLocalFile(src), QUrl::fromLocalFile(dst), silent ? KIO::HideProgressInfo|KIO::Overwrite : KIO::DefaultFlags); if (!mv->exec()) { if (!nothrow) throw Error(i18n("Cannot move %1 to %2: %3", src,dst, mv->errorString())); else Out(SYS_DIO|LOG_NOTICE) << QStringLiteral("Error : Cannot move %1 to %2: %3") .arg(src).arg(dst) .arg(mv->errorString()) << endl; } } void CopyFile(const QString & src,const QString & dst,bool nothrow) { KIO::FileCopyJob* copy = KIO::file_copy(QUrl::fromLocalFile(src), QUrl::fromLocalFile(dst)); if (!copy->exec()) { if (!nothrow) throw Error(i18n("Cannot copy %1 to %2: %3", src,dst, copy->errorString())); else Out(SYS_DIO|LOG_NOTICE) << QStringLiteral("Error : Cannot copy %1 to %2: %3") .arg(src).arg(dst) .arg(copy->errorString()) << endl; } } void CopyDir(const QString & src,const QString & dst,bool nothrow) { KIO::CopyJob* copy = KIO::copy(QUrl::fromLocalFile(src), QUrl::fromLocalFile(dst)); if (!copy->exec()) { if (!nothrow) throw Error(i18n("Cannot copy %1 to %2: %3", src,dst, copy->errorString())); else Out(SYS_DIO|LOG_NOTICE) << QStringLiteral("Error : Cannot copy %1 to %2: %3") .arg(src).arg(dst) .arg(copy->errorString()) << endl; } } bool Exists(const QString & url) { return QFile::exists(url); } static bool DelDir(const QString & fn) { QDir d(fn); const QStringList subdirs = d.entryList(QDir::Dirs); for (auto i = subdirs.begin(); i != subdirs.end();i++) { QString entry = *i; if (entry == QLatin1String("..") || entry == QLatin1String(".")) continue; if (!DelDir(d.absoluteFilePath(entry))) return false; } const QStringList files = d.entryList(QDir::Files | QDir::System | QDir::Hidden); for (auto i = files.begin(); i != files.end();i++) { QString file = d.absoluteFilePath(*i); QFile fp(file); if (!QFileInfo(file).isWritable() && !fp.setPermissions(QFile::ReadUser | QFile::WriteUser)) return false; if (!fp.remove()) return false; } if (!d.rmdir(d.absolutePath())) return false; return true; } void Delete(const QString & url,bool nothrow) { bool ok = true; // first see if it is a directory if (QDir(url).exists()) { ok = DelDir(url); } else { ok = QFile::remove(url); } if (!ok) { QString err = i18n("Cannot delete %1: %2", url, QString::fromUtf8(strerror(errno))); if (!nothrow) throw Error(err); else Out(SYS_DIO|LOG_NOTICE) << "Error : " << err << endl; } } void Touch(const QString & url,bool nothrow) { if (Exists(url)) return; File fptr; if (!fptr.open(url, QStringLiteral("wb"))) { if (!nothrow) throw Error(i18n("Cannot create %1: %2",url,fptr.errorString())); else Out(SYS_DIO|LOG_NOTICE) << "Error : Cannot create " << url << " : " << fptr.errorString() << endl; } } Uint64 FileSize(const QString & url) { int ret = 0; #ifdef HAVE_STAT64 struct stat64 sb; ret = stat64(QFile::encodeName(url).constData(), &sb); #else struct stat sb; ret = stat(QFile::encodeName(url).constData(), &sb); #endif if (ret < 0) throw Error(i18n("Cannot calculate the filesize of %1: %2", url, QString::fromUtf8(strerror(errno)))); return (Uint64)sb.st_size; } Uint64 FileSize(int fd) { int ret = 0; #ifdef HAVE_STAT64 struct stat64 sb; ret = fstat64(fd,&sb); #else struct stat sb; ret = fstat(fd,&sb); #endif if (ret < 0) throw Error(i18n("Cannot calculate the filesize: %1", QString::fromUtf8(strerror(errno)))); return (Uint64)sb.st_size; } #ifdef HAVE_XFS_XFS_H bool XfsPreallocate(int fd, Uint64 size) { if( ! platform_test_xfs_fd(fd) ) { return false; } xfs_flock64_t allocopt; allocopt.l_whence = 0; allocopt.l_start = 0; allocopt.l_len = size; return (! static_cast(xfsctl(0, fd, XFS_IOC_RESVSP64, &allocopt)) ); } bool XfsPreallocate(const QString & path, Uint64 size) { int fd = ::open(QFile::encodeName(path), O_RDWR | O_LARGEFILE); if (fd < 0) throw Error(i18n("Cannot open %1: %2",path,strerror(errno))); bool ret = XfsPreallocate(fd,size); close(fd); return ret; } #endif void TruncateFile(int fd,Uint64 size,bool quick) { if (FileSize(fd) == size) return; if (quick) { #ifdef HAVE_FTRUNCATE64 if (ftruncate64(fd,size) == -1) #else if (ftruncate(fd,size) == -1) #endif throw Error(i18n("Cannot expand file: %1", QString::fromUtf8(strerror(errno)))); } else { #ifdef HAVE_POSIX_FALLOCATE64 if (posix_fallocate64(fd,0,size) != 0) throw Error(i18n("Cannot expand file: %1", QString::fromUtf8(strerror(errno)))); #elif HAVE_POSIX_FALLOCATE if (posix_fallocate(fd,0,size) != 0) throw Error(i18n("Cannot expand file: %1", QString::fromUtf8(strerror(errno)))); #elif HAVE_FTRUNCATE64 if (ftruncate64(fd,size) == -1) throw Error(i18n("Cannot expand file: %1", QString::fromUtf8(strerror(errno)))); #else if (ftruncate(fd,size) == -1) throw Error(i18n("Cannot expand file: %1", QString::fromUtf8(strerror(errno)))); #endif } } void TruncateFile(const QString & path,Uint64 size) { int fd = ::open(QFile::encodeName(path).constData(), O_RDWR | O_LARGEFILE); if (fd < 0) throw Error(i18n("Cannot open %1: %2", path, QString::fromUtf8(strerror(errno)))); try { TruncateFile(fd,size,true); close(fd); } catch (...) { close(fd); throw; } } void SeekFile(int fd,Int64 off,int whence) { #ifdef HAVE_LSEEK64 if (lseek64(fd,off,whence) == -1) #else if (lseek(fd,off,whence) == -1) #endif throw Error(i18n("Cannot seek in file: %1", QString::fromUtf8(strerror(errno)))); } bool FreeDiskSpace(const QString & path,Uint64 & bytes_free) { #ifdef HAVE_STATVFS #ifdef HAVE_STATVFS64 struct statvfs64 stfs; if (statvfs64(QFile::encodeName(path).constData(), &stfs) == 0) #else struct statvfs stfs; if (statvfs(QFile::encodeName(path).constData(), &stfs) == 0) #endif { if (stfs.f_blocks == 0) // if this is 0, then we are using gvfs return false; bytes_free = ((Uint64)stfs.f_bavail) * ((Uint64)stfs.f_frsize); return true; } else { Out(SYS_GEN|LOG_DEBUG) << "Error : statvfs for " << path << " failed : " << QString::fromUtf8(strerror(errno)) << endl; return false; } #elif defined(Q_WS_WIN) #ifdef UNICODE LPCWSTR tpath = (LPCWSTR)path.utf16(); #else const char *tpath = path.toLocal8Bit(); #endif if(GetDiskFreeSpaceEx(tpath, (PULARGE_INTEGER)&bytes_free, NULL, NULL)) { return true; } else { return false; } #else return false; #endif } bool FileNameToLong(const QString & path) { int length = 0; const QStringList names = path.split(QLatin1Char('/')); for (const QString & s : names) { QByteArray encoded = QFile::encodeName(s); if (encoded.length() >= NAME_MAX) return true; length += encoded.length(); } length += path.count(QLatin1Char('/')); return length >= PATH_MAX; } static QString ShortenName(const QString & name, int extra_number) { QFileInfo fi(name); QString ext = fi.suffix(); QString base = fi.completeBaseName(); // calculate the fixed length, 1 is for the . between filename and extension int fixed_len = 0; if (ext.length() > 0) fixed_len += QFile::encodeName(ext).length() + 1; if (extra_number > 0) fixed_len += QFile::encodeName(QString::number(extra_number)).length(); // if we can't shorten it, give up if (fixed_len > NAME_MAX - 4) return name; do { base.chop(1); }while (fixed_len + QFile::encodeName(base).length() > NAME_MAX - 4 && base.length() != 0); base += QStringLiteral("... "); // add ... so that the user knows the name is shortened QString ret = base; if (extra_number > 0) ret += QString::number(extra_number); if (ext.length() > 0) ret += QLatin1Char('.') + ext; return ret; } static QString ShortenPath(const QString & path,int extra_number) { int max_len = PATH_MAX; QByteArray encoded = QFile::encodeName(path); if (encoded.length() < max_len) return path; QFileInfo fi(path); QString ext = fi.suffix(); QString name = fi.completeBaseName(); QString fpath = fi.path() + '/'; // calculate the fixed length, 1 is for the . between filename and extension int fixed_len = QFile::encodeName(fpath).length(); if (ext.length() > 0) fixed_len += QFile::encodeName(ext).length() + 1; if (extra_number > 0) fixed_len += QFile::encodeName(QString::number(extra_number)).length(); // if we can't shorten it, give up if (fixed_len > max_len - 4) return path; do { name.chop(1); }while (fixed_len + QFile::encodeName(name).length() > max_len - 4 && name.length() != 0); name += QLatin1String("... "); // add ... so that the user knows the name is shortened QString ret = fpath + name; if (extra_number > 0) ret += QString::number(extra_number); if (ext.length() > 0) ret += QLatin1Char('.') + ext; return ret; } QString ShortenFileName(const QString & path,int extra_number) { QString assembled = QStringLiteral("/"); QStringList names = path.split(QLatin1Char('/'),QString::SkipEmptyParts); int cnt = 0; for (QStringList::iterator i = names.begin();i != names.end();++i) { QByteArray encoded = QFile::encodeName(*i); if (encoded.length() >= NAME_MAX) *i = ShortenName(*i,extra_number); assembled += *i; if (cnt < names.count() - 1) assembled += QLatin1Char('/'); cnt++; } if (QFile::encodeName(assembled).length() >= PATH_MAX) { // still to long, then the Shorten the filename assembled = ShortenPath(assembled,extra_number); } return assembled; } Uint64 DiskUsage(const QString& filename) { Uint64 ret = 0; #ifndef Q_WS_WIN #ifdef HAVE_STAT64 struct stat64 sb; if (stat64(QFile::encodeName(filename).constData(), &sb) == 0) #else struct stat sb; if (stat(QFile::encodeName(filename).constData(), &sb) == 0) #endif { ret = (Uint64)sb.st_blocks * 512; } #else DWORD high = 0; DWORD low = GetCompressedFileSize((LPWSTR)filename.utf16(),&high); if (low != INVALID_FILE_SIZE) ret = (high * MAXDWORD) + low; #endif return ret; } Uint64 DiskUsage(int fd) { Uint64 ret = 0; #ifndef Q_WS_WIN #ifdef HAVE_FSTAT64 struct stat64 sb; if (fstat64(fd,&sb) == 0) #else struct stat sb; if (fstat(fd,&sb) == 0) #endif { ret = (Uint64)sb.st_blocks * 512; } #else struct _BY_HANDLE_FILE_INFORMATION info; GetFileInformationByHandle((void *)&fd,&info); ret = (info.nFileSizeHigh * MAXDWORD) + info.nFileSizeLow; #endif return ret; } QSet AccessibleMountPoints() { QSet result; #ifdef Q_OS_LINUX FILE* fptr = setmntent("/proc/mounts", "r"); if(!fptr) return result; struct mntent mnt; char buf[PATH_MAX]; while(getmntent_r(fptr, &mnt, buf, PATH_MAX)) { result.insert(QString::fromUtf8(mnt.mnt_dir)); } endmntent(fptr); #else QList devs = Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess); foreach (Solid::Device dev,devs) { Solid::StorageAccess* sa = dev.as(); if(!sa->filePath().isEmpty() && sa->isAccessible()) result.insert(sa->filePath()); } #endif return result; } QString MountPoint(const QString& path) { QSet mount_points = AccessibleMountPoints(); QString mount_point; foreach (const QString & mp, mount_points) { if (path.startsWith(mp) && (mount_point.isEmpty() || mp.startsWith(mount_point))) { mount_point = mp; } } return mount_point; } bool IsMounted(const QString& mount_point) { return AccessibleMountPoints().contains(mount_point); } QByteArray LoadFile(const QString& path) { QFile fptr(path); if (fptr.open(QIODevice::ReadOnly)) return fptr.readAll(); else - throw Error(i18n("Unable to open file %1: %2").arg(path).arg(fptr.errorString())); + throw Error(i18n("Unable to open file %1: %2", path, fptr.errorString())); } } diff --git a/src/util/logsystemmanager.cpp b/src/util/logsystemmanager.cpp index b7d1b13..5b29c86 100644 --- a/src/util/logsystemmanager.cpp +++ b/src/util/logsystemmanager.cpp @@ -1,76 +1,76 @@ /*************************************************************************** * Copyright (C) 2008 by Joris Guisson and Ivan Vasic * * joris.guisson@gmail.com * * ivasic@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "logsystemmanager.h" #include #include "log.h" -#include "logsystemmanager.h" namespace bt { QScopedPointer LogSystemManager::self; LogSystemManager::LogSystemManager() : QObject() { // register default systems registerSystem(i18n("General"), SYS_GEN); registerSystem(i18n("Connections"), SYS_CON); registerSystem(i18n("Tracker"), SYS_TRK); registerSystem(i18n("DHT"), SYS_DHT); registerSystem(i18n("Disk Input/Output"), SYS_DIO); registerSystem(i18n("µTP"), SYS_UTP); } LogSystemManager::~LogSystemManager() { } LogSystemManager& LogSystemManager::instance() { if(!self) self.reset(new LogSystemManager()); return *self; } void LogSystemManager::registerSystem(const QString & name, Uint32 id) { systems.insert(name, id); registered(name); } void LogSystemManager::unregisterSystem(const QString & name) { if(systems.remove(name)) unregisted(name); } Uint32 LogSystemManager::systemID(const QString & name) { iterator i = systems.find(name); if(i == systems.end()) return 0; else return i.value(); } } diff --git a/src/util/signalcatcher.cpp b/src/util/signalcatcher.cpp index bdd7d63..88d299c 100644 --- a/src/util/signalcatcher.cpp +++ b/src/util/signalcatcher.cpp @@ -1,142 +1,142 @@ /*************************************************************************** * Copyright (C) 2010 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "signalcatcher.h" #include #include #include -#include "signalcatcher.h" #include "log.h" #ifndef Q_WS_WIN namespace bt { sigjmp_buf sigbus_env; static bool siglongjmp_safe = false; static void sigbus_handler(int sig, siginfo_t *siginfo, void *ptr) { Q_UNUSED(siginfo); Q_UNUSED(ptr); Q_UNUSED(sig); // Jump to error handling code, // ignore signal if we are not safe to jump if (siglongjmp_safe) siglongjmp(sigbus_env, 1); } static bool InstallBusHandler() { struct sigaction act; memset(&act, 0, sizeof(act)); if (sigaction(SIGBUS, 0, &act) != -1 && act.sa_sigaction == sigbus_handler) return true; act.sa_sigaction = sigbus_handler; act.sa_flags = SA_SIGINFO; if (sigaction(SIGBUS, &act, 0) == -1) { Out(SYS_GEN|LOG_IMPORTANT) << "Failed to set SIGBUS handler" << endl; return false; } return true; } BusErrorGuard::BusErrorGuard() { InstallBusHandler(); siglongjmp_safe = true; } BusErrorGuard::~BusErrorGuard() { siglongjmp_safe = false; } BusError::BusError(bool write_operation) : Error(write_operation ? i18n("Error when writing to disk") : i18n("Error when reading from disk")), write_operation(write_operation) { } BusError::~BusError() { } int SignalCatcher::signal_received_pipe[2]; SignalCatcher::SignalCatcher(QObject* parent) : QObject(parent), notifier(0) { socketpair(AF_UNIX, SOCK_STREAM, 0, signal_received_pipe); notifier = new QSocketNotifier(signal_received_pipe[1], QSocketNotifier::Read, this); connect(notifier, SIGNAL(activated(int)), this, SLOT(handleInput(int))); } SignalCatcher::~SignalCatcher() { } void SignalCatcher::signalHandler(int sig, siginfo_t* siginfo, void* ptr) { Q_UNUSED(siginfo); Q_UNUSED(ptr); ::write(signal_received_pipe[0], &sig, sizeof(int)); } bool SignalCatcher::catchSignal(int sig) { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_sigaction = SignalCatcher::signalHandler; act.sa_flags = SA_SIGINFO; if (sigaction(sig, &act, 0) == -1) { Out(SYS_GEN|LOG_IMPORTANT) << "Failed to set signal handler for " << sig << endl; return false; } return true; } void SignalCatcher::handleInput(int fd) { int sig = 0; ::read(fd, &sig, sizeof(int)); Out(SYS_GEN|LOG_IMPORTANT) << "Signal " << sig << " caught " << endl; emit triggered(); } } #endif diff --git a/src/util/signalcatcher.h b/src/util/signalcatcher.h index ab94bdd..989a020 100644 --- a/src/util/signalcatcher.h +++ b/src/util/signalcatcher.h @@ -1,104 +1,104 @@ /*************************************************************************** * Copyright (C) 2010 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef BT_SIGNALCATCHER_H #define BT_SIGNALCATCHER_H #ifndef Q_WS_WIN #include #include #include #include #include #include namespace bt { /** Variable used to jump from the SIGBUS handler back to the place which triggered the SIGBUS. */ extern KTORRENT_EXPORT sigjmp_buf sigbus_env; /** * Protects against SIGBUS errors when doing mmapped IO **/ class KTORRENT_EXPORT BusErrorGuard { public: BusErrorGuard(); virtual ~BusErrorGuard(); }; /** Exception throw when a SIGBUS is caught. */ class KTORRENT_EXPORT BusError : public bt::Error { public: BusError(bool write_operation); virtual ~BusError(); - /// Wether or not the SIGBUS was triggered by a write operation + /// Whether or not the SIGBUS was triggered by a write operation bool write_operation; }; /** * Class to handle UNIX signals (not Qt ones) */ class KTORRENT_EXPORT SignalCatcher : public QObject { Q_OBJECT public: SignalCatcher(QObject* parent = 0); virtual ~SignalCatcher(); /** * Catch a UNIX signal * @param sig SIGINT, SIGTERM or some other signal * @return true upon success, false otherwise **/ bool catchSignal(int sig); private Q_SLOTS: void handleInput(int fd); private: static void signalHandler(int sig, siginfo_t *siginfo, void *ptr); Q_SIGNALS: /// Emitted when a void triggered(); private: QSocketNotifier* notifier; static int signal_received_pipe[2]; }; } /// Before writing to memory mapped data, call this macro to ensure that SIGBUS signals are caught and properly dealt with #define BUS_ERROR_WPROTECT() BusErrorGuard bus_error_guard; if (sigsetjmp(bt::sigbus_env, 1)) throw bt::BusError(true) /// Before reading from memory mapped data, call this macro to ensure that SIGBUS signals are caught and properly dealt with #define BUS_ERROR_RPROTECT() BusErrorGuard bus_error_guard; if (sigsetjmp(bt::sigbus_env, 1)) throw bt::BusError(false) #endif #endif // BT_SIGNALCATCHER_H diff --git a/src/utp/pollpipe.cpp b/src/utp/pollpipe.cpp index 80eacfa..daacde5 100644 --- a/src/utp/pollpipe.cpp +++ b/src/utp/pollpipe.cpp @@ -1,47 +1,47 @@ /*************************************************************************** * Copyright (C) 2010 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include #include "pollpipe.h" +#include #include "connection.h" using namespace bt; namespace utp { PollPipe::PollPipe(net::Poll::Mode mode) : mode(mode), poll_index(-1) { } PollPipe::~PollPipe() { } void PollPipe::reset() { QMutexLocker lock(&mutex); poll_index = -1; conn_ids.reset(); } } diff --git a/src/utp/remotewindow.h b/src/utp/remotewindow.h index 6dd1499..685222e 100644 --- a/src/utp/remotewindow.h +++ b/src/utp/remotewindow.h @@ -1,134 +1,134 @@ /*************************************************************************** * Copyright (C) 2009 by Joris Guisson * * joris.guisson@gmail.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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef UTP_REMOTEWINDOW_H #define UTP_REMOTEWINDOW_H #include #include #include #include #include #include #include namespace utp { struct SelectiveAck; struct Header; struct UnackedPacket { UnackedPacket(const PacketBuffer & packet, bt::Uint16 seq_nr, bt::TimeStamp send_time); ~UnackedPacket(); PacketBuffer packet; bt::Uint16 seq_nr; bt::TimeStamp send_time; bool retransmitted; }; /** The Retransmitter provides is an interface class to retransmit packets */ class KTORRENT_EXPORT Retransmitter { public: virtual ~Retransmitter() {} /// Update the RTT time virtual void updateRTT(const Header* hdr, bt::Uint32 packet_rtt, bt::Uint32 packet_size) = 0; /// Retransmit a packet virtual void retransmit(PacketBuffer & packet, bt::Uint16 p_seq_nr) = 0; /// Get the current timeout virtual bt::Uint32 currentTimeout() const = 0; }; /** Keeps track of the remote sides window including all packets inflight. */ class KTORRENT_EXPORT RemoteWindow { public: RemoteWindow(); virtual ~RemoteWindow(); /// A packet was received (update window size and check for acks) void packetReceived(const Header* hdr, const SelectiveAck* sack, Retransmitter* conn); /// Add a packet to the remote window (should include headers) void addPacket(const PacketBuffer & packet, bt::Uint16 seq_nr, bt::TimeStamp send_time); /// Are we allowed to send bool allowedToSend(bt::Uint32 packet_size) const { return cur_window + packet_size <= qMin(wnd_size, max_window); } /// Calculates how much window space is availabe bt::Uint32 availableSpace() const { bt::Uint32 m = qMin(wnd_size, max_window); if (cur_window > m) return 0; else return m - cur_window; } /// See if all packets are acked bool allPacketsAcked() const {return unacked_packets.isEmpty();} /// Get the number of unacked packets bt::Uint32 numUnackedPackets() const {return unacked_packets.count();} - /// A timeout occured + /// A timeout occurred void timeout(Retransmitter* conn); /// Get the window usage factor double windowUsageFactor() const {return qMax((double)cur_window / max_window, 1.0);} /// Update the window size void updateWindowSize(double scaled_gain); bt::Uint32 currentWindow() const {return cur_window;} bt::Uint32 maxWindow() const {return max_window;} bt::Uint32 windowSize() const {return wnd_size;} /// Clear the window void clear(); private: void checkLostPackets(const Header* hdr, const SelectiveAck* sack, Retransmitter* conn); bt::Uint16 lost(const SelectiveAck* sack); private: bt::Uint32 cur_window; bt::Uint32 max_window; bt::Uint32 wnd_size; // advertised window size from the other side QList unacked_packets; bt::Uint16 last_ack_nr; bt::Uint32 last_ack_receive_count; }; } #endif // UTP_REMOTEWINDOW_H diff --git a/testlib/utils.cpp b/testlib/utils.cpp index 1cc688d..38b8691 100644 --- a/testlib/utils.cpp +++ b/testlib/utils.cpp @@ -1,27 +1,27 @@ /*************************************************************************** * Copyright (C) 2012 by * * Joris Guisson * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "utils.h" bt::Uint64 RandomSize(bt::Uint64 min_size,bt::Uint64 max_size) { bt::Uint64 r = max_size - min_size; return min_size + qrand() % r; -} \ No newline at end of file +}