diff ui/sslconnection_curl.cpp @ 910:eaed02defe6a

More SSLConnection refactoring. Fixes curl downloader.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 18 Aug 2014 18:51:33 +0200
parents d1c951b3012d
children a55c6cf54365
line wrap: on
line diff
--- a/ui/sslconnection_curl.cpp	Thu Aug 14 11:24:13 2014 +0200
+++ b/ui/sslconnection_curl.cpp	Mon Aug 18 18:51:33 2014 +0200
@@ -7,9 +7,17 @@
  */
 
 #include "sslconnection_curl.h"
+#include <QSaveFile>
 
 #define CONNECTION_DEBUG
 
+/**@def Wrapper macro around curl_easy_setopt invocation */
+#define CURL_SETOPT(x, y, z) \
+    if (curl_easy_setopt(mCurl, x, y) != CURLE_OK) { \
+        qDebug() << "Setopt failed"; \
+        z; \
+    }
+
 SSLConnectionCurl::SSLConnectionCurl(const QString& url,
                              const QByteArray& certificate):
     SSLConnection (url, certificate),
@@ -23,11 +31,6 @@
         return;
     }
 
-    if (curl_easy_setopt(mCurl, CURLOPT_URL, QUrl(url).toEncoded().constData()) != CURLE_OK) {
-        qDebug() << "Setting url failed";
-        return;
-    }
-
     if (curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, 1L) != CURLE_OK) {
         /* Should be default anyway */
         qDebug() << "Setting verifypeer failed";
@@ -40,6 +43,11 @@
         return;
     }
 
