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

boxstorage.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/cloud/box/boxstorage.h"
00027 #include "backends/cloud/box/boxlistdirectorybyidrequest.h"
00028 #include "backends/cloud/box/boxtokenrefresher.h"
00029 #include "backends/cloud/box/boxuploadrequest.h"
00030 #include "backends/cloud/cloudmanager.h"
00031 #include "backends/networking/curl/connectionmanager.h"
00032 #include "backends/networking/curl/curljsonrequest.h"
00033 #include "backends/networking/curl/networkreadstream.h"
00034 #include "common/config-manager.h"
00035 #include "common/debug.h"
00036 #include "common/json.h"
00037 
00038 #ifdef ENABLE_RELEASE
00039 #include "dists/clouds/cloud_keys.h"
00040 #endif
00041 
00042 namespace Cloud {
00043 namespace Box {
00044 
00045 #define BOX_OAUTH2_TOKEN "https://api.box.com/oauth2/token"
00046 #define BOX_API_FOLDERS "https://api.box.com/2.0/folders"
00047 #define BOX_API_FILES_CONTENT "https://api.box.com/2.0/files/%s/content"
00048 #define BOX_API_USERS_ME "https://api.box.com/2.0/users/me"
00049 
00050 char *BoxStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
00051 char *BoxStorage::SECRET = nullptr;
00052 
00053 void BoxStorage::loadKeyAndSecret() {
00054 #ifdef ENABLE_RELEASE
00055     KEY = RELEASE_BOX_KEY;
00056     SECRET = RELEASE_BOX_SECRET;
00057 #else
00058     Common::String k = ConfMan.get("BOX_KEY", ConfMan.kCloudDomain);
00059     KEY = new char[k.size() + 1];
00060     memcpy(KEY, k.c_str(), k.size());
00061     KEY[k.size()] = 0;
00062 
00063     k = ConfMan.get("BOX_SECRET", ConfMan.kCloudDomain);
00064     SECRET = new char[k.size() + 1];
00065     memcpy(SECRET, k.c_str(), k.size());
00066     SECRET[k.size()] = 0;
00067 #endif
00068 }
00069 
00070 BoxStorage::BoxStorage(Common::String token, Common::String refreshToken):
00071     _token(token), _refreshToken(refreshToken) {}
00072 
00073 BoxStorage::BoxStorage(Common::String code) {
00074     getAccessToken(
00075         new Common::Callback<BoxStorage, BoolResponse>(this, &BoxStorage::codeFlowComplete),
00076         new Common::Callback<BoxStorage, Networking::ErrorResponse>(this, &BoxStorage::codeFlowFailed),
00077         code
00078     );
00079 }
00080 
00081 BoxStorage::~BoxStorage() {}
00082 
00083 void BoxStorage::getAccessToken(BoolCallback callback, Networking::ErrorCallback errorCallback, Common::String code) {
00084     if (!KEY || !SECRET)
00085         loadKeyAndSecret();
00086     bool codeFlow = (code != "");
00087 
00088     if (!codeFlow && _refreshToken == "") {
00089         warning("BoxStorage: no refresh token available to get new access token.");
00090         if (callback) (*callback)(BoolResponse(nullptr, false));
00091         return;
00092     }
00093 
00094     Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::tokenRefreshed, callback);
00095     if (errorCallback == nullptr)
00096         errorCallback = getErrorPrintingCallback();
00097 
00098     Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, BOX_OAUTH2_TOKEN);
00099     if (codeFlow) {
00100         request->addPostField("grant_type=authorization_code");
00101         request->addPostField("code=" + code);
00102     } else {
00103         request->addPostField("grant_type=refresh_token");
00104         request->addPostField("refresh_token=" + _refreshToken);
00105     }
00106     request->addPostField("client_id=" + Common::String(KEY));
00107     request->addPostField("client_secret=" + Common::String(SECRET));
00108     /*
00109     if (Cloud::CloudManager::couldUseLocalServer()) {
00110         request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345");
00111     } else {
00112         request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
00113     }
00114     */
00115     addRequest(request);
00116 }
00117 
00118 void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
00119     Common::JSONValue *json = response.value;
00120     if (!json) {
00121         warning("BoxStorage: got NULL instead of JSON");
00122         if (callback)
00123             (*callback)(BoolResponse(nullptr, false));
00124         delete callback;
00125         return;
00126     }
00127 
00128     if (!Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage")) {
00129         if (callback)
00130             (*callback)(BoolResponse(nullptr, false));
00131         delete json;
00132         delete callback;
00133         return;
00134     }
00135 
00136     Common::JSONObject result = json->asObject();
00137     if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "BoxStorage") ||
00138         !Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "BoxStorage")) {
00139         warning("BoxStorage: bad response, no token passed");
00140         debug(9, "%s", json->stringify().c_str());
00141         if (callback)
00142             (*callback)(BoolResponse(nullptr, false));
00143     } else {
00144         _token = result.getVal("access_token")->asString();
00145         _refreshToken = result.getVal("refresh_token")->asString();
00146         CloudMan.save(); //ask CloudManager to save our new refreshToken
00147         if (callback)
00148             (*callback)(BoolResponse(nullptr, true));
00149     }
00150     delete json;
00151     delete callback;
00152 }
00153 
00154 void BoxStorage::codeFlowComplete(BoolResponse response) {
00155     if (!response.value) {
00156         warning("BoxStorage: failed to get access token through code flow");
00157         CloudMan.removeStorage(this);
00158         return;
00159     }
00160 
00161     CloudMan.replaceStorage(this, kStorageBoxId);
00162     ConfMan.flushToDisk();
00163 }
00164 
00165 void BoxStorage::codeFlowFailed(Networking::ErrorResponse error) {
00166     debug(9, "BoxStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
00167     debug(9, "%s", error.response.c_str());
00168     CloudMan.removeStorage(this);
00169 }
00170 
00171 void BoxStorage::saveConfig(Common::String keyPrefix) {
00172     ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
00173     ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
00174 }
00175 
00176 Common::String BoxStorage::name() const {
00177     return "Box";
00178 }
00179 
00180 void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
00181     Common::JSONValue *json = response.value;
00182     if (!json) {
00183         warning("BoxStorage::infoInnerCallback: NULL passed instead of JSON");
00184         delete outerCallback;
00185         return;
00186     }
00187 
00188     if (!Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::infoInnerCallback")) {
00189         delete json;
00190         delete outerCallback;
00191         return;
00192     }
00193 
00194     Common::JSONObject jsonInfo = json->asObject();
00195 
00196     Common::String uid, displayName, email;
00197     uint64 quotaUsed = 0, quotaAllocated = 0;
00198 
00199     // can check that "type": "user"
00200     // there is also "max_upload_size", "phone" and "avatar_url"
00201 
00202     if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "id", "BoxStorage::infoInnerCallback"))
00203         uid = jsonInfo.getVal("id")->asString();
00204 
00205     if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "name", "BoxStorage::infoInnerCallback"))
00206         displayName = jsonInfo.getVal("name")->asString();
00207 
00208     if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "login", "BoxStorage::infoInnerCallback"))
00209         email = jsonInfo.getVal("login")->asString();
00210 
00211     if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_amount", "BoxStorage::infoInnerCallback"))
00212         quotaAllocated = jsonInfo.getVal("space_amount")->asIntegerNumber();
00213 
00214     if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_used", "BoxStorage::infoInnerCallback"))
00215         quotaUsed = jsonInfo.getVal("space_used")->asIntegerNumber();
00216 
00217     Common::String username = email;
00218     if (username == "") username = displayName;
00219     if (username == "") username = uid;
00220     CloudMan.setStorageUsername(kStorageBoxId, username);
00221 
00222     if (outerCallback) {
00223         (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
00224         delete outerCallback;
00225     }
00226 
00227     delete json;
00228 }
00229 
00230 Networking::Request *BoxStorage::listDirectoryById(Common::String id, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback) {
00231     if (!errorCallback)
00232         errorCallback = getErrorPrintingCallback();
00233     if (!callback)
00234         callback = getPrintFilesCallback();
00235     return addRequest(new BoxListDirectoryByIdRequest(this, id, callback, errorCallback));
00236 }
00237 
00238 void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response) {
00239     Common::JSONValue *json = response.value;
00240     if (!json) {
00241         warning("BoxStorage::createDirectoryInnerCallback: NULL passed instead of JSON");
00242         delete outerCallback;
00243         return;
00244     }
00245 
00246     if (outerCallback) {
00247         if (Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) {
00248             Common::JSONObject jsonInfo = json->asObject();
00249             (*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id")));
00250         } else {
00251             (*outerCallback)(BoolResponse(nullptr, false));
00252         }
00253         delete outerCallback;
00254     }
00255 
00256     delete json;
00257 }
00258 
00259 Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String parentId, Common::String directoryName, BoolCallback callback, Networking::ErrorCallback errorCallback) {
00260     if (!errorCallback)
00261         errorCallback = getErrorPrintingCallback();
00262 
00263     Common::String url = BOX_API_FOLDERS;
00264     Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::createDirectoryInnerCallback, callback);
00265     Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, url.c_str());
00266     request->addHeader("Authorization: Bearer " + accessToken());
00267     request->addHeader("Content-Type: application/json");
00268 
00269     Common::JSONObject parentObject;
00270     parentObject.setVal("id", new Common::JSONValue(parentId));
00271 
00272     Common::JSONObject jsonRequestParameters;
00273     jsonRequestParameters.setVal("name", new Common::JSONValue(directoryName));
00274     jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject));
00275 
00276     Common::JSONValue value(jsonRequestParameters);
00277     request->addPostField(Common::JSON::stringify(&value));
00278 
00279     return addRequest(request);
00280 }
00281 
00282 Networking::Request *BoxStorage::upload(Common::String remotePath, Common::String localPath, UploadCallback callback, Networking::ErrorCallback errorCallback) {
00283     if (!errorCallback)
00284         errorCallback = getErrorPrintingCallback();
00285     return addRequest(new BoxUploadRequest(this, remotePath, localPath, callback, errorCallback));
00286 }
00287 
00288 Networking::Request *BoxStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
00289     warning("BoxStorage::upload(ReadStream) not implemented");
00290     if (errorCallback)
00291         (*errorCallback)(Networking::ErrorResponse(nullptr, false, true, "BoxStorage::upload(ReadStream) not implemented", -1));
00292     delete callback;
00293     delete errorCallback;
00294     return nullptr;
00295 }
00296 
00297 bool BoxStorage::uploadStreamSupported() {
00298     return false;
00299 }
00300 
00301 Networking::Request *BoxStorage::streamFileById(Common::String id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
00302     if (callback) {
00303         Common::String url = Common::String::format(BOX_API_FILES_CONTENT, id.c_str());
00304         Common::String header = "Authorization: Bearer " + _token;
00305         curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
00306         Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
00307         (*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));
00308     }
00309     delete callback;
00310     delete errorCallback;
00311     return nullptr;
00312 }
00313 
00314 Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
00315     Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, StorageInfoResponse, Networking::JsonResponse>(this, &BoxStorage::infoInnerCallback, callback);
00316     Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, BOX_API_USERS_ME);
00317     request->addHeader("Authorization: Bearer " + _token);
00318     return addRequest(request);
00319 }
00320 
00321 Common::String BoxStorage::savesDirectoryPath() { return "scummvm/saves/"; }
00322 
00323 BoxStorage *BoxStorage::loadFromConfig(Common::String keyPrefix) {
00324     loadKeyAndSecret();
00325 
00326     if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
00327         warning("BoxStorage: no access_token found");
00328         return nullptr;
00329     }
00330 
00331     if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
00332         warning("BoxStorage: no refresh_token found");
00333         return nullptr;
00334     }
00335 
00336     Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
00337     Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
00338     return new BoxStorage(accessToken, refreshToken);
00339 }
00340 
00341 Common::String BoxStorage::getRootDirectoryId() {
00342     return "0";
00343 }
00344 
00345 } // End of namespace Box
00346 } // End of namespace Cloud


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