2016-05-11 13:45:10 +06:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers , whose names
* are too numerous to list here . Please refer to the COPYRIGHT
* file distributed with this source distribution .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
*/
2016-05-15 00:31:02 +06:00
# define FORBIDDEN_SYMBOL_ALLOW_ALL
2016-05-11 13:45:10 +06:00
2016-05-11 20:24:53 +06:00
# include "backends/cloud/dropbox/dropboxstorage.h"
2016-05-24 00:14:24 +06:00
# include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
2016-05-30 02:23:29 +06:00
# include "backends/cloud/dropbox/dropboxuploadrequest.h"
2016-05-24 11:57:49 +06:00
# include "backends/cloud/downloadrequest.h"
2016-05-27 20:59:40 +06:00
# include "backends/cloud/folderdownloadrequest.h"
2016-05-18 15:21:09 +06:00
# include "backends/networking/curl/connectionmanager.h"
2016-05-17 01:19:49 +06:00
# include "backends/networking/curl/curljsonrequest.h"
2016-05-18 14:08:05 +06:00
# include "common/config-manager.h"
2016-05-11 13:45:10 +06:00
# include "common/debug.h"
2016-05-30 02:23:29 +06:00
# include "common/file.h"
2016-05-17 01:19:49 +06:00
# include "common/json.h"
2016-05-15 00:31:02 +06:00
# include <curl/curl.h>
2016-05-11 01:10:37 +06:00
2016-05-12 18:52:57 +06:00
namespace Cloud {
namespace Dropbox {
2016-05-11 01:10:37 +06:00
2016-05-27 22:33:39 +06:00
char * DropboxStorage : : KEY ; //can't use ConfMan there yet, loading it on instance creation/auth
char * DropboxStorage : : SECRET ; //TODO: hide these secrets somehow
void DropboxStorage : : loadKeyAndSecret ( ) {
Common : : String k = ConfMan . get ( " DROPBOX_KEY " , " cloud " ) ;
KEY = new char [ k . size ( ) + 1 ] ;
memcpy ( KEY , k . c_str ( ) , k . size ( ) ) ;
KEY [ k . size ( ) ] = 0 ;
k = ConfMan . get ( " DROPBOX_SECRET " , " cloud " ) ;
SECRET = new char [ k . size ( ) + 1 ] ;
memcpy ( SECRET , k . c_str ( ) , k . size ( ) ) ;
SECRET [ k . size ( ) ] = 0 ;
}
2016-05-18 14:08:05 +06:00
2016-05-27 15:21:06 +06:00
static void saveAccessTokenCallback ( Networking : : JsonResponse pair ) {
2016-05-26 19:02:55 +06:00
Common : : JSONValue * json = ( Common : : JSONValue * ) pair . value ;
2016-05-17 01:19:49 +06:00
if ( json ) {
2016-05-18 14:08:05 +06:00
debug ( " saveAccessTokenCallback: " ) ;
2016-05-17 01:19:49 +06:00
debug ( " %s " , json - > stringify ( true ) . c_str ( ) ) ;
2016-05-18 14:08:05 +06:00
Common : : JSONObject result = json - > asObject ( ) ;
if ( ! result . contains ( " access_token " ) | | ! result . contains ( " uid " ) ) {
warning ( " Bad response, no token/uid passed " ) ;
} else {
2016-05-23 11:23:33 +06:00
//we suppose that's the first storage
ConfMan . set ( " storages_number " , " 1 " , " cloud " ) ;
ConfMan . set ( " current_storage " , " 1 " , " cloud " ) ;
ConfMan . set ( " storage1_type " , " Dropbox " , " cloud " ) ;
ConfMan . set ( " storage1_access_token " , result . getVal ( " access_token " ) - > asString ( ) , " cloud " ) ;
ConfMan . set ( " storage1_user_id " , result . getVal ( " uid " ) - > asString ( ) , " cloud " ) ;
2016-05-18 14:08:05 +06:00
ConfMan . removeKey ( " dropbox_code " , " cloud " ) ;
2016-05-27 15:21:06 +06:00
ConfMan . flushToDisk ( ) ;
2016-05-18 14:08:05 +06:00
debug ( " Now please restart ScummVM to apply the changes. " ) ;
}
2016-05-17 01:19:49 +06:00
delete json ;
} else {
2016-05-18 14:08:05 +06:00
debug ( " saveAccessTokenCallback: got NULL instead of JSON! " ) ;
2016-05-17 01:19:49 +06:00
}
2016-05-15 00:31:02 +06:00
}
2016-05-18 14:08:05 +06:00
DropboxStorage : : DropboxStorage ( Common : : String accessToken , Common : : String userId ) : _token ( accessToken ) , _uid ( userId ) {
2016-05-15 00:31:02 +06:00
curl_global_init ( CURL_GLOBAL_ALL ) ;
}
DropboxStorage : : ~ DropboxStorage ( ) {
curl_global_cleanup ( ) ;
2016-05-11 01:10:37 +06:00
}
2016-05-23 11:23:33 +06:00
void DropboxStorage : : saveConfig ( Common : : String keyPrefix ) {
ConfMan . set ( keyPrefix + " type " , " Dropbox " , " cloud " ) ;
ConfMan . set ( keyPrefix + " access_token " , _token , " cloud " ) ;
ConfMan . set ( keyPrefix + " user_id " , _uid , " cloud " ) ;
}
2016-05-27 20:59:40 +06:00
void DropboxStorage : : printFiles ( FileArrayResponse pair ) {
2016-05-24 00:14:24 +06:00
debug ( " files: " ) ;
2016-05-27 20:59:40 +06:00
Common : : Array < StorageFile > & files = pair . value ;
2016-05-24 00:14:24 +06:00
for ( uint32 i = 0 ; i < files . size ( ) ; + + i )
debug ( " \t %s " , files [ i ] . name ( ) . c_str ( ) ) ;
2016-05-23 12:21:45 +06:00
}
2016-05-30 02:23:29 +06:00
void DropboxStorage : : printBool ( BoolResponse pair ) {
debug ( " bool: %s " , ( pair . value ? " true " : " false " ) ) ;
}
2016-05-30 13:35:53 +06:00
void DropboxStorage : : printUploadStatus ( UploadResponse pair ) {
2016-05-30 13:46:14 +06:00
debug ( " " ) ;
2016-05-30 13:35:53 +06:00
UploadStatus status = pair . value ;
if ( status . interrupted ) {
debug ( " upload interrupted by user " ) ;
return ;
}
if ( status . failed ) {
debug ( " upload failed with following response: " ) ;
debug ( " %s " , status . response . c_str ( ) ) ;
return ;
}
debug ( " upload HTTP response code = %ld " , status . httpResponseCode ) ;
if ( ! status . failed ) {
debug ( " uploaded file info: " ) ;
2016-05-30 13:46:14 +06:00
debug ( " \t path: %s " , status . file . path ( ) . c_str ( ) ) ;
debug ( " \t size: %u " , status . file . size ( ) ) ;
debug ( " \t timestamp: %u " , status . file . timestamp ( ) ) ;
2016-05-30 13:35:53 +06:00
}
}
2016-05-30 18:13:31 +06:00
Networking : : Request * DropboxStorage : : listDirectory ( Common : : String path , ListDirectoryCallback outerCallback , bool recursive ) {
2016-05-26 17:12:40 +06:00
return ConnMan . addRequest ( new DropboxListDirectoryRequest ( _token , path , outerCallback , recursive ) ) ;
2016-05-23 12:21:45 +06:00
}
2016-05-30 13:35:53 +06:00
Networking : : Request * DropboxStorage : : upload ( Common : : String path , Common : : SeekableReadStream * contents , UploadCallback callback ) {
2016-05-30 02:23:29 +06:00
return ConnMan . addRequest ( new DropboxUploadRequest ( _token , path , contents , callback ) ) ;
}
2016-05-30 13:35:53 +06:00
Networking : : Request * DropboxStorage : : upload ( Common : : String remotePath , Common : : String localPath , UploadCallback callback ) {
Common : : File * f = new Common : : File ( ) ;
if ( ! f - > open ( localPath ) ) {
warning ( " DropboxStorage: unable to open file to upload from " ) ;
UploadStatus status ( false , true , StorageFile ( ) , " " , - 1 ) ;
if ( callback ) ( * callback ) ( UploadResponse ( nullptr , status ) ) ;
delete f ;
return nullptr ;
}
return upload ( remotePath , f , callback ) ;
}
2016-05-27 15:21:06 +06:00
Networking : : Request * DropboxStorage : : streamFile ( Common : : String path , Networking : : NetworkReadStreamCallback callback ) {
2016-05-24 11:57:49 +06:00
Common : : JSONObject jsonRequestParameters ;
jsonRequestParameters . setVal ( " path " , new Common : : JSONValue ( path ) ) ;
Common : : JSONValue value ( jsonRequestParameters ) ;
Networking : : CurlRequest * request = new Networking : : CurlRequest ( 0 , " https://content.dropboxapi.com/2/files/download " ) ;
request - > addHeader ( " Authorization: Bearer " + _token ) ;
request - > addHeader ( " Dropbox-API-Arg: " + Common : : JSON : : stringify ( & value ) ) ;
request - > addHeader ( " Content-Type: " ) ; //required to be empty (as we do POST, it's usually app/form-url-encoded)
2016-05-27 15:21:06 +06:00
Networking : : NetworkReadStreamResponse pair = request - > execute ( ) ;
2016-05-27 01:09:10 +06:00
if ( callback ) ( * callback ) ( pair ) ;
2016-05-27 15:21:06 +06:00
return pair . request ;
2016-05-24 11:57:49 +06:00
}
2016-05-27 15:21:06 +06:00
Networking : : Request * DropboxStorage : : download ( Common : : String remotePath , Common : : String localPath , BoolCallback callback ) {
2016-05-24 12:31:27 +06:00
Common : : DumpFile * f = new Common : : DumpFile ( ) ;
2016-05-25 16:32:22 +06:00
if ( ! f - > open ( localPath , true ) ) {
2016-05-24 12:31:27 +06:00
warning ( " DropboxStorage: unable to open file to download into " ) ;
2016-05-27 15:21:06 +06:00
if ( callback ) ( * callback ) ( BoolResponse ( nullptr , false ) ) ;
2016-05-24 12:31:27 +06:00
delete f ;
2016-05-27 15:21:06 +06:00
return nullptr ;
2016-05-24 12:31:27 +06:00
}
2016-05-27 01:09:10 +06:00
return ConnMan . addRequest ( new DownloadRequest ( this , callback , remotePath , f ) ) ;
2016-05-24 11:57:49 +06:00
}
2016-05-27 20:59:40 +06:00
Networking : : Request * DropboxStorage : : downloadFolder ( Common : : String remotePath , Common : : String localPath , FileArrayCallback callback , bool recursive ) {
return ConnMan . addRequest ( new FolderDownloadRequest ( this , callback , remotePath , localPath , recursive ) ) ;
}
2016-05-27 15:21:06 +06:00
Networking : : Request * DropboxStorage : : syncSaves ( BoolCallback callback ) {
2016-05-23 12:21:45 +06:00
//this is not the real syncSaves() implementation
2016-05-24 00:14:24 +06:00
//"" is root in Dropbox, not "/"
2016-05-25 16:32:22 +06:00
//this must create all these directories:
2016-05-27 20:59:40 +06:00
//return download("/remote/test.jpg", "local/a/b/c/d/test.jpg", 0);
2016-05-30 02:23:29 +06:00
/*
2016-05-27 20:59:40 +06:00
return downloadFolder (
" /not_flat " , " local/not_flat_1_level/ " ,
new Common : : Callback < DropboxStorage , FileArrayResponse > ( this , & DropboxStorage : : printFiles ) ,
false
) ;
2016-05-30 02:23:29 +06:00
*/
2016-05-30 13:35:53 +06:00
return upload ( " /remote/test4.bmp " , " final.bmp " , new Common : : Callback < DropboxStorage , UploadResponse > ( this , & DropboxStorage : : printUploadStatus ) ) ;
2016-05-21 14:06:50 +06:00
}
2016-05-27 15:21:06 +06:00
Networking : : Request * DropboxStorage : : info ( StorageInfoCallback outerCallback ) {
Networking : : JsonCallback innerCallback = new Common : : CallbackBridge < DropboxStorage , StorageInfoResponse , Networking : : JsonResponse > ( this , & DropboxStorage : : infoInnerCallback , outerCallback ) ;
2016-05-21 23:21:42 +06:00
Networking : : CurlJsonRequest * request = new Networking : : CurlJsonRequest ( innerCallback , " https://api.dropboxapi.com/1/account/info " ) ;
2016-05-18 14:08:05 +06:00
request - > addHeader ( " Authorization: Bearer " + _token ) ;
2016-05-26 17:12:40 +06:00
return ConnMan . addRequest ( request ) ;
2016-05-21 23:21:42 +06:00
//that callback bridge wraps the outerCallback (passed in arguments from user) into innerCallback
//so, when CurlJsonRequest is finished, it calls the innerCallback
//innerCallback (which is DropboxStorage::infoInnerCallback in this case) processes the void *ptr
//and then calls the outerCallback (which wants to receive StorageInfo, not void *)
2016-05-18 14:08:05 +06:00
}
2016-05-27 15:21:06 +06:00
void DropboxStorage : : infoInnerCallback ( StorageInfoCallback outerCallback , Networking : : JsonResponse pair ) {
2016-05-26 19:22:27 +06:00
Common : : JSONValue * json = pair . value ;
2016-05-21 23:21:42 +06:00
if ( ! json ) {
warning ( " NULL passed instead of JSON " ) ;
delete outerCallback ;
return ;
}
2016-05-23 12:00:12 +06:00
if ( outerCallback ) {
//Dropbox documentation states there is no errors for this API method
Common : : JSONObject info = json - > asObject ( ) ;
2016-05-30 13:35:53 +06:00
Common : : String uid = Common : : String : : format ( " %d " , ( int ) info . getVal ( " uid " ) - > asIntegerNumber ( ) ) ;
2016-05-23 12:00:12 +06:00
Common : : String name = info . getVal ( " display_name " ) - > asString ( ) ;
Common : : String email = info . getVal ( " email " ) - > asString ( ) ;
Common : : JSONObject quota = info . getVal ( " quota_info " ) - > asObject ( ) ;
2016-05-30 13:35:53 +06:00
uint32 quotaNormal = quota . getVal ( " normal " ) - > asIntegerNumber ( ) ;
uint32 quotaShared = quota . getVal ( " shared " ) - > asIntegerNumber ( ) ;
uint32 quotaAllocated = quota . getVal ( " quota " ) - > asIntegerNumber ( ) ;
2016-05-27 15:21:06 +06:00
( * outerCallback ) ( StorageInfoResponse ( nullptr , StorageInfo ( uid , name , email , quotaNormal + quotaShared , quotaAllocated ) ) ) ;
2016-05-21 23:21:42 +06:00
delete outerCallback ;
}
delete json ;
2016-05-21 21:30:25 +06:00
}
2016-05-27 15:21:06 +06:00
void DropboxStorage : : infoMethodCallback ( StorageInfoResponse pair ) {
2016-05-23 12:00:12 +06:00
debug ( " \n Storage info: " ) ;
2016-05-26 19:02:55 +06:00
debug ( " User name: %s " , pair . value . name ( ) . c_str ( ) ) ;
debug ( " Email: %s " , pair . value . email ( ) . c_str ( ) ) ;
debug ( " Disk usage: %u/%u " , pair . value . used ( ) , pair . value . available ( ) ) ;
2016-05-21 14:06:50 +06:00
}
2016-05-23 11:23:33 +06:00
DropboxStorage * DropboxStorage : : loadFromConfig ( Common : : String keyPrefix ) {
2016-05-27 22:33:39 +06:00
loadKeyAndSecret ( ) ;
2016-05-18 14:08:05 +06:00
2016-05-23 11:23:33 +06:00
if ( ! ConfMan . hasKey ( keyPrefix + " access_token " , " cloud " ) ) {
2016-05-18 14:08:05 +06:00
warning ( " No access_token found " ) ;
return 0 ;
}
2016-05-23 11:23:33 +06:00
if ( ! ConfMan . hasKey ( keyPrefix + " user_id " , " cloud " ) ) {
2016-05-18 14:08:05 +06:00
warning ( " No user_id found " ) ;
return 0 ;
}
2016-05-23 11:23:33 +06:00
Common : : String accessToken = ConfMan . get ( keyPrefix + " access_token " , " cloud " ) ;
Common : : String userId = ConfMan . get ( keyPrefix + " user_id " , " cloud " ) ;
2016-05-18 14:08:05 +06:00
return new DropboxStorage ( accessToken , userId ) ;
}
Common : : String DropboxStorage : : getAuthLink ( ) {
Common : : String url = " https://www.dropbox.com/1/oauth2/authorize " ;
url + = " ?response_type=code " ;
url + = " &redirect_uri=http://localhost:12345/ " ; //that's for copy-pasting
//url += "&redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F"; //that's "http://localhost:12345/" for automatic opening
2016-05-27 22:33:39 +06:00
url + = " &client_id= " ; url + = KEY ;
2016-05-18 14:08:05 +06:00
return url ;
}
2016-05-18 15:21:09 +06:00
void DropboxStorage : : authThroughConsole ( ) {
2016-05-18 14:08:05 +06:00
if ( ! ConfMan . hasKey ( " DROPBOX_KEY " , " cloud " ) | | ! ConfMan . hasKey ( " DROPBOX_SECRET " , " cloud " ) ) {
warning ( " No Dropbox keys available, cannot do auth " ) ;
2016-05-18 15:21:09 +06:00
return ;
2016-05-18 14:08:05 +06:00
}
2016-05-27 22:33:39 +06:00
loadKeyAndSecret ( ) ;
2016-05-18 14:08:05 +06:00
if ( ConfMan . hasKey ( " dropbox_code " , " cloud " ) ) {
//phase 2: get access_token using specified code
2016-05-18 15:21:09 +06:00
getAccessToken ( ConfMan . get ( " dropbox_code " , " cloud " ) ) ;
return ;
2016-05-18 14:08:05 +06:00
}
debug ( " Navigate to this URL and press \" Allow \" : " ) ;
debug ( " %s \n " , getAuthLink ( ) . c_str ( ) ) ;
debug ( " Then, add dropbox_code key in [cloud] section of configuration file. You should copy the <code> value from URL and put it as value for that key. \n " ) ;
debug ( " Navigate to this URL to get more information on ScummVM's configuration files: " ) ;
2016-05-18 15:21:09 +06:00
debug ( " http://wiki.scummvm.org/index.php/User_Manual/Configuring_ScummVM#Using_the_configuration_file_to_configure_ScummVM \n " ) ;
2016-05-18 14:08:05 +06:00
}
2016-05-18 15:21:09 +06:00
void DropboxStorage : : getAccessToken ( Common : : String code ) {
2016-05-27 15:21:06 +06:00
Networking : : JsonCallback callback = new Common : : GlobalFunctionCallback < Networking : : JsonResponse > ( saveAccessTokenCallback ) ;
2016-05-21 21:30:25 +06:00
Networking : : CurlJsonRequest * request = new Networking : : CurlJsonRequest ( callback , " https://api.dropboxapi.com/1/oauth2/token " ) ;
2016-05-18 14:08:05 +06:00
request - > addPostField ( " code= " + code ) ;
request - > addPostField ( " grant_type=authorization_code " ) ;
2016-05-27 22:33:39 +06:00
request - > addPostField ( " client_id= " + Common : : String ( KEY ) ) ;
request - > addPostField ( " client_secret= " + Common : : String ( SECRET ) ) ;
2016-05-18 15:21:09 +06:00
request - > addPostField ( " &redirect_uri=http%3A%2F%2Flocalhost%3A12345%2F " ) ;
ConnMan . addRequest ( request ) ;
2016-05-11 15:15:23 +06:00
}
2016-05-28 20:10:38 +02:00
} // End of namespace Dropbox
} // End of namespace Cloud