From 61eb921e444da2e4c4216ade5769f4b6146e7078 Mon Sep 17 00:00:00 2001 From: Christophe Dumez Date: Fri, 23 Nov 2007 22:57:49 +0000 Subject: [PATCH] - Moved from libcommoncpp to libcurl for downloads from urls - Added SOCKS5 proxy support when downloading from urls --- INSTALL | 2 +- configure | 58 +++++++++--------- qbittorrent.qc | 2 +- qcm/libcurl.qcm | 61 +++++++++++++++++++ src/downloadThread.cpp | 133 ++++++++++++++++++++--------------------- src/downloadThread.h | 16 +---- src/src.pro | 2 +- 7 files changed, 157 insertions(+), 117 deletions(-) create mode 100644 qcm/libcurl.qcm 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/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/downloadThread.cpp b/src/downloadThread.cpp index 995506bd1..9c4478f75 100644 --- a/src/downloadThread.cpp +++ b/src/downloadThread.cpp @@ -21,66 +21,45 @@ #include "downloadThread.h" #include -#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_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_LOGIN_DENIED: 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 - QString IP = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/IP"), "0.0.0.0").toString(); - qDebug("Set proxy, hostname: %s, port: %d", IP.toUtf8().data(), settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Port"), 8080).toInt()); - if(intValue==1 || intValue==3) { - if(!IP.startsWith("http://", Qt::CaseInsensitive)) { - // HTTP Proxy without leading http:// - url_stream->setProxy((QString("http://")+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()); - }else { - 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()) { - qDebug("Proxy auth required"); - // 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()); - } - } //TODO: Support SOCKS5 proxies - } -} +subDownloadThread::subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){} subDownloadThread::~subDownloadThread(){ abort = true; wait(); - delete url_stream; } void subDownloadThread::run(){ @@ -91,38 +70,54 @@ 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()); + // 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/src.pro b/src/src.pro index 00058e73c..ae33fcf0e 100644 --- a/src/src.pro +++ b/src/src.pro @@ -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)