00001
00006 #include "httprequest.h"
00007 #include <QList>
00008 #include <QDir>
00009 #include "httpcookie.h"
00010
00011 HttpRequest::HttpRequest(QSettings* settings) {
00012 status=waitForRequest;
00013 currentSize=0;
00014 expectedBodySize=0;
00015 maxSize=settings->value("maxRequestSize","16000").toInt();
00016 maxMultiPartSize=settings->value("maxMultiPartSize","1000000").toInt();
00017
00018
00019 #ifdef Q_OS_WIN32
00020 if (QDir::isRelativePath(tempDir) && settings->format()!=QSettings::NativeFormat)
00021 #else
00022 if (QDir::isRelativePath(tempDir))
00023 #endif
00024 {
00025 QFileInfo configFile(settings->fileName());
00026 tempDir=QFileInfo(configFile.absolutePath(),tempDir).absoluteFilePath();
00027 }
00028 }
00029
00030 void HttpRequest::readRequest(QTcpSocket& socket) {
00031 #ifdef SUPERVERBOSE
00032 qDebug("HttpRequest: read request");
00033 #endif
00034 int toRead=maxSize-currentSize+1;
00035 QByteArray newData=socket.readLine(toRead).trimmed();
00036 currentSize+=newData.size();
00037 if (!newData.isEmpty()) {
00038 QList<QByteArray> list=newData.split(' ');
00039 if (list.count()!=3 || !list.at(2).contains("HTTP")) {
00040 qWarning("HttpRequest: received broken HTTP request, invalid first line");
00041 status=abort;
00042 }
00043 else {
00044 method=list.at(0);
00045 path=list.at(1);
00046 version=list.at(2);
00047 status=waitForHeader;
00048 }
00049 }
00050 }
00051
00052 void HttpRequest::readHeader(QTcpSocket& socket) {
00053 #ifdef SUPERVERBOSE
00054 qDebug("HttpRequest: read header");
00055 #endif
00056 int toRead=maxSize-currentSize+1;
00057 QByteArray newData=socket.readLine(toRead).trimmed();
00058 currentSize+=newData.size();
00059 int colon=newData.indexOf(':');
00060 if (colon>0) {
00061
00062 currentHeader=newData.left(colon);
00063 QByteArray value=newData.mid(colon+1).trimmed();
00064 headers.insert(currentHeader,value);
00065 #ifdef SUPERVERBOSE
00066 qDebug("HttpRequest: received header %s: %s",currentHeader.data(),value.data());
00067 #endif
00068 }
00069 else if (!newData.isEmpty()) {
00070
00071 #ifdef SUPERVERBOSE
00072 qDebug("HttpRequest: read additional line of header");
00073 #endif
00074
00075 if (headers.contains(currentHeader)) {
00076 headers.insert(currentHeader,headers.value(currentHeader)+" "+newData);
00077 }
00078 }
00079 else {
00080
00081 #ifdef SUPERVERBOSE
00082 qDebug("HttpRequest: headers completed");
00083 #endif
00084
00085
00086 QByteArray contentType=headers.value("Content-Type");
00087 if (contentType.startsWith("multipart/form-data")) {
00088 int posi=contentType.indexOf("boundary=");
00089 if (posi>=0) {
00090 boundary=contentType.mid(posi+9);
00091 }
00092 }
00093 QByteArray contentLength=getHeader("Content-Length");
00094 if (!contentLength.isEmpty()) {
00095 expectedBodySize=contentLength.toInt();
00096 }
00097 if (expectedBodySize==0) {
00098 #ifdef SUPERVERBOSE
00099 qDebug("HttpRequest: expect no body");
00100 #endif
00101 status=complete;
00102 }
00103 else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize) {
00104 qWarning("HttpRequest: expected body is too large");
00105 status=abort;
00106 }
00107 else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize) {
00108 qWarning("HttpRequest: expected multipart body is too large");
00109 status=abort;
00110 }
00111 else {
00112 #ifdef SUPERVERBOSE
00113 qDebug("HttpRequest: expect %i bytes body",expectedBodySize);
00114 #endif
00115 status=waitForBody;
00116 }
00117 }
00118 }
00119
00120 void HttpRequest::readBody(QTcpSocket& socket) {
00121 Q_ASSERT(expectedBodySize!=0);
00122 if (boundary.isEmpty()) {
00123
00124 #ifdef SUPERVERBOSE
00125 qDebug("HttpRequest: receive body");
00126 #endif
00127 int toRead=expectedBodySize-bodyData.size();
00128 QByteArray newData=socket.read(toRead);
00129 currentSize+=newData.size();
00130 bodyData.append(newData);
00131 if (bodyData.size()>=expectedBodySize) {
00132 status=complete;
00133 }
00134 }
00135 else {
00136
00137 #ifdef SUPERVERBOSE
00138 qDebug("HttpRequest: receiving multipart body");
00139 #endif
00140 if (!tempFile.isOpen()) {
00141 tempFile.open();
00142 }
00143
00144 int fileSize=tempFile.size();
00145 int toRead=expectedBodySize-fileSize;
00146 if (toRead>65536) {
00147 toRead=65536;
00148 }
00149 fileSize+=tempFile.write(socket.read(toRead));
00150 if (fileSize>=maxMultiPartSize) {
00151 qWarning("HttpRequest: received too many multipart bytes");
00152 status=abort;
00153 }
00154 else if (fileSize>=expectedBodySize) {
00155 #ifdef SUPERVERBOSE
00156 qDebug("HttpRequest: received whole multipart body");
00157 #endif
00158 tempFile.flush();
00159 if (tempFile.error()) {
00160 qCritical("HttpRequest: Error writing temp file for multipart body");
00161 }
00162 parseMultiPartFile();
00163 tempFile.close();
00164 status=complete;
00165 }
00166 }
00167 }
00168
00169 void HttpRequest::decodeRequestParams() {
00170 #ifdef SUPERVERBOSE
00171 qDebug("HttpRequest: extract and decode request parameters");
00172 #endif
00173 QByteArray rawParameters;
00174 if (headers.value("Content-Type")=="application/x-www-form-urlencoded") {
00175 rawParameters=bodyData;
00176 }
00177 else {
00178 int questionMark=path.indexOf('?');
00179 if (questionMark>=0) {
00180 rawParameters=path.mid(questionMark+1);
00181 path=path.left(questionMark);
00182 }
00183 }
00184
00185 QList<QByteArray> list=rawParameters.split('&');
00186 foreach (QByteArray part, list) {
00187 int equalsChar=part.indexOf('=');
00188 if (equalsChar>=0) {
00189 QByteArray name=part.left(equalsChar).trimmed();
00190 QByteArray value=part.mid(equalsChar+1).trimmed();
00191 parameters.insert(urlDecode(name),urlDecode(value));
00192 }
00193 else if (!part.isEmpty()){
00194
00195 parameters.insert(urlDecode(part),"");
00196 }
00197 }
00198 }
00199
00200 void HttpRequest::extractCookies() {
00201 #ifdef SUPERVERBOSE
00202 qDebug("HttpRequest: extract cookies");
00203 #endif
00204 foreach(QByteArray cookieStr, headers.values("Cookie")) {
00205 QList<QByteArray> list=HttpCookie::splitCSV(cookieStr);
00206 foreach(QByteArray part, list) {
00207 #ifdef SUPERVERBOSE
00208 qDebug("HttpRequest: found cookie %s",part.data());
00209 #endif // Split the part into name and value
00210 QByteArray name;
00211 QByteArray value;
00212 int posi=part.indexOf('=');
00213 if (posi) {
00214 name=part.left(posi).trimmed();
00215 value=part.mid(posi+1).trimmed();
00216 }
00217 else {
00218 name=part.trimmed();
00219 value="";
00220 }
00221 cookies.insert(name,value);
00222 }
00223 }
00224 headers.remove("Cookie");
00225 }
00226
00227 void HttpRequest::readFromSocket(QTcpSocket& socket) {
00228 Q_ASSERT(status!=complete);
00229 if (status==waitForRequest) {
00230 readRequest(socket);
00231 }
00232 else if (status==waitForHeader) {
00233 readHeader(socket);
00234 }
00235 else if (status==waitForBody) {
00236 readBody(socket);
00237 }
00238 if (currentSize>maxSize) {
00239 qWarning("HttpRequest: received too many bytes");
00240 status=abort;
00241 }
00242 if (status==complete) {
00243
00244 decodeRequestParams();
00245
00246 extractCookies();
00247 }
00248 }
00249
00250
00251 HttpRequest::RequestStatus HttpRequest::getStatus() const {
00252 return status;
00253 }
00254
00255
00256 QByteArray HttpRequest::getMethod() const {
00257 return method;
00258 }
00259
00260
00261 QByteArray HttpRequest::getPath() const {
00262 return urlDecode(path);
00263 }
00264
00265
00266 QByteArray HttpRequest::getVersion() const {
00267 return version;
00268 }
00269
00270
00271 QByteArray HttpRequest::getHeader(const QByteArray& name) const {
00272 return headers.value(name);
00273 }
00274
00275 QList<QByteArray> HttpRequest::getHeaders(const QByteArray& name) const {
00276 return headers.values(name);
00277 }
00278
00279 QMultiMap<QByteArray,QByteArray> HttpRequest::getHeaderMap() const {
00280 return headers;
00281 }
00282
00283 QByteArray HttpRequest::getParameter(const QByteArray& name) const {
00284 return parameters.value(name);
00285 }
00286
00287 QList<QByteArray> HttpRequest::getParameters(const QByteArray& name) const {
00288 return parameters.values(name);
00289 }
00290
00291 QMultiMap<QByteArray,QByteArray> HttpRequest::getParameterMap() const {
00292 return parameters;
00293 }
00294
00295 QByteArray HttpRequest::getBody() const {
00296 return bodyData;
00297 }
00298
00299 QByteArray HttpRequest::urlDecode(const QByteArray source) {
00300 QByteArray buffer(source);
00301 buffer.replace('+',' ');
00302 int percentChar=buffer.indexOf('%');
00303 while (percentChar>=0) {
00304 bool ok;
00305 char byte=buffer.mid(percentChar+1,2).toInt(&ok,16);
00306 if (ok) {
00307 buffer.replace(percentChar,3,(char*)&byte,1);
00308 }
00309 percentChar=buffer.indexOf('%',percentChar+1);
00310 }
00311 return buffer;
00312 }
00313
00314
00315 void HttpRequest::parseMultiPartFile() {
00316 qDebug("HttpRequest: parsing multipart temp file");
00317 tempFile.seek(0);
00318 bool finished=false;
00319 while (!tempFile.atEnd() && !finished && !tempFile.error()) {
00320
00321 #ifdef SUPERVERBOSE
00322 qDebug("HttpRequest: reading multpart headers");
00323 #endif
00324 QByteArray fieldName;
00325 QByteArray fileName;
00326 while (!tempFile.atEnd() && !finished && !tempFile.error()) {
00327 QByteArray line=tempFile.readLine(65536).trimmed();
00328 if (line.startsWith("Content-Disposition:")) {
00329 if (line.contains("form-data")) {
00330 int start=line.indexOf(" name=\"");
00331 int end=line.indexOf("\"",start+7);
00332 if (start>=0 && end>=start) {
00333 fieldName=line.mid(start+7,end-start-7);
00334 }
00335 start=line.indexOf(" filename=\"");
00336 end=line.indexOf("\"",start+11);
00337 if (start>=0 && end>=start) {
00338 fileName=line.mid(start+11,end-start-11);
00339 }
00340 #ifdef SUPERVERBOSE
00341 qDebug("HttpRequest: multipart field=%s, filename=%s",fieldName.data(),fileName.data());
00342 #endif
00343 }
00344 else {
00345 qDebug("HttpRequest: ignoring unsupported content part %s",line.data());
00346 }
00347 }
00348 else if (line.isEmpty()) {
00349 break;
00350 }
00351 }
00352
00353 #ifdef SUPERVERBOSE
00354 qDebug("HttpRequest: reading multpart data");
00355 #endif
00356 QTemporaryFile* uploadedFile=0;
00357 QByteArray fieldValue;
00358 while (!tempFile.atEnd() && !finished && !tempFile.error()) {
00359 QByteArray line=tempFile.readLine(65536);
00360 if (line.startsWith("--"+boundary)) {
00361
00362
00363 if (fileName.isEmpty() && !fieldName.isEmpty()) {
00364
00365 fieldValue.remove(fieldValue.size()-2,2);
00366 parameters.insert(fieldName,fieldValue);
00367 qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fieldValue.data());
00368 }
00369 else if (!fileName.isEmpty() && !fieldName.isEmpty()) {
00370
00371 #ifdef SUPERVERBOSE
00372 qDebug("HttpRequest: finishing writing to uploaded file");
00373 #endif
00374 uploadedFile->resize(uploadedFile->size()-2);
00375 uploadedFile->flush();
00376 uploadedFile->seek(0);
00377 parameters.insert(fieldName,fileName);
00378 qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fileName.data());
00379 uploadedFiles.insert(fieldName,uploadedFile);
00380 qDebug("HttpRequest: uploaded file size is %i",(int) uploadedFile->size());
00381 }
00382 if (line.contains(boundary+"--")) {
00383 finished=true;
00384 }
00385 break;
00386 }
00387 else {
00388 if (fileName.isEmpty() && !fieldName.isEmpty()) {
00389
00390 currentSize+=line.size();
00391 fieldValue.append(line);
00392 }
00393 else if (!fileName.isEmpty() && !fieldName.isEmpty()) {
00394
00395 if (!uploadedFile) {
00396 uploadedFile=new QTemporaryFile();
00397 uploadedFile->open();
00398 }
00399 uploadedFile->write(line);
00400 if (uploadedFile->error()) {
00401 qCritical("HttpRequest: error writing temp file, %s",qPrintable(uploadedFile->errorString()));
00402 }
00403 }
00404 }
00405 }
00406 }
00407 if (tempFile.error()) {
00408 qCritical("HttpRequest: cannot read temp file, %s",qPrintable(tempFile.errorString()));
00409 }
00410 #ifdef SUPERVERBOSE
00411 qDebug("HttpRequest: finished parsing multipart temp file");
00412 #endif
00413 }
00414
00415 HttpRequest::~HttpRequest() {
00416 foreach(QByteArray key, uploadedFiles.keys()) {
00417 QTemporaryFile* file=uploadedFiles.value(key);
00418 file->close();
00419 delete file;
00420 }
00421 }
00422
00423 QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) {
00424 return uploadedFiles.value(fieldName);
00425 }
00426
00427 QByteArray HttpRequest::getCookie(const QByteArray& name) const {
00428 return cookies.value(name);
00429 }
00430
00432 QMap<QByteArray,QByteArray>& HttpRequest::getCookieMap() {
00433 return cookies;
00434 }
00435