ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

networkreadstream.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00024 
00025 #include <curl/curl.h>
00026 #include "backends/networking/curl/networkreadstream.h"
00027 #include "backends/networking/curl/connectionmanager.h"
00028 #include "base/version.h"
00029 
00030 namespace Networking {
00031 
00032 size_t NetworkReadStream::curlDataCallback(char *d, size_t n, size_t l, void *p) {
00033     NetworkReadStream *stream = (NetworkReadStream *)p;
00034     if (stream)
00035         return stream->_backingStream.write(d, n * l);
00036     return 0;
00037 }
00038 
00039 size_t NetworkReadStream::curlReadDataCallback(char *d, size_t n, size_t l, void *p) {
00040     NetworkReadStream *stream = (NetworkReadStream *)p;
00041     if (stream)
00042         return stream->fillWithSendingContents(d, n * l);
00043     return 0;
00044 }
00045 
00046 size_t NetworkReadStream::curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
00047     NetworkReadStream *stream = (NetworkReadStream *)p;
00048     if (stream)
00049         return stream->addResponseHeaders(d, n * l);
00050     return 0;
00051 }
00052 
00053 static int curlProgressCallback(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
00054     NetworkReadStream *stream = (NetworkReadStream *)p;
00055     if (stream)
00056         stream->setProgress(dlnow, dltotal);
00057     return 0;
00058 }
00059 
00060 int NetworkReadStream::curlProgressCallbackOlder(void *p, double dltotal, double dlnow, double ultotal, double ulnow) {
00061     // for libcurl older than 7.32.0 (CURLOPT_PROGRESSFUNCTION)
00062     return curlProgressCallback(p, (curl_off_t)dltotal, (curl_off_t)dlnow, (curl_off_t)ultotal, (curl_off_t)ulnow);
00063 }
00064 
00065 void NetworkReadStream::init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
00066     _eos = _requestComplete = false;
00067     _sendingContentsBuffer = nullptr;
00068     _sendingContentsSize = _sendingContentsPos = 0;
00069     _progressDownloaded = _progressTotal = 0;
00070     _bufferCopy = nullptr;
00071 
00072     _easy = curl_easy_init();
00073     curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
00074     curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
00075     curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
00076     curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
00077     curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
00078     curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
00079     curl_easy_setopt(_easy, CURLOPT_URL, url);
00080     curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
00081     curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
00082     curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
00083     curl_easy_setopt(_easy, CURLOPT_USERAGENT, gScummVMFullVersion);
00084     curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
00085     curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
00086     curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
00087 #if LIBCURL_VERSION_NUM >= 0x072000
00088     // CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
00089     // CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used
00090     curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, curlProgressCallback);
00091     curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
00092 #endif
00093     if (uploading) {
00094         curl_easy_setopt(_easy, CURLOPT_UPLOAD, 1L);
00095         curl_easy_setopt(_easy, CURLOPT_READDATA, this);
00096         curl_easy_setopt(_easy, CURLOPT_READFUNCTION, curlReadDataCallback);
00097         _sendingContentsBuffer = buffer;
00098         _sendingContentsSize = bufferSize;
00099     } else if (usingPatch) {
00100         curl_easy_setopt(_easy, CURLOPT_CUSTOMREQUEST, "PATCH");
00101     } else {
00102         if (post || bufferSize != 0) {
00103             curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, bufferSize);
00104 #if LIBCURL_VERSION_NUM >= 0x071101
00105             // CURLOPT_COPYPOSTFIELDS available since curl 7.17.1
00106             curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, buffer);
00107 #else
00108             _bufferCopy = (byte*)malloc(bufferSize);
00109             memcpy(_bufferCopy, buffer, bufferSize);
00110             curl_easy_setopt(_easy, CURLOPT_POSTFIELDS, _bufferCopy);
00111 #endif
00112         }
00113     }
00114     ConnMan.registerEasyHandle(_easy);
00115 }
00116 
00117 void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles) {
00118     _eos = _requestComplete = false;
00119     _sendingContentsBuffer = nullptr;
00120     _sendingContentsSize = _sendingContentsPos = 0;
00121     _progressDownloaded = _progressTotal = 0;
00122     _bufferCopy = nullptr;
00123 
00124     _easy = curl_easy_init();
00125     curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
00126     curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
00127     curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
00128     curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
00129     curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
00130     curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
00131     curl_easy_setopt(_easy, CURLOPT_URL, url);
00132     curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
00133     curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
00134     curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
00135     curl_easy_setopt(_easy, CURLOPT_USERAGENT, gScummVMFullVersion);
00136     curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
00137     curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
00138     curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
00139 #if LIBCURL_VERSION_NUM >= 0x072000
00140     // CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
00141     // CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used
00142     curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, curlProgressCallback);
00143     curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
00144 #endif
00145 
00146     // set POST multipart upload form fields/files
00147     struct curl_httppost *formpost = nullptr;
00148     struct curl_httppost *lastptr = nullptr;
00149 
00150     for (Common::HashMap<Common::String, Common::String>::iterator i = formFields.begin(); i != formFields.end(); ++i) {
00151         CURLFORMcode code = curl_formadd(
00152             &formpost,
00153             &lastptr,
00154             CURLFORM_COPYNAME, i->_key.c_str(),
00155             CURLFORM_COPYCONTENTS, i->_value.c_str(),
00156             CURLFORM_END
00157         );
00158 
00159         if (code != CURL_FORMADD_OK)
00160             warning("NetworkReadStream: field curl_formadd('%s') failed", i->_key.c_str());
00161     }
00162 
00163     for (Common::HashMap<Common::String, Common::String>::iterator i = formFiles.begin(); i != formFiles.end(); ++i) {
00164         CURLFORMcode code = curl_formadd(
00165             &formpost,
00166             &lastptr,
00167             CURLFORM_COPYNAME, i->_key.c_str(),
00168             CURLFORM_FILE, i->_value.c_str(),
00169             CURLFORM_END
00170         );
00171 
00172         if (code != CURL_FORMADD_OK)
00173             warning("NetworkReadStream: file curl_formadd('%s') failed", i->_key.c_str());
00174     }
00175 
00176     curl_easy_setopt(_easy, CURLOPT_HTTPPOST, formpost);
00177 
00178     ConnMan.registerEasyHandle(_easy);
00179 }
00180 
00181 NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading, bool usingPatch) :
00182         _backingStream(DisposeAfterUse::YES) {
00183     init(url, headersList, (const byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
00184 }
00185 
00186 NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles) :
00187         _backingStream(DisposeAfterUse::YES) {
00188     init(url, headersList, formFields, formFiles);
00189 }
00190 
00191 NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) :
00192         _backingStream(DisposeAfterUse::YES) {
00193     init(url, headersList, buffer, bufferSize, uploading, usingPatch, post);
00194 }
00195 
00196 NetworkReadStream::~NetworkReadStream() {
00197     if (_easy)
00198         curl_easy_cleanup(_easy);
00199     free(_bufferCopy);
00200 }
00201 
00202 bool NetworkReadStream::eos() const {
00203     return _eos;
00204 }
00205 
00206 uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
00207     uint32 actuallyRead = _backingStream.read(dataPtr, dataSize);
00208 
00209     if (actuallyRead == 0) {
00210         if (_requestComplete)
00211             _eos = true;
00212         return 0;
00213     }
00214 
00215     return actuallyRead;
00216 }
00217 
00218 void NetworkReadStream::finished() {
00219     _requestComplete = true;
00220 }
00221 
00222 long NetworkReadStream::httpResponseCode() const {
00223     long responseCode = -1;
00224     if (_easy)
00225         curl_easy_getinfo(_easy, CURLINFO_RESPONSE_CODE, &responseCode);
00226     return responseCode;
00227 }
00228 
00229 Common::String NetworkReadStream::currentLocation() const {
00230     Common::String result = "";
00231     if (_easy) {
00232         char *pointer;
00233         curl_easy_getinfo(_easy, CURLINFO_EFFECTIVE_URL, &pointer);
00234         result = Common::String(pointer);
00235     }
00236     return result;
00237 }
00238 
00239 Common::String NetworkReadStream::responseHeaders() const {
00240     return _responseHeaders;
00241 }
00242 
00243 uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
00244     uint32 sendSize = _sendingContentsSize - _sendingContentsPos;
00245     if (sendSize > maxSize)
00246         sendSize = maxSize;
00247     for (uint32 i = 0; i < sendSize; ++i) {
00248         bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
00249     }
00250     _sendingContentsPos += sendSize;
00251     return sendSize;
00252 }
00253 
00254 uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 bufferSize) {
00255     _responseHeaders += Common::String(buffer, bufferSize);
00256     return bufferSize;
00257 }
00258 
00259 double NetworkReadStream::getProgress() const {
00260     if (_progressTotal < 1)
00261         return 0;
00262     return (double)_progressDownloaded / (double)_progressTotal;
00263 }
00264 
00265 void NetworkReadStream::setProgress(uint64 downloaded, uint64 total) {
00266     _progressDownloaded = downloaded;
00267     _progressTotal = total;
00268 }
00269 
00270 } // End of namespace Cloud


Generated on Sat May 25 2019 05:00:50 for ResidualVM by doxygen 1.7.1
curved edge   curved edge