QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsnetworkcontentfetcher.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsnetworkcontentfetcher.cpp
3  -------------------
4  begin : July, 2014
5  copyright : (C) 2014 by Nyall Dawson
6  email : nyall dot dawson at gmail dot 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 
21 #include "qgsmessagelog.h"
22 #include "qgsapplication.h"
23 #include <QNetworkReply>
24 #include <QTextCodec>
25 
27  : mReply( nullptr )
28  , mContentLoaded( false )
29 {
30 
31 }
32 
34 {
35  if ( mReply && mReply->isRunning() )
36  {
37  //cancel running request
38  mReply->abort();
39  }
40  if ( mReply )
41  {
42  mReply->deleteLater();
43  }
44 }
45 
47 {
48  QUrl nextUrlToFetch = url;
49  mContentLoaded = false;
50 
51  //get contents
52  QNetworkRequest request( nextUrlToFetch );
53 
54  if ( mReply )
55  {
56  //cancel any in progress requests
57  mReply->abort();
58  mReply->deleteLater();
59  mReply = nullptr;
60  }
61 
62  mReply = QgsNetworkAccessManager::instance()->get( request );
63  connect( mReply, SIGNAL( finished() ), this, SLOT( contentLoaded() ) );
64 }
65 
67 {
68  if ( !mContentLoaded )
69  {
70  return nullptr;
71  }
72 
73  return mReply;
74 }
75 
77 {
78  if ( !mContentLoaded || !mReply )
79  {
80  return QString();
81  }
82 
83  QByteArray array = mReply->readAll();
84 
85  //correctly encode reply as unicode
86  QTextCodec* codec = codecForHtml( array );
87  return codec->toUnicode( array );
88 }
89 
90 QTextCodec* QgsNetworkContentFetcher::codecForHtml( QByteArray& array ) const
91 {
92  //QTextCodec::codecForHtml fails to detect "<meta charset="utf-8"/>" type tags
93  //see https://bugreports.qt-project.org/browse/QTBUG-41011
94  //so test for that ourselves
95 
96  //basic check
97  QTextCodec* codec = QTextCodec::codecForUtfText( array, nullptr );
98  if ( codec )
99  {
100  return codec;
101  }
102 
103  //check for meta charset tag
104  QByteArray header = array.left( 1024 ).toLower();
105  int pos = header.indexOf( "meta charset=" );
106  if ( pos != -1 )
107  {
108  pos += int( strlen( "meta charset=" ) ) + 1;
109  int pos2 = header.indexOf( '\"', pos );
110  QByteArray cs = header.mid( pos, pos2 - pos );
111  codec = QTextCodec::codecForName( cs );
112  if ( codec )
113  {
114  return codec;
115  }
116  }
117 
118  //fallback to QTextCodec::codecForHtml
119  codec = QTextCodec::codecForHtml( array, codec );
120  if ( codec )
121  {
122  return codec;
123  }
124 
125  //no luck, default to utf-8
126  return QTextCodec::codecForName( "UTF-8" );
127 }
128 
129 void QgsNetworkContentFetcher::contentLoaded( bool ok )
130 {
131  Q_UNUSED( ok );
132 
133  if ( mReply->error() != QNetworkReply::NoError )
134  {
135  QgsMessageLog::logMessage( tr( "HTTP fetch %1 failed with error %2" ).arg( mReply->url().toString(), mReply->errorString() ) );
136  mContentLoaded = true;
137  emit finished();
138  return;
139  }
140 
141  QVariant redirect = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
142  if ( redirect.isNull() )
143  {
144  //no error or redirect, got target
145  QVariant status = mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
146  if ( !status.isNull() && status.toInt() >= 400 )
147  {
148  QgsMessageLog::logMessage( tr( "HTTP fetch %1 failed with error %2" ).arg( mReply->url().toString(), status.toString() ) );
149  }
150  mContentLoaded = true;
151  emit finished();
152  return;
153  }
154 
155  //redirect, so fetch redirect target
156  mReply->deleteLater();
157  fetchContent( redirect.toUrl() );
158 }
159 
160 
161 
162 
QUrl toUrl() const
QByteArray toLower() const
QString errorString() const
bool isRunning() const
QNetworkReply * reply()
Returns a reference to the network reply.
QString toString(QFlags< QUrl::FormattingOption > options) const
QString contentAsString() const
Returns the fetched content as a string.
QString tr(const char *sourceText, const char *disambiguation, int n)
int indexOf(char ch, int from) const
int toInt(bool *ok) const
bool isNull() const
QByteArray readAll()
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
void finished()
Emitted when content has loaded.
void deleteLater()
QByteArray mid(int pos, int len) const
virtual void abort()=0
QByteArray left(int len) const
QTextCodec * codecForHtml(const QByteArray &ba)
static QgsNetworkAccessManager * instance()
returns a pointer to the single instance
QTextCodec * codecForName(const QByteArray &name)
QVariant attribute(QNetworkRequest::Attribute code) const
QUrl url() const
NetworkError error() const
QNetworkReply * get(const QNetworkRequest &request)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void fetchContent(const QUrl &url)
Fetches content from a remote URL and handles redirects.
QString toString() const
QString toUnicode(const QByteArray &a) const
QTextCodec * codecForUtfText(const QByteArray &ba)