|
| 1 | +/*************************************************************************** |
| 2 | + qgsnetworkreplyparser.cpp - Multipart QNetworkReply parser |
| 3 | + ------------------- |
| 4 | + begin : 4 January, 2013 |
| 5 | + copyright : (C) 2013 by Radim Blazek |
| 6 | + email : radim dot blazek at gmail.com |
| 7 | +
|
| 8 | + ***************************************************************************/ |
| 9 | + |
| 10 | +/*************************************************************************** |
| 11 | + * * |
| 12 | + * This program is free software; you can redistribute it and/or modify * |
| 13 | + * it under the terms of the GNU General Public License as published by * |
| 14 | + * the Free Software Foundation; either version 2 of the License, or * |
| 15 | + * (at your option) any later version. * |
| 16 | + * * |
| 17 | + ***************************************************************************/ |
| 18 | + |
| 19 | +#include "qgslogger.h" |
| 20 | +#include "qgsnetworkreplyparser.h" |
| 21 | + |
| 22 | +#include <QNetworkReply> |
| 23 | +#include <QObject> |
| 24 | +#include <QRegExp> |
| 25 | +#include <QString> |
| 26 | +#include <QStringList> |
| 27 | + |
| 28 | +QgsNetworkReplyParser::QgsNetworkReplyParser( QNetworkReply *reply ) |
| 29 | + : mReply(reply) |
| 30 | + , mValid(false) |
| 31 | +{ |
| 32 | + if ( !mReply ) return; |
| 33 | + |
| 34 | + // Content type examples: |
| 35 | + // multipart/mixed; boundary=wcs |
| 36 | + // multipart/mixed; boundary="wcs"\n |
| 37 | + if ( !isMultipart( mReply ) ) |
| 38 | + { |
| 39 | + // reply is not multipart, copy body and headers |
| 40 | + QMap<QByteArray,QByteArray> headers; |
| 41 | + foreach ( QByteArray h, mReply->rawHeaderList() ) |
| 42 | + { |
| 43 | + headers.insert( h, mReply->rawHeader( h ) ); |
| 44 | + } |
| 45 | + mHeaders.append( headers ); |
| 46 | + mBodies.append( mReply->readAll() ); |
| 47 | + } |
| 48 | + else // multipart |
| 49 | + { |
| 50 | + QString contentType = mReply->header( QNetworkRequest::ContentTypeHeader ).toString(); |
| 51 | + QgsDebugMsg( "contentType: " + contentType ); |
| 52 | + |
| 53 | + QRegExp re( ".*boundary=\"?([^\"]+)\"?\\s?", Qt::CaseInsensitive ); |
| 54 | + |
| 55 | + if ( !re.indexIn( contentType ) == 0 ) |
| 56 | + { |
| 57 | + mError = tr( "Cannot find boundary in multipart content type" ); |
| 58 | + return; |
| 59 | + } |
| 60 | + |
| 61 | + QString boundary = re.cap( 1 ); |
| 62 | + QgsDebugMsg( "boundary = " + boundary ); |
| 63 | + boundary = "--" + boundary; |
| 64 | + |
| 65 | + // Lines should be terminated by CRLF ("\r\n") but any new line combination may appear |
| 66 | + QByteArray data = mReply->readAll(); |
| 67 | + int from, to; |
| 68 | + from = data.indexOf( boundary.toAscii(), 0 ) + boundary.length() + 1 ; |
| 69 | + //QVector<QByteArray> partHeaders; |
| 70 | + //QVector<QByteArray> partBodies; |
| 71 | + while ( true ) |
| 72 | + { |
| 73 | + to = data.indexOf( boundary.toAscii(), from ); |
| 74 | + if ( to < 0 ) |
| 75 | + { |
| 76 | + // It may happent that bondary is missing at the end (GeoServer) |
| 77 | + // in that case, take everything to th end |
| 78 | + if ( data.size() - from > 1 ) |
| 79 | + { |
| 80 | + to = data.size(); // out of range OK |
| 81 | + } |
| 82 | + else |
| 83 | + { |
| 84 | + break; |
| 85 | + } |
| 86 | + } |
| 87 | + QgsDebugMsg( QString( "part %1 - %2" ).arg( from ).arg( to ) ); |
| 88 | + QByteArray part = data.mid( from, to - from ); |
| 89 | + // Remove possible new line from beginning |
| 90 | + while ( part.size() > 0 && ( part.at( 0 ) == '\r' || part.at( 0 ) == '\n' ) ) |
| 91 | + { |
| 92 | + part.remove( 0, 1 ); |
| 93 | + } |
| 94 | + // Split header and data (find empty new line) |
| 95 | + // New lines should be CRLF, but we support also CRLFCRLF, LFLF to find empty line |
| 96 | + int pos = 0; // body start |
| 97 | + while ( pos < part.size() - 1 ) |
| 98 | + { |
| 99 | + if ( part.at( pos ) == '\n' && ( part.at( pos + 1 ) == '\n' || part.at( pos + 1 ) == '\r' ) ) |
| 100 | + { |
| 101 | + if ( part.at( pos + 1 ) == '\r' ) pos++; |
| 102 | + pos += 2; |
| 103 | + break; |
| 104 | + } |
| 105 | + pos++; |
| 106 | + } |
| 107 | + // parse headers |
| 108 | + QMap<QByteArray,QByteArray> headersMap; |
| 109 | + QByteArray headers = part.left( pos ); |
| 110 | + QgsDebugMsg( "headers:\n" + headers ); |
| 111 | + |
| 112 | + QStringList headerRows = QString( headers ).split( QRegExp( "[\n\r]+" ) ); |
| 113 | + foreach ( QString row, headerRows ) |
| 114 | + { |
| 115 | + QgsDebugMsg( "row = " + row ); |
| 116 | + QStringList kv = row.split( ": " ); |
| 117 | + headersMap.insert( kv.value( 0 ).toAscii(), kv.value( 1 ).toAscii() ); |
| 118 | + } |
| 119 | + mHeaders.append( headersMap ); |
| 120 | + |
| 121 | + mBodies.append( part.mid( pos ) ); |
| 122 | + |
| 123 | + from = to + boundary.length() + 1; |
| 124 | + } |
| 125 | + } |
| 126 | + mValid = true; |
| 127 | +} |
| 128 | + |
| 129 | +bool QgsNetworkReplyParser::isMultipart( QNetworkReply *reply ) |
| 130 | +{ |
| 131 | + if ( !reply ) return false; |
| 132 | + |
| 133 | + QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString(); |
| 134 | + QgsDebugMsg( "contentType: " + contentType ); |
| 135 | + |
| 136 | + // Multipart content type examples: |
| 137 | + // multipart/mixed; boundary=wcs |
| 138 | + // multipart/mixed; boundary="wcs"\n |
| 139 | + if ( contentType.startsWith( "multipart/", Qt::CaseInsensitive ) ) |
| 140 | + { |
| 141 | + return true; |
| 142 | + } |
| 143 | + return false; |
| 144 | +} |
0 commit comments