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

googledriveuploadrequest.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 #include "backends/cloud/googledrive/googledriveuploadrequest.h"
00024 #include "backends/cloud/googledrive/googledrivestorage.h"
00025 #include "backends/cloud/iso8601.h"
00026 #include "backends/cloud/storage.h"
00027 #include "backends/networking/curl/connectionmanager.h"
00028 #include "backends/networking/curl/curljsonrequest.h"
00029 #include "backends/networking/curl/networkreadstream.h"
00030 #include "common/json.h"
00031 #include "googledrivetokenrefresher.h"
00032 
00033 namespace Cloud {
00034 namespace GoogleDrive {
00035 
00036 #define GOOGLEDRIVE_API_FILES "https://www.googleapis.com/upload/drive/v3/files"
00037 
00038 GoogleDriveUploadRequest::GoogleDriveUploadRequest(GoogleDriveStorage *storage, Common::String path, Common::SeekableReadStream *contents, Storage::UploadCallback callback, Networking::ErrorCallback ecb):
00039     Networking::Request(nullptr, ecb), _storage(storage), _savePath(path), _contentsStream(contents), _uploadCallback(callback),
00040     _workingRequest(nullptr), _ignoreCallback(false) {
00041     start();
00042 }
00043 
00044 GoogleDriveUploadRequest::~GoogleDriveUploadRequest() {
00045     _ignoreCallback = true;
00046     if (_workingRequest)
00047         _workingRequest->finish();
00048     delete _contentsStream;
00049     delete _uploadCallback;
00050 }
00051 
00052 void GoogleDriveUploadRequest::start() {
00053     _ignoreCallback = true;
00054     if (_workingRequest)
00055         _workingRequest->finish();
00056     if (_contentsStream == nullptr || !_contentsStream->seek(0)) {
00057         warning("GoogleDriveUploadRequest: cannot restart because stream couldn't seek(0)");
00058         finishError(Networking::ErrorResponse(this, false, true, "", -1));
00059         return;
00060     }
00061     _resolvedId = ""; //used to update file contents
00062     _parentId = ""; //used to create file within parent directory
00063     _serverReceivedBytes = 0;
00064     _ignoreCallback = false;
00065 
00066     resolveId();
00067 }
00068 
00069 void GoogleDriveUploadRequest::resolveId() {
00070     //check whether such file already exists
00071     Storage::UploadCallback innerCallback = new Common::Callback<GoogleDriveUploadRequest, Storage::UploadResponse>(this, &GoogleDriveUploadRequest::idResolvedCallback);
00072     Networking::ErrorCallback innerErrorCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::idResolveFailedCallback);
00073     _workingRequest = _storage->resolveFileId(_savePath, innerCallback, innerErrorCallback);
00074 }
00075 
00076 void GoogleDriveUploadRequest::idResolvedCallback(Storage::UploadResponse response) {
00077     _workingRequest = nullptr;
00078     if (_ignoreCallback)
00079         return;
00080     _resolvedId = response.value.id();
00081     startUpload();
00082 }
00083 
00084 void GoogleDriveUploadRequest::idResolveFailedCallback(Networking::ErrorResponse error) {
00085     _workingRequest = nullptr;
00086     if (_ignoreCallback)
00087         return;
00088 
00089     //not resolved => error or no such file
00090     if (error.response.contains("no such file found in its parent directory")) {
00091         //parent's id after the '\n'
00092         Common::String parentId = error.response;
00093         for (uint32 i = 0; i < parentId.size(); ++i)
00094             if (parentId[i] == '\n') {
00095                 parentId.erase(0, i + 1);
00096                 break;
00097             }
00098 
00099         _parentId = parentId;
00100         startUpload();
00101         return;
00102     }
00103 
00104     finishError(error);
00105 }
00106 
00107 void GoogleDriveUploadRequest::startUpload() {
00108     Common::String name = _savePath;
00109     for (uint32 i = name.size(); i > 0; --i) {
00110         if (name[i - 1] == '/' || name[i - 1] == '\\') {
00111             name.erase(0, i);
00112             break;
00113         }
00114     }
00115 
00116     Common::String url = GOOGLEDRIVE_API_FILES;
00117     if (_resolvedId != "")
00118         url += "/" + ConnMan.urlEncode(_resolvedId);
00119     url += "?uploadType=resumable&fields=id,mimeType,modifiedTime,name,size";
00120     Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, Networking::JsonResponse>(this, &GoogleDriveUploadRequest::startUploadCallback);
00121     Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::startUploadErrorCallback);
00122     Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
00123     request->addHeader("Authorization: Bearer " + _storage->accessToken());
00124     request->addHeader("Content-Type: application/json");
00125     if (_resolvedId != "")
00126         request->usePatch();
00127 
00128     Common::JSONObject jsonRequestParameters;
00129     if (_resolvedId != "") {
00130         jsonRequestParameters.setVal("id", new Common::JSONValue(_resolvedId));
00131     } else {
00132         Common::JSONArray parentsArray;
00133         parentsArray.push_back(new Common::JSONValue(_parentId));
00134         jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
00135     }
00136     jsonRequestParameters.setVal("name", new Common::JSONValue(name));
00137 
00138     Common::JSONValue value(jsonRequestParameters);
00139     request->addPostField(Common::JSON::stringify(&value));
00140 
00141     _workingRequest = ConnMan.addRequest(request);
00142 }
00143 
00144 void GoogleDriveUploadRequest::startUploadCallback(Networking::JsonResponse response) {
00145     _workingRequest = nullptr;
00146     if (_ignoreCallback)
00147         return;
00148 
00149     Networking::ErrorResponse error(this, false, true, "", -1);
00150     Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
00151     if (rq) {
00152         const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
00153         if (stream) {
00154             long code = stream->httpResponseCode();
00155             Common::String headers = stream->responseHeaders();
00156             if (code == 200) {
00157                 const char *cstr = headers.c_str();
00158                 const char *position = strstr(cstr, "Location: ");
00159 
00160                 if (position) {
00161                     Common::String result = "";
00162                     char c;
00163                     for (const char *i = position + 10; c = *i, c != 0; ++i) {
00164                         if (c == '\n' || c == '\r')
00165                             break;
00166                         result += c;
00167                     }
00168                     _uploadUrl = result;
00169                     uploadNextPart();
00170                     return;
00171                 }
00172             }
00173 
00174             error.httpResponseCode = code;
00175         }
00176     }
00177 
00178     Common::JSONValue *json = response.value;
00179     delete json;
00180 
00181     finishError(error);
00182 }
00183 
00184 void GoogleDriveUploadRequest::startUploadErrorCallback(Networking::ErrorResponse error) {
00185     _workingRequest = nullptr;
00186     if (_ignoreCallback)
00187         return;
00188     finishError(error);
00189 }
00190 
00191 void GoogleDriveUploadRequest::uploadNextPart() {
00192     const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
00193     Common::String url = _uploadUrl;
00194 
00195     Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, Networking::JsonResponse>(this, &GoogleDriveUploadRequest::partUploadedCallback);
00196     Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::partUploadedErrorCallback);
00197     Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
00198     request->addHeader("Authorization: Bearer " + _storage->accessToken());
00199     request->usePut();
00200 
00201     uint32 oldPos = _contentsStream->pos();
00202     if (oldPos != _serverReceivedBytes) {
00203         if (!_contentsStream->seek(_serverReceivedBytes)) {
00204             warning("GoogleDriveUploadRequest: cannot upload because stream couldn't seek(%lu)", _serverReceivedBytes);
00205             finishError(Networking::ErrorResponse(this, false, true, "", -1));
00206             return;
00207         }
00208         oldPos = _serverReceivedBytes;
00209     }
00210 
00211     byte *buffer = new byte[UPLOAD_PER_ONE_REQUEST];
00212     uint32 size = _contentsStream->read(buffer, UPLOAD_PER_ONE_REQUEST);
00213     if (size != 0)
00214         request->setBuffer(buffer, size);
00215 
00216     if (_uploadUrl != "") {
00217         if (_contentsStream->pos() == 0)
00218             request->addHeader(Common::String::format("Content-Length: 0"));
00219         else
00220             request->addHeader(Common::String::format("Content-Range: bytes %u-%u/%u", oldPos, _contentsStream->pos() - 1, _contentsStream->size()));
00221     }
00222 
00223     _workingRequest = ConnMan.addRequest(request);
00224 }
00225 
00226 bool GoogleDriveUploadRequest::handleHttp308(const Networking::NetworkReadStream *stream) {
00227     //308 Resume Incomplete, with Range: X-Y header
00228     if (!stream)
00229         return false;
00230     if (stream->httpResponseCode() != 308)
00231         return false; //seriously
00232 
00233     Common::String headers = stream->responseHeaders();
00234     const char *cstr = headers.c_str();
00235     for (int rangeTry = 0; rangeTry < 2; ++rangeTry) {
00236         const char *needle = (rangeTry == 0 ? "Range: 0-" : "Range: bytes=0-");
00237         uint32 needleLength = (rangeTry == 0 ? 9 : 15);
00238 
00239         const char *position = strstr(cstr, needle); //if it lost the first part, I refuse to talk with it
00240 
00241         if (position) {
00242             Common::String result = "";
00243             char c;
00244             for (const char *i = position + needleLength; c = *i, c != 0; ++i) {
00245                 if (c == '\n' || c == '\r')
00246                     break;
00247                 result += c;
00248             }
00249             _serverReceivedBytes = result.asUint64() + 1;
00250             uploadNextPart();
00251             return true;
00252         }
00253     }
00254 
00255     return false;
00256 }
00257 
00258 void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
00259     _workingRequest = nullptr;
00260     if (_ignoreCallback)
00261         return;
00262 
00263     Networking::ErrorResponse error(this, false, true, "", -1);
00264     Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
00265     if (rq) {
00266         const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
00267         if (stream) {
00268             long code = stream->httpResponseCode();
00269             error.httpResponseCode = code;
00270             if (code == 308 && handleHttp308(stream)) {
00271                 delete (Common::JSONValue *)response.value;
00272                 return;
00273             }
00274         }
00275     }
00276 
00277     Common::JSONValue *json = response.value;
00278     if (json == nullptr) {
00279         error.response = "Failed to parse JSON, null passed!";
00280         finishError(error);
00281         return;
00282     }
00283 
00284     if (json->isObject()) {
00285         Common::JSONObject object = json->asObject();
00286 
00287         if (object.contains("error")) {
00288             warning("GoogleDrive returned error: %s", json->stringify(true).c_str());
00289             error.response = json->stringify(true);
00290             finishError(error);
00291             delete json;
00292             return;
00293         }
00294 
00295         if (Networking::CurlJsonRequest::jsonContainsString(object, "id", "GoogleDriveUploadRequest") &&
00296             Networking::CurlJsonRequest::jsonContainsString(object, "name", "GoogleDriveUploadRequest") &&
00297             Networking::CurlJsonRequest::jsonContainsString(object, "mimeType", "GoogleDriveUploadRequest")) {
00298             //finished
00299             Common::String id = object.getVal("id")->asString();
00300             Common::String name = object.getVal("name")->asString();
00301             bool isDirectory = (object.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
00302             uint32 size = 0, timestamp = 0;
00303             if (Networking::CurlJsonRequest::jsonContainsString(object, "size", "GoogleDriveUploadRequest", true))
00304                 size = object.getVal("size")->asString().asUint64();
00305             if (Networking::CurlJsonRequest::jsonContainsString(object, "modifiedTime", "GoogleDriveUploadRequest", true))
00306                 timestamp = ISO8601::convertToTimestamp(object.getVal("modifiedTime")->asString());
00307 
00308             finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
00309             return;
00310         }
00311     }
00312 
00313     if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
00314         warning("GoogleDriveUploadRequest: no file info to return");
00315         finishUpload(StorageFile(_savePath, 0, 0, false));
00316     } else {
00317         uploadNextPart();
00318     }
00319 
00320     delete json;
00321 }
00322 
00323 void GoogleDriveUploadRequest::partUploadedErrorCallback(Networking::ErrorResponse error) {
00324     _workingRequest = nullptr;
00325     if (_ignoreCallback)
00326         return;
00327 
00328     Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)error.request;
00329     if (rq) {
00330         const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
00331         if (stream) {
00332             long code = stream->httpResponseCode();
00333             if (code == 308 && handleHttp308(stream)) {
00334                 return;
00335             }
00336         }
00337     }
00338 
00339     finishError(error);
00340 }
00341 
00342 void GoogleDriveUploadRequest::handle() {}
00343 
00344 void GoogleDriveUploadRequest::restart() { start(); }
00345 
00346 void GoogleDriveUploadRequest::finishUpload(StorageFile file) {
00347     Request::finishSuccess();
00348     if (_uploadCallback)
00349         (*_uploadCallback)(Storage::UploadResponse(this, file));
00350 }
00351 
00352 } // End of namespace GoogleDrive
00353 } // End of namespace Cloud


Generated on Sat Mar 16 2019 05:01:35 for ResidualVM by doxygen 1.7.1
curved edge   curved edge