103 changes: 19 additions & 84 deletions src/providers/wcs/qgswcsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "qgsrectangle.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsnetworkaccessmanager.h"
#include "qgsnetworkreplyparser.h"
#include "qgsmessageoutput.h"
#include "qgsmessagelog.h"

Expand Down Expand Up @@ -848,6 +849,7 @@ void QgsWcsProvider::cacheReplyFinished()
}

QVariant status = mCacheReply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
QgsDebugMsg( QString( "status = %1" ).arg( status.toInt() ) );
if ( !status.isNull() && status.toInt() >= 400 )
{
QVariant phrase = mCacheReply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
Expand Down Expand Up @@ -902,111 +904,44 @@ void QgsWcsProvider::cacheReplyFinished()
}

// WCS 1.1 gives response as multipart, e.g.
// multipart/mixed; boundary=wcs
// multipart/mixed; boundary="wcs"\n
if ( contentType.startsWith( "multipart/", Qt::CaseInsensitive ) )
if ( QgsNetworkReplyParser::isMultipart( mCacheReply ) )
{
// 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 have to parse response
QRegExp re( ".*boundary=\"?([^\"]+)\"?\\s?", Qt::CaseInsensitive );
QgsDebugMsg( "reply is multipart" );
QgsNetworkReplyParser parser( mCacheReply );

if ( !re.indexIn( contentType ) == 0 )
if ( !parser.isValid() )
{
QgsMessageLog::logMessage( tr( "Cannot find boundary in multipart content type" ), tr( "WCS" ) );
QgsMessageLog::logMessage( tr( "Cannot parse multipart response: %1" ).arg( parser.error() ), tr( "WCS" ) );
clearCache();
mCacheReply->deleteLater();
mCacheReply = 0;
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 = mCacheReply->readAll();
int from, to;
from = data.indexOf( boundary.toAscii(), 0 ) + boundary.length() + 1 ;
QVector<QByteArray> partHeaders;
QVector<QByteArray> partBodies;
while ( true )
if ( parser.parts() < 2 )
{
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++;
}
partHeaders.append( part.left( pos ) );
partBodies.append( part.mid( pos ) );
QgsDebugMsg( "Part header:\n" + partHeaders.last() );

from = to + boundary.length() + 1;
}
if ( partBodies.size() < 2 )
{
QgsMessageLog::logMessage( tr( "Expected 2 parts, %1 received" ).arg( partBodies.size() ), tr( "WCS" ) );
QgsMessageLog::logMessage( tr( "Expected 2 parts, %1 received" ).arg( parser.parts() ), tr( "WCS" ) );
clearCache();
mCacheReply->deleteLater();
mCacheReply = 0;
return;
}
else if ( partBodies.size() > 2 )
else if ( parser.parts() > 2 )
{
// We will try the second one
QgsMessageLog::logMessage( tr( "More than 2 parts (%1) received" ).arg( partBodies.size() ), tr( "WCS" ) );
QgsMessageLog::logMessage( tr( "More than 2 parts (%1) received" ).arg( parser.parts() ), tr( "WCS" ) );
}

QStringList headerRows = QString( partHeaders.value( 1 ) ).split( QRegExp( "[\n\r]+" ) );
QString transferEncoding;
foreach ( QString row, headerRows )
{
QgsDebugMsg( "row = " + row );
//Content-Transfer-Encoding: base64
QStringList kv = row.split( ": " );
if ( kv.value( 0 ) == "Content-Transfer-Encoding" )
{
transferEncoding = kv.value( 1 );
break;
}
}
QString transferEncoding = parser.rawHeader( 1, QString( "Content-Transfer-Encoding" ).toAscii() );
QgsDebugMsg( "transferEncoding = " + transferEncoding );

// It may happen (GeoServer) that in part header is for example
// Content-Type: image/tiff and Content-Transfer-Encoding: base64
// but content is xml ExceptionReport which is not in base64
if ( partBodies.value( 1 ).startsWith( "<?xml" ) )
QByteArray body = parser.body( 1 );
if ( body.startsWith( "<?xml" ) )
{
if ( parseServiceExceptionReportDom( partBodies.value( 1 ) ) )
if ( parseServiceExceptionReportDom( body ) )
{
QgsMessageLog::logMessage( tr( "Map request error (Title:%1; Error:%2; URL: %3)" )
.arg( mErrorCaption ).arg( mError )
Expand All @@ -1015,7 +950,7 @@ void QgsWcsProvider::cacheReplyFinished()
else
{
QgsMessageLog::logMessage( tr( "Map request error (Response: %1; URL:%2)" )
.arg( QString::fromUtf8( partBodies.value( 1 ) ) )
.arg( QString::fromUtf8( body ) )
.arg( mCacheReply->url().toString() ), tr( "WCS" ) );
}

Expand All @@ -1026,11 +961,11 @@ void QgsWcsProvider::cacheReplyFinished()

if ( transferEncoding == "binary" )
{
mCachedData = partBodies.value( 1 );
mCachedData = body;
}
else if ( transferEncoding == "base64" )
{
mCachedData = QByteArray::fromBase64( partBodies.value( 1 ) );
mCachedData = QByteArray::fromBase64( body );
}
else
{
Expand All @@ -1051,6 +986,7 @@ void QgsWcsProvider::cacheReplyFinished()
mCachedData = mCacheReply->readAll();
}

QgsDebugMsg( QString( "%1 bytes received" ).arg( mCachedData.size() ) );
if ( mCachedData.size() == 0 )
{
QgsMessageLog::logMessage( tr( "No data received" ), tr( "WCS" ) );
Expand All @@ -1059,7 +995,6 @@ void QgsWcsProvider::cacheReplyFinished()
mCacheReply = 0;
return;
}
QgsDebugMsg( QString( "%1 bytes received" ).arg( mCachedData.size() ) );

#if 0
QFile myFile( "/tmp/qgiswcscache.dat" );
Expand Down