QGIS API Documentation  master-59fd5e0
src/core/qgsnetworkreplyparser.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002       qgsnetworkreplyparser.cpp - Multipart QNetworkReply parser
00003                              -------------------
00004     begin                : 4 January, 2013
00005     copyright            : (C) 2013 by Radim Blazek
00006     email                : radim dot blazek at gmail.com
00007 
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 
00019 #include "qgslogger.h"
00020 #include "qgsnetworkreplyparser.h"
00021 
00022 #include <QNetworkReply>
00023 #include <QObject>
00024 #include <QRegExp>
00025 #include <QString>
00026 #include <QStringList>
00027 
00028 QgsNetworkReplyParser::QgsNetworkReplyParser( QNetworkReply *reply )
00029     : mReply( reply )
00030     , mValid( false )
00031 {
00032   QgsDebugMsg( "Entered." );
00033   if ( !mReply ) return;
00034 
00035   // Content type examples:
00036   //   multipart/mixed; boundary=wcs
00037   //   multipart/mixed; boundary="wcs"\n
00038   if ( !isMultipart( mReply ) )
00039   {
00040     // reply is not multipart, copy body and headers
00041     QMap<QByteArray, QByteArray> headers;
00042     foreach ( QByteArray h, mReply->rawHeaderList() )
00043     {
00044       headers.insert( h, mReply->rawHeader( h ) );
00045     }
00046     mHeaders.append( headers );
00047     mBodies.append( mReply->readAll() );
00048   }
00049   else // multipart
00050   {
00051     QString contentType = mReply->header( QNetworkRequest::ContentTypeHeader ).toString();
00052     QgsDebugMsg( "contentType: " + contentType );
00053 
00054     QRegExp re( ".*boundary=\"?([^\"]+)\"?\\s?", Qt::CaseInsensitive );
00055 
00056     if ( !re.indexIn( contentType ) == 0 )
00057     {
00058       mError = tr( "Cannot find boundary in multipart content type" );
00059       return;
00060     }
00061 
00062     QString boundary = re.cap( 1 );
00063     QgsDebugMsg( QString( "boundary = %1 size = %2" ).arg( boundary ).arg( boundary.size() ) );
00064     boundary = "--" + boundary;
00065 
00066     // Lines should be terminated by CRLF ("\r\n") but any new line combination may appear
00067     QByteArray data = mReply->readAll();
00068     int from, to;
00069     from = data.indexOf( boundary.toAscii(), 0 ) + boundary.length() + 1 ;
00070     //QVector<QByteArray> partHeaders;
00071     //QVector<QByteArray> partBodies;
00072     while ( true )
00073     {
00074       // 'to' is not really 'to', but index of the next byte after the end of part
00075       to = data.indexOf( boundary.toAscii(), from );
00076       if ( to < 0 )
00077       {
00078         QgsDebugMsg( QString( "No more boundaries, rest size = %1" ).arg( data.size() - from - 1 ) );
00079         // It may be end, last boundary is followed by '--'
00080         if ( data.size() - from - 1 == 2 && QString( data.mid( from, 2 ) ) == "--" ) // end
00081         {
00082           break;
00083         }
00084 
00085         // It may happen that boundary is missing at the end (GeoServer)
00086         // in that case, take everything to the end
00087         if ( data.size() - from > 1 )
00088         {
00089           to = data.size(); // out of range OK
00090         }
00091         else
00092         {
00093           break;
00094         }
00095       }
00096       QgsDebugMsg( QString( "part %1 - %2" ).arg( from ).arg( to ) );
00097       QByteArray part = data.mid( from, to - from );
00098       // Remove possible new line from beginning
00099       while ( part.size() > 0 && ( part.at( 0 ) == '\r' || part.at( 0 ) == '\n' ) )
00100       {
00101         part.remove( 0, 1 );
00102       }
00103       // Split header and data (find empty new line)
00104       // New lines should be CRLF, but we support also CRLFCRLF, LFLF to find empty line
00105       int pos = 0; // body start
00106       while ( pos < part.size() - 1 )
00107       {
00108         if ( part.at( pos ) == '\n' && ( part.at( pos + 1 ) == '\n' || part.at( pos + 1 ) == '\r' ) )
00109         {
00110           if ( part.at( pos + 1 ) == '\r' ) pos++;
00111           pos += 2;
00112           break;
00113         }
00114         pos++;
00115       }
00116       // parse headers
00117       RawHeaderMap headersMap;
00118       QByteArray headers = part.left( pos );
00119       QgsDebugMsg( "headers:\n" + headers );
00120 
00121       QStringList headerRows = QString( headers ).split( QRegExp( "[\n\r]+" ) );
00122       foreach ( QString row, headerRows )
00123       {
00124         QgsDebugMsg( "row = " + row );
00125         QStringList kv = row.split( ": " );
00126         headersMap.insert( kv.value( 0 ).toAscii(), kv.value( 1 ).toAscii() );
00127       }
00128       mHeaders.append( headersMap );
00129 
00130       mBodies.append( part.mid( pos ) );
00131 
00132       from = to + boundary.length();
00133     }
00134   }
00135   mValid = true;
00136 }
00137 
00138 bool QgsNetworkReplyParser::isMultipart( QNetworkReply *reply )
00139 {
00140   if ( !reply ) return false;
00141 
00142   QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
00143   QgsDebugMsg( "contentType: " + contentType );
00144 
00145   // Multipart content type examples:
00146   //   multipart/mixed; boundary=wcs
00147   //   multipart/mixed; boundary="wcs"\n
00148   return contentType.startsWith( "multipart/", Qt::CaseInsensitive );
00149 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines