QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsnetworkreplyparser.cpp
Go to the documentation of this file.
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 
29  : mReply( reply )
30  , mValid( false )
31 {
32  QgsDebugMsg( "Entered." );
33  if ( !mReply ) return;
34 
35  // Content type examples:
36  // multipart/mixed; boundary=wcs
37  // multipart/mixed; boundary="wcs"\n
38  if ( !isMultipart( mReply ) )
39  {
40  // reply is not multipart, copy body and headers
42  Q_FOREACH ( QByteArray h, mReply->rawHeaderList() )
43  {
44  headers.insert( h, mReply->rawHeader( h ) );
45  }
46  mHeaders.append( headers );
47  mBodies.append( mReply->readAll() );
48  }
49  else // multipart
50  {
51  QString contentType = mReply->header( QNetworkRequest::ContentTypeHeader ).toString();
52  QgsDebugMsg( "contentType: " + contentType );
53 
54  QRegExp re( ".*boundary=\"?([^\"]+)\"?\\s?", Qt::CaseInsensitive );
55 
56  if ( !( re.indexIn( contentType ) == 0 ) )
57  {
58  mError = tr( "Cannot find boundary in multipart content type" );
59  return;
60  }
61 
62  QString boundary = re.cap( 1 );
63  QgsDebugMsg( QString( "boundary = %1 size = %2" ).arg( boundary ).arg( boundary.size() ) );
64  boundary = "--" + boundary;
65 
66  // Lines should be terminated by CRLF ("\r\n") but any new line combination may appear
67  QByteArray data = mReply->readAll();
68  int from, to;
69  from = data.indexOf( boundary.toAscii(), 0 ) + boundary.length() + 1;
70  //QVector<QByteArray> partHeaders;
71  //QVector<QByteArray> partBodies;
72  while ( true )
73  {
74  // 'to' is not really 'to', but index of the next byte after the end of part
75  to = data.indexOf( boundary.toAscii(), from );
76  if ( to < 0 )
77  {
78  QgsDebugMsg( QString( "No more boundaries, rest size = %1" ).arg( data.size() - from - 1 ) );
79  // It may be end, last boundary is followed by '--'
80  if ( data.size() - from - 1 == 2 && QString( data.mid( from, 2 ) ) == "--" ) // end
81  {
82  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  if ( data.size() - from > 1 )
88  {
89  to = data.size(); // out of range OK
90  }
91  else
92  {
93  break;
94  }
95  }
96  QgsDebugMsg( QString( "part %1 - %2" ).arg( from ).arg( to ) );
97  QByteArray part = data.mid( from, to - from );
98  // Remove possible new line from beginning
99  while ( !part.isEmpty() && ( part.at( 0 ) == '\r' || part.at( 0 ) == '\n' ) )
100  {
101  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  int pos = 0; // body start
106  while ( pos < part.size() - 1 )
107  {
108  if ( part.at( pos ) == '\n' && ( part.at( pos + 1 ) == '\n' || part.at( pos + 1 ) == '\r' ) )
109  {
110  if ( part.at( pos + 1 ) == '\r' ) pos++;
111  pos += 2;
112  break;
113  }
114  pos++;
115  }
116  // parse headers
117  RawHeaderMap headersMap;
118  QByteArray headers = part.left( pos );
119  QgsDebugMsg( "headers:\n" + headers );
120 
121  QStringList headerRows = QString( headers ).split( QRegExp( "[\n\r]+" ) );
122  Q_FOREACH ( const QString& row, headerRows )
123  {
124  QgsDebugMsg( "row = " + row );
125  QStringList kv = row.split( ": " );
126  headersMap.insert( kv.value( 0 ).toAscii(), kv.value( 1 ).toAscii() );
127  }
128  mHeaders.append( headersMap );
129 
130  mBodies.append( part.mid( pos ) );
131 
132  from = to + boundary.length();
133  }
134  }
135  mValid = true;
136 }
137 
139 {
140  if ( !reply ) return false;
141 
142  QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
143  QgsDebugMsg( "contentType: " + contentType );
144 
145  // Multipart content type examples:
146  // multipart/mixed; boundary=wcs
147  // multipart/mixed; boundary="wcs"\n
148  return contentType.startsWith( "multipart/", Qt::CaseInsensitive );
149 }
QList< QByteArray > rawHeaderList() const
QString cap(int nth) const
char at(int i) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QList< RawHeaderMap > headers() const
Get headers.
int size() const
bool isEmpty() const
QString tr(const char *sourceText, const char *disambiguation, int n)
T value(int i) const
static bool isMultipart(QNetworkReply *reply)
Test if reply is multipart.
int indexIn(const QString &str, int offset, CaretMode caretMode) const
int indexOf(char ch, int from) const
void append(const T &value)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
QByteArray readAll()
QByteArray rawHeader(const QByteArray &headerName) const
QgsNetworkReplyParser(QNetworkReply *reply)
Constructor.
QByteArray mid(int pos, int len) const
QVariant header(QNetworkRequest::KnownHeaders header) const
QByteArray left(int len) const
int length() const
iterator insert(const Key &key, const T &value)
int size() const
QByteArray & remove(int pos, int len)
QString toString() const
QByteArray toAscii() const