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