diff --git a/INSTALL b/INSTALL index 8f44209ca..3e1a62f0c 100644 --- a/INSTALL +++ b/INSTALL @@ -25,7 +25,7 @@ Dependencies: - libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization -- libcommoncpp2 +- libcurl - python >= 2.3 (needed by search engine) diff --git a/TODO b/TODO index 73e9b7de9..10a098c3e 100644 --- a/TODO +++ b/TODO @@ -50,14 +50,11 @@ -> in download list -> in seeding list -// TODO v1.0.0 -- Fix libcommoncpp proxy support -- Fix layout in preferences/Proxy - rc8->rc9 changelog: - FEATURE: Better media file preview (player detected automatically) - BUGFIX: Remember properties window size and position -- BUGFIX: Added proxy support in search engine, RSS, downloads from urls +- BUGFIX: Added HTTP and SOCKS5 proxy support in downloads from urls, RSS +- BUGFIX: Added HTTP proxy support in search engine (no SOCKS yet) - BUGFIX: Do no pause torrents before saving fastresume data anymore (no longer needed) - BUGFIX: Save fast resume data regularly (every 60 seconds) to avoid downloading from scratch if qBittorrent crashes - BUGFIX: Do not save fastresume data for checking torrents anymore @@ -66,3 +63,6 @@ rc8->rc9 changelog: - BUGFIX: Fixed possible overflow in ETA calculation - BUGFIX: title bar is now reset when "Display speed in title" is disabled - BUGFIX: Fixed HTTP_PW and SOCKS5_PW in proxy combobox +- BUGFIX: Fixed proxy auth disable problem when disabling proxy +- BUGFIX: Fixed proxy layout in program preferences +- BUGFIX: Fixed everlasting libtorrent session destruction on exit diff --git a/configure b/configure index 5f8fbcdcd..ac0af4282 100755 --- a/configure +++ b/configure @@ -22,8 +22,8 @@ Dependency options: --with-libtorrent-lib=[path] Path to libtorrent library files --with-libtorrent-static-lib=[path] Path to libtorrent .a file --with-libboost-inc=[path] Path to libboost include files - --with-libcommoncpp2-inc=[path] Path to libcommoncpp2 include files - --with-libcommoncpp2-lib=[path] Path to libcommoncpp2 library files + --with-libcurl-inc=[path] Path to libcurl include files + --with-libcurl-lib=[path] Path to libcurl library files --disable-libmagick Disable use of libmagick --with-libmagick-inc=[path] Path to libmagick++ include files --with-libmagick-lib=[path] Path to libmagick++ library files @@ -166,13 +166,13 @@ while [ $# -gt 0 ]; do shift ;; - --with-libcommoncpp2-inc=*) - QC_WITH_LIBCOMMONCPP2_INC=$optarg + --with-libcurl-inc=*) + QC_WITH_LIBCURL_INC=$optarg shift ;; - --with-libcommoncpp2-lib=*) - QC_WITH_LIBCOMMONCPP2_LIB=$optarg + --with-libcurl-lib=*) + QC_WITH_LIBCURL_LIB=$optarg shift ;; @@ -231,8 +231,8 @@ echo QC_WITH_LIBTORRENT_INC=$QC_WITH_LIBTORRENT_INC echo QC_WITH_LIBTORRENT_LIB=$QC_WITH_LIBTORRENT_LIB echo QC_WITH_LIBTORRENT_STATIC_LIB=$QC_WITH_LIBTORRENT_STATIC_LIB echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC -echo QC_WITH_LIBCOMMONCPP2_INC=$QC_WITH_LIBCOMMONCPP2_INC -echo QC_WITH_LIBCOMMONCPP2_LIB=$QC_WITH_LIBCOMMONCPP2_LIB +echo QC_WITH_LIBCURL_INC=$QC_WITH_LIBCURL_INC +echo QC_WITH_LIBCURL_LIB=$QC_WITH_LIBCURL_LIB echo QC_DISABLE_libmagick=$QC_DISABLE_libmagick echo QC_WITH_LIBMAGICK_INC=$QC_WITH_LIBMAGICK_INC echo QC_WITH_LIBMAGICK_LIB=$QC_WITH_LIBMAGICK_LIB @@ -485,25 +485,25 @@ public: return true; } }; -#line 1 "libcommoncpp2.qcm" +#line 1 "libcurl.qcm" /* -----BEGIN QCMOD----- name: libcommoncpp2 -arg: with-libcommoncpp2-inc=[path], Path to libcommoncpp2 include files -arg: with-libcommoncpp2-lib=[path], Path to libcommoncpp2 library files +arg: with-libcurl-inc=[path], Path to libcurl include files +arg: with-libcurl-lib=[path], Path to libcurl library files -----END QCMOD----- */ -class qc_libcommoncpp2 : public ConfObj +class qc_libcurl : public ConfObj { public: - qc_libcommoncpp2(Conf *c) : ConfObj(c) {} - QString name() const { return "GNU Common C++ library (libcommoncpp2)"; } - QString shortname() const { return "libcommoncpp2"; } + qc_libcurl(Conf *c) : ConfObj(c) {} + QString name() const { return "libcurl"; } + QString shortname() const { return "libcurl"; } bool exec(){ QString s; - s = conf->getenv("QC_WITH_LIBCOMMONCPP2_INC"); + s = conf->getenv("QC_WITH_LIBCURL_INC"); if(!s.isEmpty()) { - if(!conf->checkHeader(s, "cc++/url.h")) { + if(!conf->checkHeader(s, "curl/curl.h")) { return false; } }else{ @@ -512,7 +512,7 @@ public: sl << "/usr/local/include"; bool found = false; foreach(s, sl){ - if(conf->checkHeader(s, "cc++/url.h")){ + if(conf->checkHeader(s, "curl/curl.h")){ found = true; break; } @@ -523,12 +523,10 @@ public: } conf->addIncludePath(s); - s = conf->getenv("QC_WITH_LIBCOMMONCPP2_LIB"); + s = conf->getenv("QC_WITH_LIBCURL_LIB"); if(!s.isEmpty()) { - if(!QFile::exists(s+QString("/libccext2.so"))) - return false; - if(!QFile::exists(s+QString("/libccgnu2.so"))) - return false; + if(!QFile::exists(s+QString("/libcurl.so"))) + return false; conf->addLib(QString("-L") + s); }else{ QStringList sl; @@ -538,11 +536,9 @@ public: sl << "/usr/local/lib64/"; bool found = false; foreach(s, sl){ - if(QFile::exists(s+QString("libccext2.so"))){ - if(QFile::exists(s+QString("libccgnu2.so"))){ - found = true; - break; - } + if(QFile::exists(s+QString("libcurl.so"))){ + found = true; + break; } } if(!found) return false; @@ -715,7 +711,7 @@ cat >$1/modules_new.cpp <required = true; o->disabled = false; - o = new qc_libcommoncpp2(conf); + o = new qc_libcurl(conf); o->required = true; o->disabled = false; o = new qc_libmagick(conf); @@ -1673,8 +1669,8 @@ export QC_WITH_LIBTORRENT_INC export QC_WITH_LIBTORRENT_LIB export QC_WITH_LIBTORRENT_STATIC_LIB export QC_WITH_LIBBOOST_INC -export QC_WITH_LIBCOMMONCPP2_INC -export QC_WITH_LIBCOMMONCPP2_LIB +export QC_WITH_LIBCURL_INC +export QC_WITH_LIBCURL_LIB export QC_DISABLE_libmagick export QC_WITH_LIBMAGICK_INC export QC_WITH_LIBMAGICK_LIB diff --git a/qbittorrent.qc b/qbittorrent.qc index 4121c05ac..c8b4e646b 100644 --- a/qbittorrent.qc +++ b/qbittorrent.qc @@ -12,7 +12,7 @@ - + diff --git a/qcm/libcurl.qcm b/qcm/libcurl.qcm new file mode 100644 index 000000000..b3af39b4a --- /dev/null +++ b/qcm/libcurl.qcm @@ -0,0 +1,61 @@ +/* +-----BEGIN QCMOD----- +name: libcommoncpp2 +arg: with-libcurl-inc=[path], Path to libcurl include files +arg: with-libcurl-lib=[path], Path to libcurl library files +-----END QCMOD----- +*/ +class qc_libcurl : public ConfObj +{ +public: + qc_libcurl(Conf *c) : ConfObj(c) {} + QString name() const { return "libcurl"; } + QString shortname() const { return "libcurl"; } + bool exec(){ + QString s; + s = conf->getenv("QC_WITH_LIBCURL_INC"); + if(!s.isEmpty()) { + if(!conf->checkHeader(s, "curl/curl.h")) { + return false; + } + }else{ + QStringList sl; + sl << "/usr/include"; + sl << "/usr/local/include"; + bool found = false; + foreach(s, sl){ + if(conf->checkHeader(s, "curl/curl.h")){ + found = true; + break; + } + } + if(!found) { + return false; + } + } + conf->addIncludePath(s); + + s = conf->getenv("QC_WITH_LIBCURL_LIB"); + if(!s.isEmpty()) { + if(!QFile::exists(s+QString("/libcurl.so"))) + return false; + conf->addLib(QString("-L") + s); + }else{ + QStringList sl; + sl << "/usr/lib/"; + sl << "/usr/lib64/"; + sl << "/usr/local/lib/"; + sl << "/usr/local/lib64/"; + bool found = false; + foreach(s, sl){ + if(QFile::exists(s+QString("libcurl.so"))){ + found = true; + break; + } + } + if(!found) return false; + conf->addLib(QString("-L") + s); + } + return true; + } +}; diff --git a/src/bittorrent.cpp b/src/bittorrent.cpp index 826883648..7d84e02d8 100644 --- a/src/bittorrent.cpp +++ b/src/bittorrent.cpp @@ -43,7 +43,7 @@ #define MAX_TRACKER_ERRORS 2 // Main constructor -bittorrent::bittorrent() : timerScan(0), DHTEnabled(false), preAllocateAll(false), addInPause(false), maxConnecsPerTorrent(500), maxUploadsPerTorrent(4), max_ratio(-1) { +bittorrent::bittorrent() : timerScan(0), DHTEnabled(false), preAllocateAll(false), addInPause(false), maxConnecsPerTorrent(500), maxUploadsPerTorrent(4), max_ratio(-1), UPnPEnabled(false), NATPMPEnabled(false), LSDEnabled(false) { // To avoid some exceptions fs::path::default_name_check(fs::no_check); // Creating bittorrent session @@ -59,7 +59,7 @@ bittorrent::bittorrent() : timerScan(0), DHTEnabled(false), preAllocateAll(false connect(ETARefresher, SIGNAL(timeout()), this, SLOT(updateETAs())); ETARefresher->start(ETA_REFRESH_INTERVAL); fastResumeSaver = new QTimer(); - connect(fastResumeSaver, SIGNAL(timeout()), this, SLOT(saveFastResumeAndRatioDataUnfinished())); + connect(fastResumeSaver, SIGNAL(timeout()), this, SLOT(saveFastResumeAndRatioData())); fastResumeSaver->start(60000); // To download from urls downloader = new downloadThread(this); @@ -321,6 +321,8 @@ bool bittorrent::pauseTorrent(QString hash) { if(h.is_valid() && !h.is_paused()) { h.pause(); change = true; + // Save fast resume data + saveFastResumeAndRatioData(hash); qDebug("Torrent paused successfully"); }else{ if(!h.is_valid()) { @@ -629,25 +631,49 @@ bool bittorrent::isDHTEnabled() const{ void bittorrent::enableUPnP(bool b) { if(b) { - s->start_upnp(); + if(!UPnPEnabled) { + qDebug("Enabling UPnP"); + s->start_upnp(); + UPnPEnabled = true; + } } else { - s->stop_upnp(); + if(UPnPEnabled) { + qDebug("Disabling UPnP"); + s->stop_upnp(); + UPnPEnabled = false; + } } } void bittorrent::enableNATPMP(bool b) { if(b) { - s->start_natpmp(); + if(!NATPMPEnabled) { + qDebug("Enabling NAT-PMP"); + s->start_natpmp(); + NATPMPEnabled = true; + } } else { - s->stop_natpmp(); + if(NATPMPEnabled) { + qDebug("Disabling NAT-PMP"); + s->stop_natpmp(); + NATPMPEnabled = false; + } } } void bittorrent::enableLSD(bool b) { if(b) { - s->start_lsd(); + if(!LSDEnabled) { + qDebug("Enabling LSD"); + s->start_lsd(); + LSDEnabled = true; + } } else { - s->stop_lsd(); + if(LSDEnabled) { + qDebug("Disabling LSD"); + s->stop_lsd(); + LSDEnabled = false; + } } } @@ -833,32 +859,23 @@ void bittorrent::saveDownloadUploadForTorrent(QString hash) { ratio_file.close(); } -// Only save fast resume data for unfinished torrents (Optimization) -void bittorrent::saveFastResumeAndRatioDataUnfinished() { +// Only save fast resume data for unfinished and unpaused torrents (Optimization) +// Called periodically and on exit +void bittorrent::saveFastResumeAndRatioData() { QString hash; QStringList hashes = getUnfinishedTorrents(); foreach(hash, hashes) { - saveFastResumeAndRatioData(hash); - } -} - -// Save fastresume data for all torrents (called periodically) -void bittorrent::saveFastResumeAndRatioData() { - qDebug("Saving fast resume and ratio data"); - std::vector handles = s->get_torrents(); - // It is not necessary to pause the torrents before saving fastresume data anymore - // because we either use Full allocation or sparse mode. - // Write fast resume data - for(unsigned int i=0; i -#include #include +#include -QString subDownloadThread::errorCodeToString(int status) { +// http://curl.rtin.bz/libcurl/c/libcurl-errors.html +QString subDownloadThread::errorCodeToString(CURLcode status) { switch(status){ - case 1://ost::URLStream::errUnreachable: + case CURLE_FTP_CANT_GET_HOST: + case CURLE_COULDNT_RESOLVE_HOST: return tr("Host is unreachable"); - case 2://ost::URLStream::errMissing: + case CURLE_READ_ERROR: + case CURLE_FILE_COULDNT_READ_FILE: return tr("File was not found (404)"); - case 3://ost::URLStream::errDenied: + case CURLE_FTP_ACCESS_DENIED: + case CURLE_LOGIN_DENIED: + case CURLE_FTP_USER_PASSWORD_INCORRECT: return tr("Connection was denied"); - case 4://ost::URLStream::errInvalid: + case CURLE_URL_MALFORMAT: return tr("Url is invalid"); - case 5://ost::URLStream::errForbidden: - return tr("Connection forbidden (403)"); - case 6://ost::URLStream::errUnauthorized: - return tr("Connection was not authorized (401)"); - case 7://ost::URLStream::errRelocated: - return tr("Content has moved (301)"); - case 8://ost::URLStream::errFailure: + case CURLE_COULDNT_RESOLVE_PROXY: + return tr("Could not resolve proxy"); + //case 5: + // return tr("Connection forbidden (403)"); + //case 6: + // return tr("Connection was not authorized (401)"); + //case 7: + // return tr("Content has moved (301)"); + case CURLE_COULDNT_CONNECT: return tr("Connection failure"); - case 9://ost::URLStream::errTimeout: + case CURLE_OPERATION_TIMEOUTED: return tr("Connection was timed out"); - case 10://ost::URLStream::errInterface: + case CURLE_INTERFACE_FAILED: return tr("Incorrect network interface"); default: return tr("Unknown error"); } } -subDownloadThread::subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){ - url_stream = new ost::URLStream(); - // Proxy support - QSettings settings("qBittorrent", "qBittorrent"); - int intValue = settings.value(QString::fromUtf8("Preferences/Connection/ProxyType"), 0).toInt(); - if(intValue > 0) { - // Proxy enabled - url_stream->setProxy(settings.value(QString::fromUtf8("Preferences/Connection/Proxy/IP"), "0.0.0.0").toString().toUtf8().data(), settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Port"), 8080).toInt()); - if(settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Authentication"), false).toBool()) { - // Authentication required - url_stream->setProxyUser(settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Username"), QString()).toString().toUtf8().data()); - url_stream->setProxyPassword(settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Password"), QString()).toString().toUtf8().data()); - } - } -} +subDownloadThread::subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){} subDownloadThread::~subDownloadThread(){ abort = true; wait(); - delete url_stream; } void subDownloadThread::run(){ @@ -81,38 +73,57 @@ void subDownloadThread::run(){ filePath = tmpfile->fileName(); } delete tmpfile; - QFile dest_file(filePath); - if(!dest_file.open(QIODevice::WriteOnly | QIODevice::Text)){ - std::cerr << "Error: could't create temporary file: " << (const char*)filePath.toUtf8() << '\n'; + FILE *f = fopen(filePath.toUtf8().data(), "w"); + if(!f) { + std::cerr << "couldn't open destination file" << "\n"; return; } - ost::URLStream::Error status = url_stream->get((const char*)url.toUtf8()); - if(status){ - // Failure - QString error_msg = errorCodeToString((int)status); - qDebug("Download failed for %s, reason: %s", (const char*)url.toUtf8(), (const char*)error_msg.toUtf8()); - url_stream->close(); - emit downloadFailureST(this, url, error_msg); - return; - } - qDebug("Downloading %s...", (const char*)url.toUtf8()); - char cbuf[1024]; - int len; - while(!url_stream->eof()) { - url_stream->read(cbuf, sizeof(cbuf)); - len = url_stream->gcount(); - if(len > 0) - dest_file.write(cbuf, len); - if(abort){ - dest_file.close(); - url_stream->close(); - return; + CURL *curl; + CURLcode res; + curl = curl_easy_init(); + if(curl) { + std::string c_url = url.toUtf8().data(); + curl_easy_setopt(curl, CURLOPT_URL, c_url.c_str()); + // SSL support + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + // PROXY SUPPORT + QSettings settings("qBittorrent", "qBittorrent"); + int intValue = settings.value(QString::fromUtf8("Preferences/Connection/ProxyType"), 0).toInt(); + if(intValue > 0) { + // Proxy enabled + QString IP = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/IP"), "0.0.0.0").toString(); + QString port = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Port"), 8080).toString(); + qDebug("Using proxy: %s", (IP+QString(":")+port).toUtf8().data()); + curl_easy_setopt(curl, CURLOPT_PROXYPORT, (IP+QString(":")+port).toUtf8().data()); + // Default proxy type is HTTP, we must change if it is SOCKS5 + if(intValue%2==0) { + qDebug("Proxy is SOCKS5, not HTTP"); + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + } + // Authentication? + if(intValue > 2) { + qDebug("Proxy requires authentication, authenticating"); + QString username = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Username"), QString()).toString(); + QString password = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Password"), QString()).toString(); + curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, (username+QString(":")+password).toUtf8().data()); + } } + // TODO: define CURLOPT_WRITEFUNCTION or it will crash on windows + curl_easy_setopt(curl, CURLOPT_WRITEDATA, f); + qDebug("Downloading %s", url.toUtf8().data()); + res = curl_easy_perform(curl); + /* always cleanup */ + curl_easy_cleanup(curl); + fclose(f); + if(res) { + emit downloadFailureST(this, url, errorCodeToString(res)); + } else { + emit downloadFinishedST(this, url, filePath); + } + } else { + std::cerr << "Could not initialize CURL" << "\n"; } - dest_file.close(); - url_stream->close(); - emit downloadFinishedST(this, url, filePath); - qDebug("download completed here: %s", (const char*)filePath.toUtf8()); } /** Download Thread **/ diff --git a/src/downloadThread.h b/src/downloadThread.h index 4c5d952f1..752b8f534 100644 --- a/src/downloadThread.h +++ b/src/downloadThread.h @@ -29,29 +29,18 @@ #include #include #include - -namespace ost { - class URLStream; -} - -typedef struct { - QString IP; - int port; - QString username; - QString password; -} tmp_proxy; +#include class subDownloadThread : public QThread { Q_OBJECT private: QString url; - ost::URLStream *url_stream; bool abort; public: subDownloadThread(QObject *parent, QString url); ~subDownloadThread(); - QString errorCodeToString(int status); + QString errorCodeToString(CURLcode status); signals: // For subthreads @@ -90,7 +79,6 @@ class downloadThread : public QThread { protected slots: void propagateDownloadedFile(subDownloadThread* st, QString url, QString path); - void propagateDownloadFailure(subDownloadThread* st, QString url, QString reason); }; diff --git a/src/downloadingTorrents.cpp b/src/downloadingTorrents.cpp index e333e9b83..0eabc9979 100644 --- a/src/downloadingTorrents.cpp +++ b/src/downloadingTorrents.cpp @@ -190,7 +190,6 @@ void DownloadingTorrents::setInfoBar(QString info, QColor color) { infoBar->clear(); nbLines = 1; } - qDebug("Color is %s", color.name().toUtf8().data()); infoBar->append(QString::fromUtf8("")+ QTime::currentTime().toString(QString::fromUtf8("hh:mm:ss")) + QString::fromUtf8(" - ") + info + QString::fromUtf8("")); } diff --git a/src/options.ui b/src/options.ui index 3c7247761..62b1302d1 100644 --- a/src/options.ui +++ b/src/options.ui @@ -947,70 +947,72 @@ Affected connections - - - - 10 - 20 - 341 - 22 - - - - Use proxy for connections to trackers - - - true - - - - - - 10 - 40 - 341 - 22 - - - - Use proxy for connections to regular peers - - - true - - - - - - 10 - 60 - 341 - 22 - - - - Use proxy for connections to web seeds - - - true - - - - - - 10 - 80 - 341 - 22 - - - - Use proxy for DHT messages - - - true - - + + + + + + 0 + 0 + + + + Use proxy for connections to trackers + + + true + + + + + + + + 0 + 0 + + + + Use proxy for connections to regular peers + + + true + + + + + + + + 0 + 0 + + + + Use proxy for DHT messages + + + true + + + + + + + + 0 + 0 + + + + Use proxy for connections to web seeds + + + true + + + + diff --git a/src/options_imp.cpp b/src/options_imp.cpp index b26eb0acc..96ac8d95e 100644 --- a/src/options_imp.cpp +++ b/src/options_imp.cpp @@ -871,6 +871,7 @@ void options_imp::enableProxy(int index){ textProxyIP->setEnabled(false); lblProxyPort->setEnabled(false); spinProxyPort->setEnabled(false); + checkProxyAuth->setChecked(false); checkProxyAuth->setEnabled(false); ProxyConnecsBox->setEnabled(false); } diff --git a/src/src.pro b/src/src.pro index 1f59ce52a..8906c9185 100644 --- a/src/src.pro +++ b/src/src.pro @@ -11,7 +11,7 @@ TARGET = qbittorrent CONFIG += qt thread x11 network # Update this VERSION for each release -DEFINES += VERSION=\\\"v1.0.0rc8\\\" +DEFINES += VERSION=\\\"v1.0.0rc9\\\" DEFINES += VERSION_MAJOR=1 DEFINES += VERSION_MINOR=0 DEFINES += VERSION_BUGFIX=0 @@ -81,7 +81,7 @@ QMAKE_CXXFLAGS_RELEASE += -fwrapv -O2 QMAKE_CXXFLAGS_DEBUG += -fwrapv -O1 CONFIG += link_pkgconfig -PKGCONFIG += libtorrent libccext2 libccgnu2 +PKGCONFIG += "libtorrent libcurl" !contains(DEFINES, HAVE_MAGICK){ message(ImageMagick disabled)