1 /***************************************************************************
2 qgsfiledownloader.cpp
3 --------------------------------------
4 Date : November 2016
5 Copyright : (C) 2016 by Alessandro Pasotti
6 Email : apasotti at boundlessgeo dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgsfiledownloader.h"
17 #include "qgsnetworkaccessmanager.h"
18 #include "qgsapplication.h"
19 #include "qgsauthmanager.h"
20
21 #include <QNetworkAccessManager>
22 #include <QNetworkRequest>
23 #include <QNetworkReply>
24 #ifndef QT_NO_SSL
25 #include <QSslError>
26 #endif
27
QgsFileDownloader(const QUrl & url,const QString & outputFileName,const QString & authcfg,bool delayStart,Qgis::HttpMethod httpMethod,const QByteArray & data)28 QgsFileDownloader::QgsFileDownloader( const QUrl &url, const QString &outputFileName, const QString &authcfg, bool delayStart, Qgis::HttpMethod httpMethod, const QByteArray &data )
29 : mUrl( url )
30 , mDownloadCanceled( false )
31 , mHttpMethod( httpMethod )
32 , mData( data )
33 {
34 mFile.setFileName( outputFileName );
35 mAuthCfg = authcfg;
36 if ( !delayStart )
37 startDownload();
38 }
39
40
~QgsFileDownloader()41 QgsFileDownloader::~QgsFileDownloader()
42 {
43 if ( mReply )
44 {
45 mReply->abort();
46 mReply->deleteLater();
47 }
48 }
49
startDownload()50 void QgsFileDownloader::startDownload()
51 {
52 QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
53
54 QNetworkRequest request( mUrl );
55 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsFileDownloader" ) );
56 if ( !mAuthCfg.isEmpty() )
57 {
58 QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg );
59 }
60
61 if ( mReply )
62 {
63 disconnect( mReply, &QNetworkReply::readyRead, this, &QgsFileDownloader::onReadyRead );
64 disconnect( mReply, &QNetworkReply::finished, this, &QgsFileDownloader::onFinished );
65 disconnect( mReply, &QNetworkReply::downloadProgress, this, &QgsFileDownloader::onDownloadProgress );
66 mReply->abort();
67 mReply->deleteLater();
68 }
69
70 switch ( mHttpMethod )
71 {
72 case Qgis::HttpMethod::Get:
73 {
74 mReply = nam->get( request );
75 break;
76 }
77 case Qgis::HttpMethod::Post:
78 {
79 mReply = nam->post( request, mData );
80 break;
81 }
82 }
83
84 if ( !mAuthCfg.isEmpty() )
85 {
86 QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg );
87 }
88
89 connect( mReply, &QNetworkReply::readyRead, this, &QgsFileDownloader::onReadyRead );
90 connect( mReply, &QNetworkReply::finished, this, &QgsFileDownloader::onFinished );
91 connect( mReply, &QNetworkReply::downloadProgress, this, &QgsFileDownloader::onDownloadProgress );
92 connect( nam, qOverload< QNetworkReply *>( &QgsNetworkAccessManager::requestTimedOut ), this, &QgsFileDownloader::onRequestTimedOut, Qt::UniqueConnection );
93 #ifndef QT_NO_SSL
94 connect( nam, &QgsNetworkAccessManager::sslErrors, this, &QgsFileDownloader::onSslErrors, Qt::UniqueConnection );
95 #endif
96 }
97
cancelDownload()98 void QgsFileDownloader::cancelDownload()
99 {
100 mDownloadCanceled = true;
101 emit downloadCanceled();
102 onFinished();
103 }
104
onRequestTimedOut(QNetworkReply * reply)105 void QgsFileDownloader::onRequestTimedOut( QNetworkReply *reply )
106 {
107 if ( reply == mReply )
108 error( tr( "Network request %1 timed out" ).arg( mUrl.toString() ) );
109 }
110
111 #ifndef QT_NO_SSL
onSslErrors(QNetworkReply * reply,const QList<QSslError> & errors)112 void QgsFileDownloader::onSslErrors( QNetworkReply *reply, const QList<QSslError> &errors )
113 {
114 if ( reply == mReply )
115 {
116 QStringList errorMessages;
117 errorMessages.reserve( errors.size() + 1 );
118 errorMessages << QStringLiteral( "SSL Errors: " );
119
120 for ( const QSslError &error : errors )
121 errorMessages << error.errorString();
122
123 error( errorMessages );
124 }
125 }
126 #endif
127
128
error(const QStringList & errorMessages)129 void QgsFileDownloader::error( const QStringList &errorMessages )
130 {
131 for ( const QString &error : errorMessages )
132 mErrors << error;
133
134 if ( mReply )
135 mReply->abort();
136 emit downloadError( mErrors );
137 }
138
error(const QString & errorMessage)139 void QgsFileDownloader::error( const QString &errorMessage )
140 {
141 error( QStringList() << errorMessage );
142 }
143
onReadyRead()144 void QgsFileDownloader::onReadyRead()
145 {
146 Q_ASSERT( mReply );
147 if ( mFile.fileName().isEmpty() )
148 {
149 error( tr( "No output filename specified" ) );
150 onFinished();
151 }
152 else if ( ! mFile.isOpen() && ! mFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
153 {
154 error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
155 onFinished();
156 }
157 else
158 {
159 const QByteArray data = mReply->readAll();
160 mFile.write( data );
161 }
162 }
163
onFinished()164 void QgsFileDownloader::onFinished()
165 {
166 // when canceled
167 if ( ! mErrors.isEmpty() || mDownloadCanceled )
168 {
169 if ( mFile.isOpen() )
170 mFile.close();
171 if ( mFile.exists() )
172 mFile.remove();
173 }
174 else
175 {
176 // download finished normally
177 if ( mFile.isOpen() )
178 {
179 mFile.flush();
180 mFile.close();
181 }
182
183 // get redirection url
184 const QVariant redirectionTarget = mReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
185 if ( mReply->error() )
186 {
187 mFile.remove();
188 error( tr( "Download failed: %1" ).arg( mReply->errorString() ) );
189 }
190 else if ( !redirectionTarget.isNull() )
191 {
192 const QUrl newUrl = mUrl.resolved( redirectionTarget.toUrl() );
193 mUrl = newUrl;
194 mReply->deleteLater();
195 if ( !mFile.open( QIODevice::WriteOnly ) )
196 {
197 mFile.remove();
198 error( tr( "Cannot open output file: %1" ).arg( mFile.fileName() ) );
199 }
200 else
201 {
202 mFile.resize( 0 );
203 mFile.close();
204 startDownload();
205 }
206 return;
207 }
208 else
209 {
210 emit downloadCompleted( mReply->url() );
211 }
212 }
213 emit downloadExited();
214 this->deleteLater();
215 }
216
217
onDownloadProgress(qint64 bytesReceived,qint64 bytesTotal)218 void QgsFileDownloader::onDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
219 {
220 if ( mDownloadCanceled )
221 {
222 return;
223 }
224 emit downloadProgress( bytesReceived, bytesTotal );
225 }
226
227