Vidalia 0.2.12

CrashReportUploader.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file CrashReportUploader.cpp
00013 ** \brief Uploads a minidump file and any extra information to a crash
00014 ** reporting server.
00015 */
00016 
00017 #include "CrashReportUploader.h"
00018 
00019 #include <QtGlobal>
00020 #include <QDateTime>
00021 #include <QSslSocket>
00022 #include <QSslCertificate>
00023 #include <QHttpRequestHeader>
00024 #include <QHttpResponseHeader>
00025 #include <QMap>
00026 #include <QUrl>
00027 
00028 
00029 CrashReportUploader::CrashReportUploader(QObject *parent)
00030   : QObject(parent),
00031     _requestId(-1)
00032 {
00033   /* Clear the default CA certificate store and add the only one we want */
00034   QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>());
00035   QSslSocket::addDefaultCaCertificates(":/pki/gd-class2-root.crt");
00036 
00037   /* Create the QHttp object used to talk to the crash reporting server */
00038   _http = new QHttp(this);
00039   connect(_http, SIGNAL(stateChanged(int)),
00040           this, SLOT(httpStateChanged(int)));
00041   connect(_http, SIGNAL(requestFinished(int, bool)),
00042           this, SLOT(httpRequestFinished(int, bool)));
00043   connect(_http, SIGNAL(dataSendProgress(int, int)),
00044           this, SIGNAL(uploadProgress(int, int)));
00045 
00046   /* Seed the lame PRNG we'll use to generate the multipart boundary marker */
00047   qsrand(QDateTime::currentDateTime().toTime_t());
00048 }
00049 
00050 void
00051 CrashReportUploader::uploadMinidump(const QUrl &serverUrl,
00052                                     const QString &minidumpId,
00053                                     const QByteArray &minidump,
00054                                     const QMap<QString,QString> &parameters)
00055 {
00056   QByteArray body;
00057 
00058   /* Set the destination host. If it looks like the destination URL uses SSL,
00059    * then we need to tell the QHttp object to use it as well. */
00060   if (! serverUrl.scheme().compare("https", Qt::CaseInsensitive)) {
00061     _http->setHost(serverUrl.host(), QHttp::ConnectionModeHttps,
00062                    serverUrl.port(443));
00063   } else {
00064     _http->setHost(serverUrl.host(), QHttp::ConnectionModeHttp,
00065                    serverUrl.port(80));
00066   }
00067 
00068   /* Set up the request header */
00069   QHttpRequestHeader header("POST", serverUrl.path());
00070   QString boundary = generateBoundaryMarker();
00071   header.setValue("Host", serverUrl.host());
00072   header.setContentType(QString("multipart/form-data; boundary=%1")
00073                                                     .arg(boundary));
00074 
00075   /* Add all the key=value parameters to the request body */
00076   foreach (QString key, parameters.keys()) {
00077     QString value = parameters.value(key);
00078     if (! value.isEmpty()) {
00079       body.append(QString("--%1\r\n").arg(boundary));
00080       body.append(QString("Content-Disposition: form-data; name=\"%1\"").arg(key));
00081       body.append("\r\n\r\n");
00082       body.append(value.toUtf8());
00083       body.append("\r\n");
00084     }
00085   }
00086 
00087   /* Append the minidump contents */
00088   body.append(QString("--%1\r\n").arg(boundary));
00089   body.append("Content-Disposition: form-data; ");
00090   body.append("name=\"upload_file_minidump\"; ");
00091   body.append(QString("filename=\"%1\"\r\n").arg(minidumpId));
00092   body.append("Content-Type: application/octet-stream\r\n\r\n");
00093   body.append(minidump);
00094   body.append(QString("\r\n--%1\r\n").arg(boundary));
00095 
00096   /* Initiate the request and return the request ID */
00097   _requestId = _http->request(header, body);
00098 }
00099 
00100 QString
00101 CrashReportUploader::generateBoundaryMarker() const
00102 {
00103   return QString("%1%2").arg((quint32)qrand(), 8, 16, QChar('0'))
00104                         .arg((quint32)qrand(), 8, 16, QChar('0'));  
00105 }
00106 
00107 void
00108 CrashReportUploader::cancel()
00109 {
00110   _http->abort();
00111 }
00112 
00113 void
00114 CrashReportUploader::httpStateChanged(int state)
00115 {
00116   switch (state) {
00117     case QHttp::Connecting:
00118       emit statusChanged(tr("Connecting..."));
00119       break;
00120 
00121     case QHttp::Sending:
00122       emit statusChanged(tr("Sending crash report..."));
00123       break;
00124 
00125     case QHttp::Reading:
00126       emit statusChanged(tr("Receiving response..."));
00127       break;
00128 
00129     default:
00130       break;
00131   }
00132 }
00133 
00134 void
00135 CrashReportUploader::httpRequestFinished(int id, bool error)
00136 {
00137   if (id != _requestId)
00138     return;
00139 
00140   if (error) {
00141     QString errorString = _http->errorString();
00142     emit uploadFailed(errorString);
00143   } else {
00144     QHttpResponseHeader response = _http->lastResponse();
00145     if (response.statusCode() == 200)
00146       emit uploadFinished();
00147     else
00148       emit uploadFailed(response.reasonPhrase());
00149   }
00150   _http->close();
00151 }
00152