+    if (curl_easy_setopt(mCurl, CURLOPT_ERRORBUFFER, mErrBuf) != CURLE_OK) {
+        qDebug() << "Setting errorbuf failed";
+        return;
+    }
+
     mCertFile.open();
     if (mCertFile.write(mPinnedCert) != mPinnedCert.size()) {
         qDebug() << "Failed to write temporary certificate";
@@ -70,12 +78,25 @@
 }
 
 int SSLConnectionCurl::connect() {
+    CURLcode retval;
+
+    if (curl_easy_setopt(mCurl, CURLOPT_URL, mUrl.toEncoded().constData()) != CURLE_OK) {
+        qDebug() << "Failed to set URL";
+        return -1;
+    }
+
     if (curl_easy_setopt(mCurl, CURLOPT_CONNECT_ONLY, 1L) != CURLE_OK) {
         qDebug() << "Failed to set connect only option";
         return -1;
     }
-    if (curl_easy_perform (mCurl) != CURLE_OK) {
-        qDebug() << "Failed to connect";
+    retval = curl_easy_perform(mCurl);
+    if (retval != CURLE_OK) {
+        qDebug() << "Failed to connect: " << mErrBuf << " retval: " << retval;
+        if (retval == CURLE_PEER_FAILED_VERIFICATION) {
+            mErrorState = InvalidCertificate;
+            return -1;
+        }
+
         mErrorState = NoConnection;
         return -1;
     }
@@ -83,51 +104,128 @@
     return 0;
 }
 
-int SSLConnectionCurl::write(const QByteArray& request) {
-    size_t written = 0;
+/* Globally do this as we can't pass this to the c function */
+size_t ssl_curl_max_write, ssl_curl_written;
 
-    if (curl_easy_send (mCurl, request.constData(), request.size(), &written) != CURLE_OK) {
-        qDebug() << "Failed to send request";
-        return -1;
+size_t write_data(void *ptr, size_t size, size_t nmemb,
+        QSaveFile *fp)
+{
+    qDebug() << "Writing size: " << size << " * " << nmemb;
+    if (ssl_curl_max_write < ssl_curl_written) {
+        qDebug() << "Aborting write. Too much data.";
+        return 0;
     }
-    if (written != (size_t)request.size()) {
-        qDebug() << "Failed to write everything";
-        return -1;
+    size_t written = fp->write((const char *)ptr, size * nmemb);
+    if (written != size * nmemb) {
+        qDebug() << "Failed to write data. Written: " << written 
+                 << " requested: " << size * nmemb;
+        return 0;
     }
-    return 0;
+    ssl_curl_written += written;
+    return written;
 }
 
-QByteArray SSLConnectionCurl::read(size_t len)
+bool SSLConnectionCurl::downloadFile(const QString &resource,
+                                     const QString &fileName,
+                                     size_t maxSize)
 {
-    unsigned char buf[len];
-    QByteArray retval("");
-    CURLcode ret;
-    size_t read = 0;
-    unsigned int tries = 0;
+    QSaveFile outputFile(fileName);
+    ssl_curl_written = 0;
+    ssl_curl_max_write = maxSize;
+    // Open / Create the file to write to.
+    if (!outputFile.open(QIODevice::WriteOnly)) {
+        qDebug() << "Failed to open file";
+        return false;
+    }
+    QUrl urlCopy = mUrl;
+    urlCopy.setPath(resource);
 
-    do {
-        memset (buf, 0, sizeof(buf));
-        ret = curl_easy_recv (mCurl, buf, len, &read);
-        if (ret == CURLE_OK && read == 0) {
-            return retval;
-        }
-        if (ret == CURLE_AGAIN) {
-            tries++;
-            continue;
-        }
-        if (ret != CURLE_OK) {
-            qDebug() << "Read failed.";
-            return QByteArray();
-        }
-        if (len < (len - read)) {
-            /* Should never happen if ssl_read behaves */
-            qDebug() << "integer overflow in polarSSLRead";
-            return QByteArray();
-        }
+    if (curl_easy_setopt(mCurl, CURLOPT_URL, urlCopy.toEncoded().constData()) != CURLE_OK) {
+        qDebug() << "Failed to set URL";
+        return false;
+    }
 
-        len -= read;
-        retval.append((const char *)buf, read);
-    } while (len > 0 && tries < 10);
+    if (curl_easy_setopt(mCurl, CURLOPT_CONNECT_ONLY, 0L) != CURLE_OK) {
+        qDebug() << "Failed to set connect";
+        return false;
+    }
 
-    return retval;
+    if (curl_easy_setopt(mCurl, CURLOPT_HEADER, 0L) != CURLE_OK) {
+        qDebug() << "Failed to set header";
+        return false;
+    }
+
+    if (curl_easy_setopt(mCurl, CURLOPT_NOBODY, 0L) != CURLE_OK) {
+        qDebug() << "Failed to set no body";
+        return false;
+    }
+
+    if (curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, write_data) != CURLE_OK) {
+        qDebug() << "Failed to set write function";
+        return false;
+    }
+
+    if (curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, &outputFile) != CURLE_OK) {
+        qDebug() << "Failed to set write function";
+        return false;
+    }
+
+    if (curl_easy_perform (mCurl) != CURLE_OK) {
+        qDebug() << "Failed to perform download.";
+        return false;
+    }
+
+    if (!outputFile.commit()) {
+        qDebug() << "Failed to commit data to filesystem.";
+        return false;
+    }
+
+    return true;
 }
+
+QDateTime SSLConnectionCurl::getLastModifiedHeader(const QString &resource) {
+    QUrl urlCopy = mUrl;
+    urlCopy.setPath(resource);
+
+    if (curl_easy_setopt(mCurl, CURLOPT_URL, urlCopy.toEncoded().constData()) != CURLE_OK) {
+        qDebug() << "Failed to set URL";
+        return QDateTime();
+    }
+
+    if (curl_easy_setopt(mCurl, CURLOPT_CONNECT_ONLY, 0L) != CURLE_OK) {
+        qDebug() << "Failed to set connect";
+        return QDateTime();
+    }
+
+    if (curl_easy_setopt(mCurl, CURLOPT_HEADER, 1L) != CURLE_OK) {
+        qDebug() << "Failed to set header";
+        return QDateTime();
+    }
+
+    if (curl_easy_setopt(mCurl, CURLOPT_NOBODY, 1L) != CURLE_OK) {
+        qDebug() << "Failed to set no body";
+        return QDateTime();
+    }
+
+    if (curl_easy_setopt(mCurl, CURLOPT_FILETIME, 1L) != CURLE_OK) {
+        qDebug() << "Failed to set filetime";
+        return QDateTime();
+    }
+
+    if (curl_easy_perform (mCurl) != CURLE_OK) {
+        qDebug() << "Failed to perform last modified check.";
+        return QDateTime();
+    }
+    long filetime = 0;
+
+    if (curl_easy_getinfo (mCurl, CURLINFO_FILETIME, &filetime) != CURLE_OK) {
+        qDebug() << "Failed to get filetime";
+        return QDateTime();
+    }
+
+    if (filetime == -1) {
+        qDebug() << "Invalid Time";
+        return QDateTime();
+    }
+    return QDateTime::fromTime_t(filetime);
+}

http://wald.intevation.org/projects/trustbridge/