Skip to content

Commit

Permalink
multipart reply extracted from WCS provider QgsNetworkReplyParser
Browse files Browse the repository at this point in the history
  • Loading branch information
blazek committed Jan 4, 2013
1 parent f6c6324 commit a572f3c
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 84 deletions.
3 changes: 3 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -78,6 +78,7 @@ SET(QGIS_CORE_SRCS
qgsmessageoutput.cpp
qgsmimedatautils.cpp
qgsmessagelog.cpp
qgsnetworkreplyparser.cpp
qgscredentials.cpp
qgsoverlayobject.cpp
qgsowsconnection.cpp
Expand Down Expand Up @@ -286,6 +287,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsmaprenderer.h
qgsmessageoutput.h
qgsmessagelog.h
qgsnetworkreplyparser.h
qgscredentials.h
qgspluginlayer.h
qgsproject.h
Expand Down Expand Up @@ -375,6 +377,7 @@ SET(QGIS_CORE_HDRS
qgsmaptopixel.h
qgsmessageoutput.h
qgsmimedatautils.h
qgsnetworkreplyparser.h
qgscredentials.h
qgsoverlayobjectpositionmanager.h
qgsowsconnection.h
Expand Down
144 changes: 144 additions & 0 deletions src/core/qgsnetworkreplyparser.cpp
@@ -0,0 +1,144 @@
/***************************************************************************
qgsnetworkreplyparser.cpp - Multipart QNetworkReply parser
-------------------
begin : 4 January, 2013
copyright : (C) 2013 by Radim Blazek
email : radim dot blazek at gmail.com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgslogger.h"
#include "qgsnetworkreplyparser.h"

#include <QNetworkReply>
#include <QObject>
#include <QRegExp>
#include <QString>
#include <QStringList>

QgsNetworkReplyParser::QgsNetworkReplyParser( QNetworkReply *reply )
: mReply(reply)
, mValid(false)
{
if ( !mReply ) return;

// Content type examples:
// multipart/mixed; boundary=wcs
// multipart/mixed; boundary="wcs"\n
if ( !isMultipart( mReply ) )
{
// reply is not multipart, copy body and headers
QMap<QByteArray,QByteArray> headers;
foreach ( QByteArray h, mReply->rawHeaderList() )
{
headers.insert( h, mReply->rawHeader( h ) );
}
mHeaders.append( headers );
mBodies.append( mReply->readAll() );
}
else // multipart
{
QString contentType = mReply->header( QNetworkRequest::ContentTypeHeader ).toString();
QgsDebugMsg( "contentType: " + contentType );

QRegExp re( ".*boundary=\"?([^\"]+)\"?\\s?", Qt::CaseInsensitive );

if ( !re.indexIn( contentType ) == 0 )
{
mError = tr( "Cannot find boundary in multipart content type" );
return;
}

QString boundary = re.cap( 1 );
QgsDebugMsg( "boundary = " + boundary );
boundary = "--" + boundary;

// Lines should be terminated by CRLF ("\r\n") but any new line combination may appear
QByteArray data = mReply->readAll();
int from, to;
from = data.indexOf( boundary.toAscii(), 0 ) + boundary.length() + 1 ;
//QVector<QByteArray> partHeaders;
//QVector<QByteArray> partBodies;
while ( true )
{
to = data.indexOf( boundary.toAscii(), from );
if ( to < 0 )
{
// It may happent that bondary is missing at the end (GeoServer)
// in that case, take everything to th end
if ( data.size() - from > 1 )
{
to = data.size(); // out of range OK
}
else
{
break;
}
}
QgsDebugMsg( QString( "part %1 - %2" ).arg( from ).arg( to ) );
QByteArray part = data.mid( from, to - from );
// Remove possible new line from beginning
while ( part.size() > 0 && ( part.at( 0 ) == '\r' || part.at( 0 ) == '\n' ) )
{
part.remove( 0, 1 );
}
// Split header and data (find empty new line)
// New lines should be CRLF, but we support also CRLFCRLF, LFLF to find empty line
int pos = 0; // body start
while ( pos < part.size() - 1 )
{
if ( part.at( pos ) == '\n' && ( part.at( pos + 1 ) == '\n' || part.at( pos + 1 ) == '\r' ) )
{
if ( part.at( pos + 1 ) == '\r' ) pos++;
pos += 2;
break;
}
pos++;
}
// parse headers
QMap<QByteArray,QByteArray> headersMap;
QByteArray headers = part.left( pos );
QgsDebugMsg( "headers:\n" + headers );

QStringList headerRows = QString( headers ).split( QRegExp( "[\n\r]+" ) );
foreach ( QString row, headerRows )
{
QgsDebugMsg( "row = " + row );
QStringList kv = row.split( ": " );
headersMap.insert( kv.value( 0 ).toAscii(), kv.value( 1 ).toAscii() );
}
mHeaders.append( headersMap );

mBodies.append( part.mid( pos ) );

from = to + boundary.length() + 1;
}
}
mValid = true;
}

bool QgsNetworkReplyParser::isMultipart( QNetworkReply *reply )
{
if ( !reply ) return false;

QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
QgsDebugMsg( "contentType: " + contentType );

// Multipart content type examples:
// multipart/mixed; boundary=wcs
// multipart/mixed; boundary="wcs"\n
if ( contentType.startsWith( "multipart/", Qt::CaseInsensitive ) )
{
return true;
}
return false;
}
81 changes: 81 additions & 0 deletions src/core/qgsnetworkreplyparser.h
@@ -0,0 +1,81 @@
/***************************************************************************
qgsnetworkreplyparser.h - Multipart QNetworkReply parser
-------------------
begin : 4 January, 2013
copyright : (C) 2013 by Radim Blazek
email : radim dot blazek at gmail.com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSNETWORKREPLYPARSER_H
#define QGSNETWORKREPLYPARSER_H

#include <QNetworkReply>

/**
\brief Multipart QNetworkReply parser.
It seams that Qt does not have currently support for multipart reply
and it is not even possible to create QNetworkReply from raw data
so we need a class for multipart QNetworkReply parsing.
*/

class CORE_EXPORT QgsNetworkReplyParser : public QObject
{
Q_OBJECT

public:
/** Constructor
* @param reply */
QgsNetworkReplyParser( QNetworkReply *reply );

/** Indicates if successfully parsed
* @return true if successfully parsed */
bool isValid() const { return mValid; }

/** Get number of parts
* @return number of parts */
int parts() const { return mHeaders.size(); }

/** Get part header
* @param part part index
* @return raw header */
QByteArray rawHeader ( int part, const QByteArray & headerName ) const { return mHeaders.value(part).value(headerName); }

/** Get part part body
* @param part part index
* @return part body */
QByteArray body ( int part ) const { return mBodies.value(part); }

/** Parsing error */
QString error() const { return mError; }

/** Test if reply is multipart.
* @return true if reply is multipart */
static bool isMultipart ( QNetworkReply *reply );

private:
QNetworkReply *mReply;

bool mValid;

QString mError;

/* List of header maps */
QList< QMap<QByteArray,QByteArray> > mHeaders;

/* List of part bodies */
QList<QByteArray> mBodies;
};

#endif

0 comments on commit a572f3c

Please sign in to comment.