Branch data Line data Source code
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 : 0 : QgsNetworkReplyParser::QgsNetworkReplyParser( QNetworkReply *reply ) 29 : 0 : : mReply( reply ) 30 : 0 : , mValid( false ) 31 : 0 : { 32 : 0 : if ( !mReply ) return; 33 : : 34 : : // Content type examples: 35 : : // multipart/mixed; boundary=wcs 36 : : // multipart/mixed; boundary="wcs"\n 37 : 0 : if ( !isMultipart( mReply ) ) 38 : : { 39 : : // reply is not multipart, copy body and headers 40 : 0 : QMap<QByteArray, QByteArray> headers; 41 : 0 : const auto constRawHeaderList = mReply->rawHeaderList(); 42 : 0 : for ( QByteArray h : constRawHeaderList ) 43 : : { 44 : 0 : headers.insert( h, mReply->rawHeader( h ) ); 45 : 0 : } 46 : 0 : mHeaders.append( headers ); 47 : 0 : mBodies.append( mReply->readAll() ); 48 : 0 : } 49 : : else // multipart 50 : : { 51 : 0 : QString contentType = mReply->header( QNetworkRequest::ContentTypeHeader ).toString(); 52 : 0 : QgsDebugMsg( "contentType: " + contentType ); 53 : : 54 : 0 : QRegExp re( ".*boundary=\"?([^\"]+)\"?\\s?", Qt::CaseInsensitive ); 55 : : 56 : 0 : if ( !( re.indexIn( contentType ) == 0 ) ) 57 : : { 58 : 0 : mError = tr( "Cannot find boundary in multipart content type" ); 59 : 0 : return; 60 : : } 61 : : 62 : 0 : QString boundary = re.cap( 1 ); 63 : 0 : QgsDebugMsg( QStringLiteral( "boundary = %1 size = %2" ).arg( boundary ).arg( boundary.size() ) ); 64 : 0 : boundary = "--" + boundary; 65 : : 66 : : // Lines should be terminated by CRLF ("\r\n") but any new line combination may appear 67 : 0 : QByteArray data = mReply->readAll(); 68 : : int from, to; 69 : 0 : from = data.indexOf( boundary.toLatin1(), 0 ) + boundary.length() + 1; 70 : : //QVector<QByteArray> partHeaders; 71 : : //QVector<QByteArray> partBodies; 72 : 0 : while ( true ) 73 : : { 74 : : // 'to' is not really 'to', but index of the next byte after the end of part 75 : 0 : to = data.indexOf( boundary.toLatin1(), from ); 76 : 0 : if ( to < 0 ) 77 : : { 78 : 0 : QgsDebugMsg( QStringLiteral( "No more boundaries, rest size = %1" ).arg( data.size() - from - 1 ) ); 79 : : // It may be end, last boundary is followed by '--' 80 : 0 : if ( data.size() - from - 1 == 2 && QString( data.mid( from, 2 ) ) == QLatin1String( "--" ) ) // end 81 : : { 82 : 0 : break; 83 : : } 84 : : 85 : : // It may happen that boundary is missing at the end (GeoServer) 86 : : // in that case, take everything to the end 87 : 0 : if ( data.size() - from > 1 ) 88 : : { 89 : 0 : to = data.size(); // out of range OK 90 : 0 : } 91 : : else 92 : : { 93 : 0 : break; 94 : : } 95 : 0 : } 96 : 0 : QgsDebugMsg( QStringLiteral( "part %1 - %2" ).arg( from ).arg( to ) ); 97 : 0 : QByteArray part = data.mid( from, to - from ); 98 : : // Remove possible new line from beginning 99 : 0 : while ( !part.isEmpty() && ( part.at( 0 ) == '\r' || part.at( 0 ) == '\n' ) ) 100 : : { 101 : 0 : part.remove( 0, 1 ); 102 : : } 103 : : // Split header and data (find empty new line) 104 : : // New lines should be CRLF, but we support also CRLFCRLF, LFLF to find empty line 105 : 0 : int pos = 0; // body start 106 : 0 : while ( pos < part.size() - 1 ) 107 : : { 108 : 0 : if ( part.at( pos ) == '\n' && ( part.at( pos + 1 ) == '\n' || part.at( pos + 1 ) == '\r' ) ) 109 : : { 110 : 0 : if ( part.at( pos + 1 ) == '\r' ) pos++; 111 : 0 : pos += 2; 112 : 0 : break; 113 : : } 114 : 0 : pos++; 115 : : } 116 : : // parse headers 117 : 0 : RawHeaderMap headersMap; 118 : 0 : QByteArray headers = part.left( pos ); 119 : 0 : QgsDebugMsg( "headers:\n" + headers ); 120 : : 121 : 0 : QStringList headerRows = QString( headers ).split( QRegExp( "[\n\r]+" ) ); 122 : 0 : const auto constHeaderRows = headerRows; 123 : 0 : for ( const QString &row : constHeaderRows ) 124 : : { 125 : 0 : QgsDebugMsg( "row = " + row ); 126 : 0 : QStringList kv = row.split( QStringLiteral( ": " ) ); 127 : 0 : headersMap.insert( kv.value( 0 ).toLatin1(), kv.value( 1 ).toLatin1() ); 128 : 0 : } 129 : 0 : mHeaders.append( headersMap ); 130 : : 131 : 0 : mBodies.append( part.mid( pos ) ); 132 : : 133 : 0 : from = to + boundary.length(); 134 : 0 : } 135 : 0 : } 136 : 0 : mValid = true; 137 : 0 : } 138 : : 139 : 0 : bool QgsNetworkReplyParser::isMultipart( QNetworkReply *reply ) 140 : : { 141 : 0 : if ( !reply ) return false; 142 : : 143 : 0 : QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString(); 144 : 0 : QgsDebugMsg( "contentType: " + contentType ); 145 : : 146 : : // Multipart content type examples: 147 : : // multipart/mixed; boundary=wcs 148 : : // multipart/mixed; boundary="wcs"\n 149 : 0 : return contentType.startsWith( QLatin1String( "multipart/" ), Qt::CaseInsensitive ); 150 : 0 : }