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

Generated on Mon Aug 30 19:09:59 2010 for Vidalia by  doxygen 1.5.9