Vidalia 0.2.12
|
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> ¶meters) 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