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

onedrivestorage.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/onedrive/onedrivestorage.h"
00027 #include "backends/cloud/cloudmanager.h"
00028 #include "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
00029 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
00030 #include "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
00031 #include "backends/cloud/onedrive/onedriveuploadrequest.h"
00032 #include "backends/networking/curl/connectionmanager.h"
00033 #include "backends/networking/curl/curljsonrequest.h"
00034 #include "backends/networking/curl/networkreadstream.h"
00035 #include "common/config-manager.h"
00036 #include "common/debug.h"
00037 #include "common/json.h"
00038 
00039 #ifdef ENABLE_RELEASE
00040 #include "dists/clouds/cloud_keys.h"
00041 #endif
00042 
00043 namespace Cloud {
00044 namespace OneDrive {
00045 
00046 #define ONEDRIVE_OAUTH2_TOKEN "https://login.live.com/oauth20_token.srf"
00047 #define ONEDRIVE_API_SPECIAL_APPROOT_ID "https://api.onedrive.com/v1.0/drive/special/approot:/"
00048 #define ONEDRIVE_API_SPECIAL_APPROOT "https://api.onedrive.com/v1.0/drive/special/approot"
00049 
00050 char *OneDriveStorage::KEY = nullptr; //can't use CloudConfig there yet, loading it on instance creation/auth
00051 char *OneDriveStorage::SECRET = nullptr;
00052 
00053 void OneDriveStorage::loadKeyAndSecret() {
00054 #ifdef ENABLE_RELEASE
00055     KEY = RELEASE_ONEDRIVE_KEY;
00056     SECRET = RELEASE_ONEDRIVE_SECRET;
00057 #else
00058     Common::String k = ConfMan.get("ONEDRIVE_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("ONEDRIVE_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 OneDriveStorage::OneDriveStorage(Common::String token, Common::String uid, Common::String refreshToken):
00071     _token(token), _uid(uid), _refreshToken(refreshToken) {}
00072 
00073 OneDriveStorage::OneDriveStorage(Common::String code) {
00074     getAccessToken(
00075         new Common::Callback<OneDriveStorage, BoolResponse>(this, &OneDriveStorage::codeFlowComplete),
00076         new Common::Callback<OneDriveStorage, Networking::ErrorResponse>(this, &OneDriveStorage::codeFlowFailed),
00077         code
00078     );
00079 }
00080 
00081 OneDriveStorage::~OneDriveStorage() {}
00082 
00083 void OneDriveStorage::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("OneDriveStorage: no refresh token available to get new access token.");
00090         if (callback)
00091             (*callback)(BoolResponse(nullptr, false));
00092         return;
00093     }
00094 
00095     Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, BoolResponse, Networking::JsonResponse>(this, &OneDriveStorage::tokenRefreshed, callback);
00096     if (errorCallback == nullptr)
00097         errorCallback = getErrorPrintingCallback();
00098     Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, ONEDRIVE_OAUTH2_TOKEN);
00099     if (codeFlow) {
00100         request->addPostField("code=" + code);
00101         request->addPostField("grant_type=authorization_code");
00102     } else {
00103         request->addPostField("refresh_token=" + _refreshToken);
00104         request->addPostField("grant_type=refresh_token");
00105     }
00106     request->addPostField("client_id=" + Common::String(KEY));
00107     request->addPostField("client_secret=" + Common::String(SECRET));
00108     if (Cloud::CloudManager::couldUseLocalServer()) {
00109         request->addPostField("&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F");
00110     } else {
00111         request->addPostField("&redirect_uri=https%3A%2F%2Fwww.scummvm.org/c/code");
00112     }
00113     addRequest(request);
00114 }
00115 
00116 void OneDriveStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
00117     Common::JSONValue *json = response.value;
00118     if (!json) {
00119         warning("OneDriveStorage: got NULL instead of JSON");
00120         if (callback)
00121             (*callback)(BoolResponse(nullptr, false));
00122         delete callback;
00123         return;
00124     }
00125 
00126     if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage")) {
00127         if (callback)
00128             (*callback)(BoolResponse(nullptr, false));
00129         delete json;
00130         delete callback;
00131         return;
00132     }
00133 
00134     Common::JSONObject result = json->asObject();
00135     if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "OneDriveStorage") ||
00136         !Networking::CurlJsonRequest::jsonContainsString(result, "user_id", "OneDriveStorage") ||
00137         !Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "OneDriveStorage")) {
00138         warning("OneDriveStorage: bad response, no token or user_id passed");
00139         debug(9, "%s", json->stringify().c_str());
00140         if (callback)
00141             (*callback)(BoolResponse(nullptr, false));
00142     } else {
00143         _token = result.getVal("access_token")->asString();
00144         _uid = result.getVal("user_id")->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 OneDriveStorage::codeFlowComplete(BoolResponse response) {
00155     if (!response.value) {
00156         warning("OneDriveStorage: failed to get access token through code flow");
00157         CloudMan.removeStorage(this);
00158         return;
00159     }
00160 
00161     ConfMan.removeKey("onedrive_code", ConfMan.kCloudDomain);
00162     CloudMan.replaceStorage(this, kStorageOneDriveId);
00163     ConfMan.flushToDisk();
00164 }
00165 
00166 void OneDriveStorage::codeFlowFailed(Networking::ErrorResponse error) {
00167     debug(9, "OneDriveStorage: code flow failed (%s, %ld):", (error.failed ? "failed" : "interrupted"), error.httpResponseCode);
00168     debug(9, "%s", error.response.c_str());
00169     CloudMan.removeStorage(this);
00170 }
00171 
00172 void OneDriveStorage::saveConfig(Common::String keyPrefix) {
00173     ConfMan.set(keyPrefix + "access_token", _token, ConfMan.kCloudDomain);
00174     ConfMan.set(keyPrefix + "user_id", _uid, ConfMan.kCloudDomain);
00175     ConfMan.set(keyPrefix + "refresh_token", _refreshToken, ConfMan.kCloudDomain);
00176 }
00177 
00178 Common::String OneDriveStorage::name() const {
00179     return "OneDrive";
00180 }
00181 
00182 void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
00183     Common::JSONValue *json = response.value;
00184     if (!json) {
00185         warning("OneDriveStorage::infoInnerCallback: NULL passed instead of JSON");
00186         delete outerCallback;
00187         return;
00188     }
00189 
00190     if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage::infoInnerCallback")) {
00191         delete json;
00192         delete outerCallback;
00193         return;
00194     }
00195 
00196     Common::JSONObject jsonInfo = json->asObject();
00197 
00198     Common::String uid, displayName, email;
00199     uint64 quotaUsed = 0, quotaAllocated = 26843545600LL; // 25 GB, because I actually don't know any way to find out the real one
00200 
00201     if (Networking::CurlJsonRequest::jsonContainsObject(jsonInfo, "createdBy", "OneDriveStorage::infoInnerCallback")) {
00202         Common::JSONObject createdBy = jsonInfo.getVal("createdBy")->asObject();
00203         if (Networking::CurlJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) {
00204             Common::JSONObject user = createdBy.getVal("user")->asObject();
00205             if (Networking::CurlJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback"))
00206                 uid = user.getVal("id")->asString();
00207             if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback"))
00208                 displayName = user.getVal("displayName")->asString();
00209         }
00210     }
00211 
00212     if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "size", "OneDriveStorage::infoInnerCallback")) {
00213         quotaUsed = jsonInfo.getVal("size")->asIntegerNumber();
00214     }
00215 
00216     Common::String username = email;
00217     if (username == "")
00218         username = displayName;
00219     if (username == "")
00220         username = uid;
00221     CloudMan.setStorageUsername(kStorageOneDriveId, username);
00222 
00223     if (outerCallback) {
00224         (*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
00225         delete outerCallback;
00226     }
00227 
00228     delete json;
00229 }
00230 
00231 void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
00232     Common::JSONValue *json = response.value;
00233     if (!json) {
00234         warning("OneDriveStorage::fileInfoCallback: NULL passed instead of JSON");
00235         if (outerCallback)
00236             (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
00237         delete outerCallback;
00238         return;
00239     }
00240 
00241     if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage::fileInfoCallback")) {
00242         if (outerCallback)
00243             (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
00244         delete json;
00245         delete outerCallback;
00246         return;
00247     }
00248 
00249     Common::JSONObject result = response.value->asObject();
00250     if (!Networking::CurlJsonRequest::jsonContainsString(result, "@content.downloadUrl", "OneDriveStorage::fileInfoCallback")) {
00251         warning("OneDriveStorage: downloadUrl not found in passed JSON");
00252         debug(9, "%s", response.value->stringify().c_str());
00253         if (outerCallback)
00254             (*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
00255         delete json;
00256         delete outerCallback;
00257         return;
00258     }
00259 
00260     const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
00261     if (outerCallback)
00262         (*outerCallback)(Networking::NetworkReadStreamResponse(
00263             response.request,
00264             new Networking::NetworkReadStream(url, nullptr, "")
00265         ));
00266 
00267     delete json;
00268     delete outerCallback;
00269 }
00270 
00271 Networking::Request *OneDriveStorage::listDirectory(Common::String path, ListDirectoryCallback callback, Networking::ErrorCallback errorCallback, bool recursive) {
00272     return addRequest(new OneDriveListDirectoryRequest(this, path, callback, errorCallback, recursive));
00273 }
00274 
00275 Networking::Request *OneDriveStorage::upload(Common::String path, Common::SeekableReadStream *contents, UploadCallback callback, Networking::ErrorCallback errorCallback) {
00276     return addRequest(new OneDriveUploadRequest(this, path, contents, callback, errorCallback));
00277 }
00278 
00279 Networking::Request *OneDriveStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
00280     Common::String url = ONEDRIVE_API_SPECIAL_APPROOT_ID + ConnMan.urlEncode(path);
00281     Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, Networking::NetworkReadStreamResponse, Networking::JsonResponse>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
00282     Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
00283     request->addHeader("Authorization: Bearer " + _token);
00284     return addRequest(request);
00285 }
00286 
00287 Networking::Request *OneDriveStorage::createDirectory(Common::String path, BoolCallback callback, Networking::ErrorCallback errorCallback) {
00288     if (!errorCallback)
00289         errorCallback = getErrorPrintingCallback();
00290     return addRequest(new OneDriveCreateDirectoryRequest(this, path, callback, errorCallback));
00291 }
00292 
00293 Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
00294     Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, StorageInfoResponse, Networking::JsonResponse>(this, &OneDriveStorage::infoInnerCallback, callback);
00295     Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, ONEDRIVE_API_SPECIAL_APPROOT);
00296     request->addHeader("Authorization: bearer " + _token);
00297     return addRequest(request);
00298 }
00299 
00300 Common::String OneDriveStorage::savesDirectoryPath() { return "saves/"; }
00301 
00302 OneDriveStorage *OneDriveStorage::loadFromConfig(Common::String keyPrefix) {
00303     loadKeyAndSecret();
00304 
00305     if (!ConfMan.hasKey(keyPrefix + "access_token", ConfMan.kCloudDomain)) {
00306         warning("OneDriveStorage: no access_token found");
00307         return nullptr;
00308     }
00309 
00310     if (!ConfMan.hasKey(keyPrefix + "user_id", ConfMan.kCloudDomain)) {
00311         warning("OneDriveStorage: no user_id found");
00312         return nullptr;
00313     }
00314 
00315     if (!ConfMan.hasKey(keyPrefix + "refresh_token", ConfMan.kCloudDomain)) {
00316         warning("OneDriveStorage: no refresh_token found");
00317         return nullptr;
00318     }
00319 
00320     Common::String accessToken = ConfMan.get(keyPrefix + "access_token", ConfMan.kCloudDomain);
00321     Common::String userId = ConfMan.get(keyPrefix + "user_id", ConfMan.kCloudDomain);
00322     Common::String refreshToken = ConfMan.get(keyPrefix + "refresh_token", ConfMan.kCloudDomain);
00323     return new OneDriveStorage(accessToken, userId, refreshToken);
00324 }
00325 
00326 } // End of namespace OneDrive
00327 } // End of namespace Cloud


Generated on Sat May 18 2019 05:01:11 for ResidualVM by doxygen 1.7.1
curved edge   curved edge