Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgscplhttpfetchoverrider.cpp
3 : : ----------------------------
4 : : begin : September 2020
5 : : copyright : (C) 2020 by Even Rouault
6 : : email : even.rouault at spatialys.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 "qgscplhttpfetchoverrider.h"
17 : : #include "qgslogger.h"
18 : : #include "qgsblockingnetworkrequest.h"
19 : :
20 : : #include "cpl_http.h"
21 : : #include "gdal.h"
22 : :
23 : : #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,2,0)
24 : :
25 : : QgsCPLHTTPFetchOverrider::QgsCPLHTTPFetchOverrider( const QString &authCfg, QgsFeedback *feedback )
26 : : {
27 : : Q_UNUSED( authCfg );
28 : : Q_UNUSED( feedback );
29 : : Q_UNUSED( mAuthCfg );
30 : : Q_UNUSED( mFeedback );
31 : : }
32 : :
33 : : QgsCPLHTTPFetchOverrider::~QgsCPLHTTPFetchOverrider()
34 : : {
35 : : }
36 : :
37 : : #else
38 : :
39 : 1758 : QgsCPLHTTPFetchOverrider::QgsCPLHTTPFetchOverrider( const QString &authCfg, QgsFeedback *feedback ):
40 : 1758 : mAuthCfg( authCfg ),
41 : 1758 : mFeedback( feedback )
42 : : {
43 : 1758 : CPLHTTPPushFetchCallback( QgsCPLHTTPFetchOverrider::callback, this );
44 : 1758 : }
45 : :
46 : 1758 : QgsCPLHTTPFetchOverrider::~QgsCPLHTTPFetchOverrider()
47 : : {
48 : 1758 : CPLHTTPPopFetchCallback();
49 : 1758 : }
50 : :
51 : :
52 : 0 : CPLHTTPResult *QgsCPLHTTPFetchOverrider::callback( const char *pszURL,
53 : : CSLConstList papszOptions,
54 : : GDALProgressFunc /* pfnProgress */,
55 : : void * /*pProgressArg */,
56 : : CPLHTTPFetchWriteFunc pfnWrite,
57 : : void *pWriteArg,
58 : : void *pUserData )
59 : : {
60 : 0 : QgsCPLHTTPFetchOverrider *pThis = static_cast<QgsCPLHTTPFetchOverrider *>( pUserData );
61 : :
62 : 0 : auto psResult = static_cast<CPLHTTPResult *>( CPLCalloc( sizeof( CPLHTTPResult ), 1 ) );
63 : 0 : if ( CSLFetchNameValue( papszOptions, "CLOSE_PERSISTENT" ) )
64 : : {
65 : : // CLOSE_PERSISTENT is a CPL trick to maintain a curl handle open over
66 : : // a series of CPLHTTPFetch() call to the same server.
67 : : // Just return a dummy result to acknowledge we 'processed' it
68 : 0 : return psResult;
69 : : }
70 : :
71 : : // Look for important options we don't handle yet
72 : 0 : for ( const char *pszOption : { "FORM_FILE_PATH", "FORM_ITEM_COUNT" } )
73 : : {
74 : 0 : if ( CSLFetchNameValue( papszOptions, pszOption ) )
75 : : {
76 : 0 : QgsDebugMsg( QStringLiteral( "Option %1 not handled" ).arg( pszOption ) );
77 : 0 : return nullptr;
78 : : }
79 : : }
80 : :
81 : 0 : QgsBlockingNetworkRequest blockingRequest;
82 : 0 : blockingRequest.setAuthCfg( pThis->mAuthCfg );
83 : :
84 : 0 : QNetworkRequest request( QString::fromUtf8( pszURL ) );
85 : 0 : for ( const auto &keyValue : pThis->mAttributes )
86 : : {
87 : 0 : request.setAttribute( keyValue.first, keyValue.second );
88 : : }
89 : :
90 : : // Store request headers
91 : 0 : const char *pszHeaders = CSLFetchNameValue( papszOptions, "HEADERS" );
92 : 0 : if ( pszHeaders )
93 : : {
94 : 0 : char **papszTokensHeaders = CSLTokenizeString2( pszHeaders, "\r\n", 0 );
95 : 0 : for ( int i = 0; papszTokensHeaders[i] != nullptr; ++i )
96 : : {
97 : 0 : char *pszKey = nullptr;
98 : 0 : const char *pszValue = CPLParseNameValue( papszTokensHeaders[i], &pszKey );
99 : 0 : if ( pszKey && pszValue )
100 : : {
101 : 0 : request.setRawHeader(
102 : 0 : QByteArray::fromStdString( pszKey ),
103 : 0 : QByteArray::fromStdString( pszValue ) );
104 : 0 : }
105 : 0 : CPLFree( pszKey );
106 : 0 : }
107 : 0 : CSLDestroy( papszTokensHeaders );
108 : 0 : }
109 : :
110 : 0 : constexpr bool forceRefresh = true;
111 : 0 : const char *pszCustomRequest = CSLFetchNameValue( papszOptions, "CUSTOMREQUEST" );
112 : 0 : const char *pszPostFields = CSLFetchNameValue( papszOptions, "POSTFIELDS" );
113 : : QgsBlockingNetworkRequest::ErrorCode errCode;
114 : 0 : if ( pszPostFields )
115 : : {
116 : 0 : if ( pszCustomRequest == nullptr || EQUAL( pszCustomRequest, "POST" ) )
117 : : {
118 : 0 : errCode = blockingRequest.post( request,
119 : 0 : QByteArray::fromStdString( pszPostFields ),
120 : : forceRefresh,
121 : 0 : pThis->mFeedback );
122 : 0 : }
123 : 0 : else if ( EQUAL( pszCustomRequest, "PUT" ) )
124 : : {
125 : 0 : errCode = blockingRequest.put( request,
126 : 0 : QByteArray::fromStdString( pszPostFields ),
127 : 0 : pThis->mFeedback );
128 : 0 : }
129 : : else
130 : : {
131 : 0 : QgsDebugMsg( QStringLiteral( "Invalid CUSTOMREQUEST = %1 when POSTFIELDS is defined" ).arg( pszCustomRequest ) );
132 : 0 : return nullptr;
133 : : }
134 : 0 : }
135 : : else
136 : : {
137 : 0 : if ( pszCustomRequest == nullptr || EQUAL( pszCustomRequest, "GET" ) )
138 : : {
139 : 0 : errCode = blockingRequest.get( request, forceRefresh, pThis->mFeedback );
140 : 0 : }
141 : 0 : else if ( EQUAL( pszCustomRequest, "HEAD" ) )
142 : : {
143 : 0 : errCode = blockingRequest.head( request, forceRefresh, pThis->mFeedback );
144 : 0 : }
145 : 0 : else if ( EQUAL( pszCustomRequest, "DELETE" ) )
146 : : {
147 : 0 : errCode = blockingRequest.deleteResource( request, pThis->mFeedback );
148 : 0 : }
149 : : else
150 : : {
151 : 0 : QgsDebugMsg( QStringLiteral( "Invalid CUSTOMREQUEST = %1 when POSTFIELDS is not defined" ).arg( pszCustomRequest ) );
152 : 0 : return nullptr;
153 : : }
154 : : }
155 : 0 : if ( errCode != QgsBlockingNetworkRequest::NoError )
156 : : {
157 : 0 : psResult->nStatus = 1;
158 : 0 : psResult->pszErrBuf = CPLStrdup( blockingRequest.errorMessage().toUtf8() );
159 : 0 : return psResult;
160 : : }
161 : :
162 : 0 : QgsNetworkReplyContent reply( blockingRequest.reply() );
163 : :
164 : : // Store response headers
165 : 0 : for ( const auto &pair : reply.rawHeaderPairs() )
166 : : {
167 : 0 : if ( EQUAL( pair.first.toStdString().c_str(), "Content-Type" ) )
168 : : {
169 : 0 : CPLFree( psResult->pszContentType );
170 : 0 : psResult->pszContentType = CPLStrdup( pair.second.toStdString().c_str() );
171 : 0 : }
172 : 0 : psResult->papszHeaders = CSLAddNameValue(
173 : 0 : psResult->papszHeaders,
174 : 0 : pair.first.toStdString().c_str(),
175 : 0 : pair.second.toStdString().c_str() );
176 : : }
177 : :
178 : : // Process content
179 : 0 : QByteArray content( reply.content() );
180 : :
181 : : // Poor-man implementation of the pfnWrite mechanism which is supposed to be
182 : : // called on the fly as bytes are received
183 : 0 : if ( pfnWrite )
184 : : {
185 : 0 : if ( static_cast<int>( pfnWrite( content.data(), 1, content.size(), pWriteArg ) ) != content.size() )
186 : : {
187 : 0 : psResult->nStatus = 1;
188 : 0 : psResult->pszErrBuf = CPLStrdup( "download interrupted by user" );
189 : 0 : return psResult;
190 : : }
191 : 0 : }
192 : : else
193 : : {
194 : 0 : psResult->nDataLen = static_cast<int>( content.size() );
195 : 0 : psResult->pabyData = static_cast<GByte *>( CPLMalloc( psResult->nDataLen + 1 ) );
196 : 0 : memcpy( psResult->pabyData, content.constData(), psResult->nDataLen );
197 : 0 : psResult->pabyData[psResult->nDataLen] = 0;
198 : : }
199 : :
200 : 0 : return psResult;
201 : 0 : }
202 : :
203 : : #endif
204 : :
205 : 3516 : void QgsCPLHTTPFetchOverrider::setAttribute( QNetworkRequest::Attribute code, const QVariant &value )
206 : : {
207 : 3516 : mAttributes[code] = value;
208 : 3516 : }
|