mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-22 23:49:50 +00:00
Cloud Sync (#15548)
* cloud sync - algorithm and abstract cloud storage API * WebDAV cloud sync driver, and associated net_http improvements * Cloud sync settings menu
This commit is contained in:
parent
45af11efe9
commit
893b0d142e
@ -1490,6 +1490,7 @@ static struct config_array_setting *populate_settings_array(settings_t *settings
|
||||
SETTING_ARRAY("bluetooth_driver", settings->arrays.bluetooth_driver, false, NULL, true);
|
||||
SETTING_ARRAY("wifi_driver", settings->arrays.wifi_driver, false, NULL, true);
|
||||
SETTING_ARRAY("location_driver", settings->arrays.location_driver,false, NULL, true);
|
||||
SETTING_ARRAY("cloud_sync_driver", settings->arrays.cloud_sync_driver, false, NULL, true);
|
||||
#ifdef HAVE_MENU
|
||||
SETTING_ARRAY("menu_driver", settings->arrays.menu_driver, false, NULL, true);
|
||||
#endif
|
||||
@ -1516,6 +1517,9 @@ static struct config_array_setting *populate_settings_array(settings_t *settings
|
||||
SETTING_ARRAY("midi_driver", settings->arrays.midi_driver, false, NULL, true);
|
||||
SETTING_ARRAY("midi_input", settings->arrays.midi_input, true, DEFAULT_MIDI_INPUT, true);
|
||||
SETTING_ARRAY("midi_output", settings->arrays.midi_output, true, DEFAULT_MIDI_OUTPUT, true);
|
||||
SETTING_ARRAY("webdav_url", settings->arrays.webdav_url, false, NULL, true);
|
||||
SETTING_ARRAY("webdav_username", settings->arrays.webdav_username, false, NULL, true);
|
||||
SETTING_ARRAY("webdav_password", settings->arrays.webdav_password, false, NULL, true);
|
||||
SETTING_ARRAY("youtube_stream_key", settings->arrays.youtube_stream_key, true, NULL, true);
|
||||
SETTING_ARRAY("twitch_stream_key", settings->arrays.twitch_stream_key, true, NULL, true);
|
||||
SETTING_ARRAY("facebook_stream_key", settings->arrays.facebook_stream_key, true, NULL, true);
|
||||
@ -1848,6 +1852,8 @@ static struct config_bool_setting *populate_settings_bool(
|
||||
SETTING_BOOL("core_updater_show_experimental_cores", &settings->bools.network_buildbot_show_experimental_cores, true, DEFAULT_NETWORK_BUILDBOT_SHOW_EXPERIMENTAL_CORES, false);
|
||||
SETTING_BOOL("core_updater_auto_backup", &settings->bools.core_updater_auto_backup, true, DEFAULT_CORE_UPDATER_AUTO_BACKUP, false);
|
||||
SETTING_BOOL("camera_allow", &settings->bools.camera_allow, true, false, false);
|
||||
SETTING_BOOL("cloud_sync_enable", &settings->bools.cloud_sync_enable, true, false, false);
|
||||
SETTING_BOOL("cloud_sync_destructive", &settings->bools.cloud_sync_destructive, true, false, false);
|
||||
SETTING_BOOL("discord_allow", &settings->bools.discord_enable, true, false, false);
|
||||
#if defined(VITA)
|
||||
SETTING_BOOL("input_backtouch_enable", &settings->bools.input_backtouch_enable, false, DEFAULT_INPUT_BACKTOUCH_ENABLE, false);
|
||||
|
@ -427,6 +427,7 @@ typedef struct settings
|
||||
char wifi_driver[32];
|
||||
char led_driver[32];
|
||||
char location_driver[32];
|
||||
char cloud_sync_driver[32];
|
||||
char menu_driver[32];
|
||||
char cheevos_username[32];
|
||||
char cheevos_password[256];
|
||||
@ -460,6 +461,10 @@ typedef struct settings
|
||||
|
||||
char translation_service_url[2048];
|
||||
|
||||
char webdav_url[255];
|
||||
char webdav_username[255];
|
||||
char webdav_password[255];
|
||||
|
||||
char youtube_stream_key[PATH_MAX_LENGTH];
|
||||
char twitch_stream_key[PATH_MAX_LENGTH];
|
||||
char facebook_stream_key[PATH_MAX_LENGTH];
|
||||
@ -899,6 +904,10 @@ typedef struct settings
|
||||
bool steam_rich_presence_enable;
|
||||
#endif
|
||||
|
||||
/* Cloud Sync */
|
||||
bool cloud_sync_enable;
|
||||
bool cloud_sync_destructive;
|
||||
|
||||
/* Misc. */
|
||||
bool discord_enable;
|
||||
bool threaded_data_runloop_enable;
|
||||
|
@ -1621,3 +1621,12 @@ STEAM INTEGRATION USING MIST
|
||||
#ifdef HAVE_PRESENCE
|
||||
#include "../network/presence.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
CLOUD SYNC
|
||||
============================================================ */
|
||||
#ifdef HAVE_CLOUDSYNC
|
||||
#include "../tasks/task_cloudsync.c"
|
||||
#include "../network/cloud_sync_driver.c"
|
||||
#include "../network/cloud_sync/webdav.c"
|
||||
#endif
|
||||
|
@ -1409,6 +1409,10 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_DEFERRED_SAVING_SETTINGS_LIST,
|
||||
"deferred_saving_settings_list"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_DEFERRED_CLOUD_SYNC_SETTINGS_LIST,
|
||||
"deferred_cloud_sync_settings_list"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_DEFERRED_THUMBNAILS_UPDATER_LIST,
|
||||
"deferred_thumbnails_updater_list"
|
||||
@ -3483,6 +3487,34 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_SAVING_SETTINGS,
|
||||
"saving_settings"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS,
|
||||
"cloud_sync_settings"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE,
|
||||
"cloud_sync_enable"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE,
|
||||
"cloud_sync_destructive"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER,
|
||||
"cloud_sync_driver"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_URL,
|
||||
"cloud_sync_url"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME,
|
||||
"cloud_sync_username"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD,
|
||||
"cloud_sync_password"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_SCAN_DIRECTORY,
|
||||
"scan_directory"
|
||||
|
@ -1204,6 +1204,62 @@ MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_SAVING_SETTINGS,
|
||||
"Change saving settings."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_SETTINGS,
|
||||
"Cloud Sync"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CLOUD_SYNC_SETTINGS,
|
||||
"Change cloud sync settings."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_ENABLE,
|
||||
"Enable Cloud Sync"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CLOUD_SYNC_ENABLE,
|
||||
"Attempt to sync configs, sram, and states to a cloud storage provider."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_DESTRUCTIVE,
|
||||
"Destructive Cloud Sync"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CLOUD_SYNC_DESTRUCTIVE,
|
||||
"When disabled, files are moved to a backup folder before being overwritten or deleted."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_DRIVER,
|
||||
"Cloud Sync Backend"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CLOUD_SYNC_DRIVER,
|
||||
"Which cloud storage network protocol to use."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_URL,
|
||||
"Cloud Storage URL"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CLOUD_SYNC_URL,
|
||||
"The URL for the API entry point to the cloud storage service."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_USERNAME,
|
||||
"Username"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CLOUD_SYNC_USERNAME,
|
||||
"Your username for your cloud storage account."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_PASSWORD,
|
||||
"Password"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CLOUD_SYNC_PASSWORD,
|
||||
"Your password for your cloud storage account."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_LOGGING_SETTINGS,
|
||||
"Logging"
|
||||
|
@ -51,6 +51,9 @@ void net_http_connection_set_user_agent(struct http_connection_t *conn, const ch
|
||||
|
||||
void net_http_connection_set_headers(struct http_connection_t *conn, const char *headers);
|
||||
|
||||
void net_http_connection_set_content(struct http_connection_t *conn, const char *content_type,
|
||||
size_t content_length, const void *content);
|
||||
|
||||
const char *net_http_connection_url(struct http_connection_t *conn);
|
||||
|
||||
const char* net_http_connection_method(struct http_connection_t* conn);
|
||||
@ -93,6 +96,17 @@ int net_http_status(struct http_t *state);
|
||||
**/
|
||||
bool net_http_error(struct http_t *state);
|
||||
|
||||
/**
|
||||
* net_http_headers:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return the response headers. The returned buffer is owned by the
|
||||
* caller of net_http_new; it is not freed by net_http_delete.
|
||||
* If the status is not 20x and accept_error is false, it returns NULL.
|
||||
**/
|
||||
struct string_list *net_http_headers(struct http_t *state);
|
||||
|
||||
/**
|
||||
* net_http_data:
|
||||
*
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <compat/strl.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <string.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <retro_common_api.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
@ -63,6 +64,7 @@ struct http_socket_state_t
|
||||
struct http_t
|
||||
{
|
||||
char *data;
|
||||
struct string_list *headers;
|
||||
struct http_socket_state_t sock_state; /* ptr alignment */
|
||||
size_t pos;
|
||||
size_t len;
|
||||
@ -81,10 +83,11 @@ struct http_connection_t
|
||||
char *scan;
|
||||
char *methodcopy;
|
||||
char *contenttypecopy;
|
||||
char *postdatacopy;
|
||||
void *postdatacopy;
|
||||
char *useragentcopy;
|
||||
char *headerscopy;
|
||||
struct http_socket_state_t sock_state; /* ptr alignment */
|
||||
size_t contentlength;
|
||||
int port;
|
||||
};
|
||||
|
||||
@ -530,7 +533,10 @@ struct http_connection_t *net_http_connection_new(const char *url,
|
||||
conn->methodcopy = strdup(method);
|
||||
|
||||
if (data)
|
||||
{
|
||||
conn->postdatacopy = strdup(data);
|
||||
conn->contentlength = strlen(data);
|
||||
}
|
||||
|
||||
if (!(conn->urlcopy = strdup(url)))
|
||||
goto error;
|
||||
@ -703,6 +709,25 @@ void net_http_connection_set_headers(
|
||||
conn->headerscopy = headers ? strdup(headers) : NULL;
|
||||
}
|
||||
|
||||
void net_http_connection_set_content(
|
||||
struct http_connection_t *conn, const char *content_type,
|
||||
size_t content_length, const void *content)
|
||||
|
||||
{
|
||||
if (conn->contenttypecopy)
|
||||
free(conn->contenttypecopy);
|
||||
if (conn->postdatacopy)
|
||||
free(conn->postdatacopy);
|
||||
|
||||
conn->contenttypecopy = content_type ? strdup(content_type) : NULL;
|
||||
conn->contentlength = content_length;
|
||||
if (content_length)
|
||||
{
|
||||
conn->postdatacopy = malloc(content_length);
|
||||
memcpy(conn->postdatacopy, content, content_length);
|
||||
}
|
||||
}
|
||||
|
||||
const char *net_http_connection_url(struct http_connection_t *conn)
|
||||
{
|
||||
return conn->urlcopy;
|
||||
@ -767,8 +792,7 @@ struct http_t *net_http_new(struct http_connection_t *conn)
|
||||
if (conn->headerscopy)
|
||||
net_http_send_str(&conn->sock_state, &error, conn->headerscopy,
|
||||
strlen(conn->headerscopy));
|
||||
/* This is not being set anywhere yet */
|
||||
else if (conn->contenttypecopy)
|
||||
if (conn->contenttypecopy)
|
||||
{
|
||||
net_http_send_str(&conn->sock_state, &error, "Content-Type: ",
|
||||
STRLEN_CONST("Content-Type: "));
|
||||
@ -778,12 +802,12 @@ struct http_t *net_http_new(struct http_connection_t *conn)
|
||||
STRLEN_CONST("\r\n"));
|
||||
}
|
||||
|
||||
if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST")))
|
||||
if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST") || string_is_equal(conn->methodcopy, "PUT")))
|
||||
{
|
||||
size_t post_len, len;
|
||||
char *len_str = NULL;
|
||||
|
||||
if (!conn->postdatacopy)
|
||||
if (!conn->postdatacopy && !string_is_equal(conn->methodcopy, "PUT"))
|
||||
goto err;
|
||||
|
||||
if (!conn->headerscopy)
|
||||
@ -799,7 +823,7 @@ struct http_t *net_http_new(struct http_connection_t *conn)
|
||||
net_http_send_str(&conn->sock_state, &error, "Content-Length: ",
|
||||
STRLEN_CONST("Content-Length: "));
|
||||
|
||||
post_len = strlen(conn->postdatacopy);
|
||||
post_len = conn->contentlength;
|
||||
#ifdef _WIN32
|
||||
len = snprintf(NULL, 0, "%" PRIuPTR, post_len);
|
||||
len_str = (char*)malloc(len + 1);
|
||||
@ -836,9 +860,9 @@ struct http_t *net_http_new(struct http_connection_t *conn)
|
||||
net_http_send_str(&conn->sock_state, &error, "\r\n",
|
||||
STRLEN_CONST("\r\n"));
|
||||
|
||||
if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST")))
|
||||
if (conn->postdatacopy && conn->contentlength)
|
||||
net_http_send_str(&conn->sock_state, &error, conn->postdatacopy,
|
||||
strlen(conn->postdatacopy));
|
||||
conn->contentlength);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
@ -854,7 +878,12 @@ struct http_t *net_http_new(struct http_connection_t *conn)
|
||||
state->buflen = 512;
|
||||
|
||||
if ((state->data = (char*)malloc(state->buflen)))
|
||||
return state;
|
||||
{
|
||||
if ((state->headers = string_list_new()) &&
|
||||
string_list_initialize(state->headers))
|
||||
return state;
|
||||
string_list_free(state->headers);
|
||||
}
|
||||
free(state);
|
||||
}
|
||||
|
||||
@ -865,6 +894,8 @@ err:
|
||||
free(conn->methodcopy);
|
||||
if (conn->contenttypecopy)
|
||||
free(conn->contenttypecopy);
|
||||
if (conn->postdatacopy)
|
||||
free(conn->postdatacopy);
|
||||
conn->methodcopy = NULL;
|
||||
conn->contenttypecopy = NULL;
|
||||
conn->postdatacopy = NULL;
|
||||
@ -981,12 +1012,24 @@ bool net_http_update(struct http_t *state, size_t* progress, size_t* total)
|
||||
if (string_is_equal_case_insensitive(state->data, "Transfer-Encoding: chunked"))
|
||||
state->bodytype = T_CHUNK;
|
||||
|
||||
/* TODO: save headers somewhere */
|
||||
if (state->data[0]=='\0')
|
||||
{
|
||||
state->part = P_BODY;
|
||||
if (state->bodytype == T_CHUNK)
|
||||
state->part = P_BODY_CHUNKLEN;
|
||||
if (state->status == 100)
|
||||
{
|
||||
state->part = P_HEADER_TOP;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->part = P_BODY;
|
||||
if (state->bodytype == T_CHUNK)
|
||||
state->part = P_BODY_CHUNKLEN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
union string_list_elem_attr attr;
|
||||
attr.i = 0;
|
||||
string_list_append(state->headers, state->data, attr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1007,7 +1050,7 @@ bool net_http_update(struct http_t *state, size_t* progress, size_t* total)
|
||||
{
|
||||
if (state->error)
|
||||
newlen = -1;
|
||||
else
|
||||
else if (state->len)
|
||||
{
|
||||
#ifdef HAVE_SSL
|
||||
if (state->sock_state.ssl && state->sock_state.ssl_ctx)
|
||||
@ -1158,6 +1201,26 @@ int net_http_status(struct http_t *state)
|
||||
return state->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* net_http_headers:
|
||||
*
|
||||
* Leaf function.
|
||||
*
|
||||
* @return the response headers. The returned buffer is owned by the
|
||||
* caller of net_http_new; it is not freed by net_http_delete().
|
||||
* If the status is not 20x and accept_error is false, it returns NULL.
|
||||
**/
|
||||
struct string_list *net_http_headers(struct http_t *state)
|
||||
{
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
if (state->error)
|
||||
return NULL;
|
||||
|
||||
return state->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* net_http_data:
|
||||
*
|
||||
|
@ -234,11 +234,24 @@ int ssl_socket_send_all_blocking(void *state_data,
|
||||
|
||||
mbedtls_net_set_block(&state->net_ctx);
|
||||
|
||||
while ((ret = mbedtls_ssl_write(&state->ctx, data, size)) <= 0)
|
||||
while (size)
|
||||
{
|
||||
if ( ret != MBEDTLS_ERR_SSL_WANT_READ &&
|
||||
ret != MBEDTLS_ERR_SSL_WANT_WRITE)
|
||||
return false;
|
||||
ret = mbedtls_ssl_write(&state->ctx, data, size);
|
||||
|
||||
if (!ret)
|
||||
continue;
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
if ( ret != MBEDTLS_ERR_SSL_WANT_READ &&
|
||||
ret != MBEDTLS_ERR_SSL_WANT_WRITE)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
data += ret;
|
||||
size -= ret;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -57,6 +57,7 @@ enum string_list_type
|
||||
STRING_LIST_INPUT_JOYPAD_DRIVERS,
|
||||
STRING_LIST_INPUT_HID_DRIVERS,
|
||||
STRING_LIST_RECORD_DRIVERS,
|
||||
STRING_LIST_CLOUD_SYNC_DRIVERS,
|
||||
#ifdef HAVE_LAKKA
|
||||
STRING_LIST_TIMEZONES,
|
||||
#endif
|
||||
|
@ -176,6 +176,7 @@ GENERIC_DEFERRED_PUSH(deferred_push_video_hdr_settings_list, DISPLAYLIST_
|
||||
GENERIC_DEFERRED_PUSH(deferred_push_crt_switchres_settings_list, DISPLAYLIST_CRT_SWITCHRES_SETTINGS_LIST)
|
||||
GENERIC_DEFERRED_PUSH(deferred_push_configuration_settings_list, DISPLAYLIST_CONFIGURATION_SETTINGS_LIST)
|
||||
GENERIC_DEFERRED_PUSH(deferred_push_saving_settings_list, DISPLAYLIST_SAVING_SETTINGS_LIST)
|
||||
GENERIC_DEFERRED_PUSH(deferred_push_cloud_sync_settings_list, DISPLAYLIST_CLOUD_SYNC_SETTINGS_LIST)
|
||||
GENERIC_DEFERRED_PUSH(deferred_push_mixer_stream_settings_list, DISPLAYLIST_MIXER_STREAM_SETTINGS_LIST)
|
||||
GENERIC_DEFERRED_PUSH(deferred_push_logging_settings_list, DISPLAYLIST_LOGGING_SETTINGS_LIST)
|
||||
GENERIC_DEFERRED_PUSH(deferred_push_frame_throttle_settings_list, DISPLAYLIST_FRAME_THROTTLE_SETTINGS_LIST)
|
||||
@ -702,6 +703,7 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
|
||||
{MENU_ENUM_LABEL_DEFERRED_CORE_INFORMATION_LIST, deferred_push_core_information_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_CONFIGURATION_SETTINGS_LIST, deferred_push_configuration_settings_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_SAVING_SETTINGS_LIST, deferred_push_saving_settings_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_CLOUD_SYNC_SETTINGS_LIST, deferred_push_cloud_sync_settings_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_MIXER_STREAM_SETTINGS_LIST, deferred_push_mixer_stream_settings_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_LOGGING_SETTINGS_LIST, deferred_push_logging_settings_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_FRAME_THROTTLE_SETTINGS_LIST, deferred_push_frame_throttle_settings_list},
|
||||
@ -1262,8 +1264,11 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
|
||||
case MENU_ENUM_LABEL_DEFERRED_SAVING_SETTINGS_LIST:
|
||||
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_saving_settings_list);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_DEFERRED_CLOUD_SYNC_SETTINGS_LIST:
|
||||
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_cloud_sync_settings_list);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_DEFERRED_LOGGING_SETTINGS_LIST:
|
||||
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_saving_settings_list);
|
||||
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_logging_settings_list);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_DEFERRED_FRAME_THROTTLE_SETTINGS_LIST:
|
||||
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_frame_throttle_settings_list);
|
||||
|
@ -366,6 +366,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl)
|
||||
return MENU_ENUM_LABEL_DEFERRED_CONFIGURATION_SETTINGS_LIST;
|
||||
case ACTION_OK_DL_SAVING_SETTINGS_LIST:
|
||||
return MENU_ENUM_LABEL_DEFERRED_SAVING_SETTINGS_LIST;
|
||||
case ACTION_OK_DL_CLOUD_SYNC_SETTINGS_LIST:
|
||||
return MENU_ENUM_LABEL_DEFERRED_CLOUD_SYNC_SETTINGS_LIST;
|
||||
case ACTION_OK_DL_LOGGING_SETTINGS_LIST:
|
||||
return MENU_ENUM_LABEL_DEFERRED_LOGGING_SETTINGS_LIST;
|
||||
case ACTION_OK_DL_FRAME_THROTTLE_SETTINGS_LIST:
|
||||
@ -1672,6 +1674,7 @@ int generic_action_ok_displaylist_push(
|
||||
case ACTION_OK_DL_CRT_SWITCHRES_SETTINGS_LIST:
|
||||
case ACTION_OK_DL_CONFIGURATION_SETTINGS_LIST:
|
||||
case ACTION_OK_DL_SAVING_SETTINGS_LIST:
|
||||
case ACTION_OK_DL_CLOUD_SYNC_SETTINGS_LIST:
|
||||
case ACTION_OK_DL_LOGGING_SETTINGS_LIST:
|
||||
case ACTION_OK_DL_FRAME_THROTTLE_SETTINGS_LIST:
|
||||
case ACTION_OK_DL_FRAME_TIME_COUNTER_SETTINGS_LIST:
|
||||
@ -5924,6 +5927,7 @@ STATIC_DEFAULT_ACTION_OK_FUNC(action_ok_parent_directory_push, ACTION_OK_DL_PARE
|
||||
STATIC_DEFAULT_ACTION_OK_FUNC(action_ok_directory_push, ACTION_OK_DL_DIRECTORY_PUSH)
|
||||
STATIC_DEFAULT_ACTION_OK_FUNC(action_ok_configurations_list, ACTION_OK_DL_CONFIGURATIONS_LIST)
|
||||
STATIC_DEFAULT_ACTION_OK_FUNC(action_ok_saving_list, ACTION_OK_DL_SAVING_SETTINGS_LIST)
|
||||
STATIC_DEFAULT_ACTION_OK_FUNC(action_ok_cloud_sync_list, ACTION_OK_DL_CLOUD_SYNC_SETTINGS_LIST)
|
||||
STATIC_DEFAULT_ACTION_OK_FUNC(action_ok_network_list, ACTION_OK_DL_NETWORK_SETTINGS_LIST)
|
||||
STATIC_DEFAULT_ACTION_OK_FUNC(action_ok_network_hosting_list, ACTION_OK_DL_NETWORK_HOSTING_SETTINGS_LIST)
|
||||
STATIC_DEFAULT_ACTION_OK_FUNC(action_ok_netplay_kick_list, ACTION_OK_DL_NETPLAY_KICK_LIST)
|
||||
@ -8568,6 +8572,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
|
||||
{MENU_ENUM_LABEL_SUBSYSTEM_LOAD, action_ok_subsystem_add_load},
|
||||
{MENU_ENUM_LABEL_CONFIGURATIONS, action_ok_configurations_list},
|
||||
{MENU_ENUM_LABEL_SAVING_SETTINGS, action_ok_saving_list},
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS, action_ok_cloud_sync_list},
|
||||
{MENU_ENUM_LABEL_LOGGING_SETTINGS, action_ok_logging_list},
|
||||
{MENU_ENUM_LABEL_FRAME_THROTTLE_SETTINGS, action_ok_frame_throttle_list},
|
||||
{MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS, action_ok_frame_time_counter_list},
|
||||
|
@ -252,6 +252,13 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_crt_switchres_hires_menu, MENU
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_driver_settings_list, MENU_ENUM_SUBLABEL_DRIVER_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_retro_achievements_settings_list, MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_saving_settings_list, MENU_ENUM_SUBLABEL_SAVING_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_settings_list, MENU_ENUM_SUBLABEL_CLOUD_SYNC_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_enable, MENU_ENUM_SUBLABEL_CLOUD_SYNC_ENABLE)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_destructive, MENU_ENUM_SUBLABEL_CLOUD_SYNC_DESTRUCTIVE)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_driver, MENU_ENUM_SUBLABEL_CLOUD_SYNC_DRIVER)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_url, MENU_ENUM_SUBLABEL_CLOUD_SYNC_URL)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_username, MENU_ENUM_SUBLABEL_CLOUD_SYNC_USERNAME)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_password, MENU_ENUM_SUBLABEL_CLOUD_SYNC_PASSWORD)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_logging_settings_list, MENU_ENUM_SUBLABEL_LOGGING_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_user_interface_settings_list, MENU_ENUM_SUBLABEL_USER_INTERFACE_SETTINGS)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_ai_service_settings_list, MENU_ENUM_SUBLABEL_AI_SERVICE_SETTINGS)
|
||||
@ -4784,6 +4791,27 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_SAVING_SETTINGS:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_saving_settings_list);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_settings_list);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_enable);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_destructive);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_driver);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_URL:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_url);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_username);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_password);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_LOGGING_SETTINGS:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_logging_settings_list);
|
||||
break;
|
||||
|
@ -629,6 +629,7 @@ DEFAULT_TITLE_MACRO(action_get_dump_disc_list, MENU_ENUM_LABEL_
|
||||
DEFAULT_TITLE_MACRO(action_get_eject_disc, MENU_ENUM_LABEL_VALUE_EJECT_DISC)
|
||||
#endif
|
||||
DEFAULT_TITLE_MACRO(action_get_saving_settings_list, MENU_ENUM_LABEL_VALUE_SAVING_SETTINGS)
|
||||
DEFAULT_TITLE_MACRO(action_get_cloud_sync_settings_list, MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_SETTINGS)
|
||||
DEFAULT_TITLE_MACRO(action_get_logging_settings_list, MENU_ENUM_LABEL_VALUE_LOGGING_SETTINGS)
|
||||
DEFAULT_TITLE_MACRO(action_get_frame_throttle_settings_list, MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_SETTINGS)
|
||||
DEFAULT_TITLE_MACRO(action_get_frame_time_counter_settings_list, MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_SETTINGS)
|
||||
@ -989,6 +990,7 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
|
||||
{MENU_ENUM_LABEL_DEFERRED_LOAD_DISC_LIST, action_get_load_disc_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_CONFIGURATION_SETTINGS_LIST, action_get_configuration_settings_list },
|
||||
{MENU_ENUM_LABEL_DEFERRED_SAVING_SETTINGS_LIST, action_get_saving_settings_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_CLOUD_SYNC_SETTINGS_LIST, action_get_cloud_sync_settings_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_LOGGING_SETTINGS_LIST, action_get_logging_settings_list},
|
||||
{MENU_ENUM_LABEL_DEFERRED_FRAME_TIME_COUNTER_SETTINGS_LIST, action_get_frame_time_counter_settings_list },
|
||||
{MENU_ENUM_LABEL_DEFERRED_FRAME_THROTTLE_SETTINGS_LIST, action_get_frame_throttle_settings_list},
|
||||
@ -1597,6 +1599,9 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_DEFERRED_SAVING_SETTINGS_LIST:
|
||||
BIND_ACTION_GET_TITLE(cbs, action_get_saving_settings_list);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_DEFERRED_CLOUD_SYNC_SETTINGS_LIST:
|
||||
BIND_ACTION_GET_TITLE(cbs, action_get_cloud_sync_settings_list);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_DEFERRED_FRAME_THROTTLE_SETTINGS_LIST:
|
||||
BIND_ACTION_GET_TITLE(cbs, action_get_frame_throttle_settings_list);
|
||||
break;
|
||||
|
@ -10860,6 +10860,7 @@ static void materialui_list_insert(
|
||||
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SIDELOAD_CORE_LIST))
|
||||
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CRT_SWITCHRES_SETTINGS))
|
||||
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_SAVING_SETTINGS))
|
||||
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS))
|
||||
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_LOGGING_SETTINGS))
|
||||
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FRAME_THROTTLE_SETTINGS))
|
||||
|| string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_RECORDING_SETTINGS))
|
||||
|
@ -2125,6 +2125,7 @@ static uintptr_t ozone_entries_icon_get_texture(
|
||||
case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS:
|
||||
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST:
|
||||
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST:
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS:
|
||||
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RELOAD];
|
||||
case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE:
|
||||
/* Only show icon in Throttle settings */
|
||||
|
@ -2956,6 +2956,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
|
||||
case MENU_ENUM_LABEL_FRAME_TIME_COUNTER_SETTINGS:
|
||||
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_CLEAN_PLAYLIST:
|
||||
case MENU_ENUM_LABEL_PLAYLIST_MANAGER_REFRESH_PLAYLIST:
|
||||
case MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS:
|
||||
return xmb->textures.list[XMB_TEXTURE_RELOAD];
|
||||
case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE:
|
||||
/* Only show icon in Throttle settings */
|
||||
|
@ -120,6 +120,7 @@ enum
|
||||
ACTION_OK_DL_LATENCY_SETTINGS_LIST,
|
||||
ACTION_OK_DL_CONFIGURATION_SETTINGS_LIST,
|
||||
ACTION_OK_DL_SAVING_SETTINGS_LIST,
|
||||
ACTION_OK_DL_CLOUD_SYNC_SETTINGS_LIST,
|
||||
ACTION_OK_DL_LOGGING_SETTINGS_LIST,
|
||||
ACTION_OK_DL_FRAME_THROTTLE_SETTINGS_LIST,
|
||||
ACTION_OK_DL_FRAME_TIME_COUNTER_SETTINGS_LIST,
|
||||
|
@ -10142,6 +10142,9 @@ unsigned menu_displaylist_build_list(
|
||||
{MENU_ENUM_LABEL_SCREENSHOTS_IN_CONTENT_DIR_ENABLE, PARSE_ONLY_BOOL, true},
|
||||
{MENU_ENUM_LABEL_CONTENT_RUNTIME_LOG, PARSE_ONLY_BOOL, true},
|
||||
{MENU_ENUM_LABEL_CONTENT_RUNTIME_LOG_AGGREGATE, PARSE_ONLY_BOOL, true},
|
||||
#if HAVE_CLOUDSYNC
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS, PARSE_ACTION, true},
|
||||
#endif
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(build_list); i++)
|
||||
@ -10169,6 +10172,26 @@ unsigned menu_displaylist_build_list(
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DISPLAYLIST_CLOUD_SYNC_SETTINGS_LIST:
|
||||
{
|
||||
menu_displaylist_build_info_t build_list[] = {
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE, PARSE_ONLY_BOOL },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE, PARSE_ONLY_BOOL },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER, PARSE_ONLY_STRING_OPTIONS },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_URL, PARSE_ONLY_STRING },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME, PARSE_ONLY_STRING },
|
||||
{MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD, PARSE_ONLY_STRING },
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(build_list); i++)
|
||||
{
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
build_list[i].enum_idx, build_list[i].parse_type,
|
||||
false) == 0)
|
||||
count++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_MIST
|
||||
case DISPLAYLIST_STEAM_SETTINGS_LIST:
|
||||
{
|
||||
@ -13637,6 +13660,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
break;
|
||||
#endif
|
||||
case DISPLAYLIST_SAVING_SETTINGS_LIST:
|
||||
case DISPLAYLIST_CLOUD_SYNC_SETTINGS_LIST:
|
||||
case DISPLAYLIST_DRIVER_SETTINGS_LIST:
|
||||
case DISPLAYLIST_LOGGING_SETTINGS_LIST:
|
||||
case DISPLAYLIST_FRAME_THROTTLE_SETTINGS_LIST:
|
||||
|
@ -181,6 +181,7 @@ enum menu_displaylist_ctl_state
|
||||
DISPLAYLIST_VIDEO_SETTINGS_LIST,
|
||||
DISPLAYLIST_CONFIGURATION_SETTINGS_LIST,
|
||||
DISPLAYLIST_SAVING_SETTINGS_LIST,
|
||||
DISPLAYLIST_CLOUD_SYNC_SETTINGS_LIST,
|
||||
DISPLAYLIST_LOGGING_SETTINGS_LIST,
|
||||
DISPLAYLIST_FRAME_THROTTLE_SETTINGS_LIST,
|
||||
DISPLAYLIST_FRAME_TIME_COUNTER_SETTINGS_LIST,
|
||||
|
@ -93,6 +93,7 @@
|
||||
#endif
|
||||
#include "../midi_driver.h"
|
||||
#include "../location_driver.h"
|
||||
#include "../network/cloud_sync_driver.h"
|
||||
#include "../record/record_driver.h"
|
||||
#include "../tasks/tasks_internal.h"
|
||||
#include "../config.def.h"
|
||||
@ -275,6 +276,7 @@ enum settings_list_type
|
||||
SETTINGS_LIST_CONFIGURATION,
|
||||
SETTINGS_LIST_LOGGING,
|
||||
SETTINGS_LIST_SAVING,
|
||||
SETTINGS_LIST_CLOUD_SYNC,
|
||||
SETTINGS_LIST_REWIND,
|
||||
SETTINGS_LIST_CHEAT_DETAILS,
|
||||
SETTINGS_LIST_CHEAT_SEARCH,
|
||||
@ -2936,8 +2938,8 @@ static void setting_get_string_representation_max_users(rarch_setting_t *setting
|
||||
snprintf(s, len, "%d", *setting->value.target.unsigned_integer);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
static void setting_get_string_representation_cheevos_password(
|
||||
#if defined(HAVE_CHEEVOS) || defined(HAVE_CLOUDSYNC)
|
||||
static void setting_get_string_representation_password(
|
||||
rarch_setting_t *setting,
|
||||
char *s, size_t len)
|
||||
{
|
||||
@ -11099,10 +11101,128 @@ static bool setting_append_list(
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
|
||||
CONFIG_ACTION(
|
||||
list, list_info,
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS,
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_SETTINGS,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group);
|
||||
|
||||
END_SUB_GROUP(list, list_info, parent_group);
|
||||
END_GROUP(list, list_info, parent_group);
|
||||
}
|
||||
|
||||
break;
|
||||
case SETTINGS_LIST_CLOUD_SYNC:
|
||||
#ifdef HAVE_CLOUDSYNC
|
||||
START_GROUP(list, list_info, &group_info,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_SETTINGS),
|
||||
parent_group);
|
||||
parent_group = msg_hash_to_str(MENU_ENUM_LABEL_CLOUD_SYNC_SETTINGS);
|
||||
START_SUB_GROUP(list, list_info, "State", &group_info, &subgroup_info, parent_group);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.cloud_sync_enable,
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE,
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_ENABLE,
|
||||
false,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.cloud_sync_destructive,
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE,
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_DESTRUCTIVE,
|
||||
false,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
|
||||
CONFIG_STRING_OPTIONS(
|
||||
list, list_info,
|
||||
settings->arrays.cloud_sync_driver,
|
||||
sizeof(settings->arrays.cloud_sync_driver),
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER,
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_DRIVER,
|
||||
"null",
|
||||
config_get_cloud_sync_driver_options(),
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_read_handler,
|
||||
general_write_handler);
|
||||
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_IS_DRIVER);
|
||||
(*list)[list_info->index - 1].action_ok = setting_action_ok_uint;
|
||||
(*list)[list_info->index - 1].action_left = setting_string_action_left_driver;
|
||||
(*list)[list_info->index - 1].action_right = setting_string_action_right_driver;
|
||||
|
||||
CONFIG_STRING(
|
||||
list, list_info,
|
||||
settings->arrays.webdav_url,
|
||||
sizeof(settings->arrays.webdav_url),
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_URL,
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_URL,
|
||||
"",
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler);
|
||||
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT);
|
||||
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT;
|
||||
(*list)[list_info->index - 1].action_start = setting_generic_action_start_default;
|
||||
|
||||
CONFIG_STRING(
|
||||
list, list_info,
|
||||
settings->arrays.webdav_username,
|
||||
sizeof(settings->arrays.webdav_username),
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME,
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_USERNAME,
|
||||
"",
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler);
|
||||
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT);
|
||||
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT;
|
||||
(*list)[list_info->index - 1].action_start = setting_generic_action_start_default;
|
||||
|
||||
CONFIG_STRING(
|
||||
list, list_info,
|
||||
settings->arrays.webdav_password,
|
||||
sizeof(settings->arrays.webdav_password),
|
||||
MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD,
|
||||
MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_PASSWORD,
|
||||
"",
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler);
|
||||
(*list)[list_info->index - 1].get_string_representation =
|
||||
&setting_get_string_representation_password;
|
||||
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT);
|
||||
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_PASSWORD_LINE_EDIT;
|
||||
(*list)[list_info->index - 1].action_start = setting_generic_action_start_default;
|
||||
|
||||
END_SUB_GROUP(list, list_info, parent_group);
|
||||
END_GROUP(list, list_info, parent_group);
|
||||
#endif
|
||||
break;
|
||||
case SETTINGS_LIST_FRAME_TIME_COUNTER:
|
||||
START_GROUP(list, list_info, &group_info, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FRAME_TIME_COUNTER_SETTINGS), parent_group);
|
||||
@ -21994,7 +22114,7 @@ static bool setting_append_list(
|
||||
general_write_handler,
|
||||
general_read_handler);
|
||||
(*list)[list_info->index - 1].get_string_representation =
|
||||
&setting_get_string_representation_cheevos_password;
|
||||
&setting_get_string_representation_password;
|
||||
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT);
|
||||
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_PASSWORD_LINE_EDIT;
|
||||
(*list)[list_info->index - 1].action_start = setting_generic_action_start_default;
|
||||
@ -22917,6 +23037,7 @@ static rarch_setting_t *menu_setting_new_internal(rarch_setting_info_t *list_inf
|
||||
SETTINGS_LIST_CONFIGURATION,
|
||||
SETTINGS_LIST_LOGGING,
|
||||
SETTINGS_LIST_SAVING,
|
||||
SETTINGS_LIST_CLOUD_SYNC,
|
||||
SETTINGS_LIST_REWIND,
|
||||
SETTINGS_LIST_CHEAT_DETAILS,
|
||||
SETTINGS_LIST_CHEAT_SEARCH,
|
||||
|
@ -1920,6 +1920,7 @@ enum msg_hash_enums
|
||||
MENU_ENUM_LABEL_DEFERRED_CRT_SWITCHRES_SETTINGS_LIST,
|
||||
MENU_ENUM_LABEL_DEFERRED_CONFIGURATION_SETTINGS_LIST,
|
||||
MENU_ENUM_LABEL_DEFERRED_SAVING_SETTINGS_LIST,
|
||||
MENU_ENUM_LABEL_DEFERRED_CLOUD_SYNC_SETTINGS_LIST,
|
||||
MENU_ENUM_LABEL_DEFERRED_FRAME_THROTTLE_SETTINGS_LIST,
|
||||
MENU_ENUM_LABEL_DEFERRED_REWIND_SETTINGS_LIST,
|
||||
MENU_ENUM_LABEL_DEFERRED_FRAME_TIME_COUNTER_SETTINGS_LIST,
|
||||
@ -3062,6 +3063,13 @@ enum msg_hash_enums
|
||||
MENU_LABEL(CONFIGURATION_SETTINGS),
|
||||
MENU_LABEL(LOGGING_SETTINGS),
|
||||
MENU_LABEL(SAVING_SETTINGS),
|
||||
MENU_LABEL(CLOUD_SYNC_SETTINGS),
|
||||
MENU_LABEL(CLOUD_SYNC_ENABLE),
|
||||
MENU_LABEL(CLOUD_SYNC_DESTRUCTIVE),
|
||||
MENU_LABEL(CLOUD_SYNC_DRIVER),
|
||||
MENU_LABEL(CLOUD_SYNC_URL),
|
||||
MENU_LABEL(CLOUD_SYNC_USERNAME),
|
||||
MENU_LABEL(CLOUD_SYNC_PASSWORD),
|
||||
MENU_LABEL(RECORDING_SETTINGS),
|
||||
MENU_LABEL(OVERLAY_SETTINGS),
|
||||
MENU_LABEL(REWIND_SETTINGS),
|
||||
|
803
network/cloud_sync/webdav.c
Normal file
803
network/cloud_sync/webdav.c
Normal file
@ -0,0 +1,803 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <encodings/base64.h>
|
||||
#include <lrc_hash.h>
|
||||
#include <net/net_http.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <time/rtime.h>
|
||||
|
||||
#include "../cloud_sync_driver.h"
|
||||
#include "../../retroarch.h"
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char path[PATH_MAX_LENGTH];
|
||||
char file[PATH_MAX_LENGTH];
|
||||
cloud_sync_complete_handler_t cb;
|
||||
void *user_data;
|
||||
RFILE *rfile;
|
||||
} webdav_cb_state_t;
|
||||
|
||||
typedef void (*webdav_mkdir_cb_t)(bool success, webdav_cb_state_t *state);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char url[PATH_MAX_LENGTH];
|
||||
char *last_slash;
|
||||
webdav_mkdir_cb_t cb;
|
||||
webdav_cb_state_t *cb_st;
|
||||
} webdav_mkdir_state_t;
|
||||
|
||||
// TODO: all of this HTTP auth stuff should probably live in libretro-common/net?
|
||||
typedef struct
|
||||
{
|
||||
char url[PATH_MAX_LENGTH];
|
||||
|
||||
bool basic;
|
||||
char *basic_auth_header;
|
||||
|
||||
char *username;
|
||||
char *ha1hash;
|
||||
char *realm;
|
||||
char *nonce;
|
||||
char *algo;
|
||||
char *opaque;
|
||||
char *cnonce;
|
||||
bool qop_auth;
|
||||
unsigned nc;
|
||||
char *digest_auth_header;
|
||||
} webdav_state_t;
|
||||
|
||||
static webdav_state_t webdav_driver_st = {0};
|
||||
|
||||
webdav_state_t *webdav_state_get_ptr(void)
|
||||
{
|
||||
return &webdav_driver_st;
|
||||
}
|
||||
|
||||
static char *webdav_create_basic_auth(void)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
size_t len = 0;
|
||||
char userpass[512];
|
||||
char *base64auth;
|
||||
int flen;
|
||||
|
||||
if (!string_is_empty(settings->arrays.webdav_username))
|
||||
len += strlcpy(userpass + len, settings->arrays.webdav_username, sizeof(userpass) - len);
|
||||
userpass[len++] = ':';
|
||||
if (!string_is_empty(settings->arrays.webdav_password))
|
||||
len += strlcpy(userpass + len, settings->arrays.webdav_password, sizeof(userpass) - len);
|
||||
userpass[len] = '\0';
|
||||
base64auth = base64(userpass, (int)len, &flen);
|
||||
len = strlcpy(userpass, "Authorization: Basic ", sizeof(userpass));
|
||||
len += strlcpy(userpass + len, base64auth, sizeof(userpass) - len);
|
||||
free(base64auth);
|
||||
userpass[len++] = '\r';
|
||||
userpass[len++] = '\n';
|
||||
userpass[len ] = '\0';
|
||||
|
||||
return strdup(userpass);
|
||||
}
|
||||
|
||||
static void webdav_cleanup_digest(void)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
|
||||
if (webdav_st->ha1hash)
|
||||
free(webdav_st->ha1hash);
|
||||
webdav_st->ha1hash = NULL;
|
||||
|
||||
if (webdav_st->realm)
|
||||
free(webdav_st->realm);
|
||||
webdav_st->realm = NULL;
|
||||
|
||||
if (webdav_st->nonce)
|
||||
free(webdav_st->nonce);
|
||||
webdav_st->nonce = NULL;
|
||||
|
||||
if (webdav_st->algo)
|
||||
free(webdav_st->algo);
|
||||
webdav_st->algo = NULL;
|
||||
|
||||
if (webdav_st->opaque)
|
||||
free(webdav_st->opaque);
|
||||
webdav_st->opaque = NULL;
|
||||
|
||||
webdav_st->qop_auth = false;
|
||||
webdav_st->nc = 1;
|
||||
|
||||
if (webdav_st->digest_auth_header)
|
||||
free(webdav_st->digest_auth_header);
|
||||
webdav_st->digest_auth_header = NULL;
|
||||
}
|
||||
|
||||
static char *webdav_create_ha1_hash(char *user, char *realm, char *pass)
|
||||
{
|
||||
char *hash = malloc(33);
|
||||
MD5_CTX md5;
|
||||
unsigned char digest[16];
|
||||
|
||||
MD5_Init(&md5);
|
||||
MD5_Update(&md5, user, strlen(user));
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, realm, strlen(realm));
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, pass, strlen(pass));
|
||||
MD5_Final(digest, &md5);
|
||||
|
||||
snprintf(hash, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
|
||||
);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static bool webdav_create_digest_auth(char *digest)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
settings_t *settings = config_get_ptr();
|
||||
char *ptr = digest + strlen("WWW-Authenticate: Digest ");
|
||||
char *end = ptr + strlen(ptr);
|
||||
size_t sz;
|
||||
|
||||
if (string_is_empty(settings->arrays.webdav_username) &&
|
||||
string_is_empty(settings->arrays.webdav_password))
|
||||
return false;
|
||||
|
||||
webdav_cleanup_digest();
|
||||
|
||||
webdav_st->username = settings->arrays.webdav_username;
|
||||
|
||||
while (ptr < end)
|
||||
{
|
||||
while (ISSPACE(*ptr))
|
||||
++ptr;
|
||||
|
||||
if (!*ptr)
|
||||
break;
|
||||
|
||||
if (string_starts_with(ptr, "realm=\""))
|
||||
{
|
||||
ptr += strlen("realm=\"");
|
||||
sz = strchr(ptr, '"') + 1 - ptr;
|
||||
webdav_st->realm = malloc(sz);
|
||||
strlcpy(webdav_st->realm, ptr, sz);
|
||||
ptr += sz;
|
||||
|
||||
webdav_st->ha1hash = webdav_create_ha1_hash(webdav_st->username, webdav_st->realm, settings->arrays.webdav_password);
|
||||
}
|
||||
else if (string_starts_with(ptr, "qop=\""))
|
||||
{
|
||||
char *tail;
|
||||
ptr += strlen("qop=\"");
|
||||
tail = strchr(ptr, '"');
|
||||
while (ptr < tail)
|
||||
{
|
||||
if (string_starts_with(ptr, "auth") &&
|
||||
(ptr[4] == ',' || ptr[4] == '"'))
|
||||
{
|
||||
webdav_st->qop_auth = true;
|
||||
break;
|
||||
}
|
||||
while (*ptr != ',' && *ptr != '"' && *ptr != '\0')
|
||||
ptr++;
|
||||
ptr++;
|
||||
}
|
||||
/* not even going to try for auth-int, sorry */
|
||||
if (!webdav_st->qop_auth)
|
||||
return false;
|
||||
while (*ptr != ',' && *ptr != '"' && *ptr != '\0')
|
||||
ptr++;
|
||||
ptr++;
|
||||
}
|
||||
else if (string_starts_with(ptr, "nonce=\""))
|
||||
{
|
||||
ptr += strlen("nonce=\"");
|
||||
sz = strchr(ptr, '"') + 1 - ptr;
|
||||
webdav_st->nonce = malloc(sz);
|
||||
strlcpy(webdav_st->nonce, ptr, sz);
|
||||
ptr += sz;
|
||||
}
|
||||
else if (string_starts_with(ptr, "algorithm="))
|
||||
{
|
||||
ptr += strlen("algorithm=");
|
||||
sz = strchr(ptr, ',') + 1 - ptr;
|
||||
webdav_st->algo = malloc(sz);
|
||||
strlcpy(webdav_st->algo, ptr, sz);
|
||||
ptr += sz;
|
||||
}
|
||||
else if (string_starts_with(ptr, "opaque=\""))
|
||||
{
|
||||
ptr += strlen("opaque=\"");
|
||||
sz = strchr(ptr, '"') + 1 - ptr;
|
||||
webdav_st->opaque = malloc(sz);
|
||||
strlcpy(webdav_st->opaque, ptr, sz);
|
||||
ptr += sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (*ptr != '=' && *ptr != '\0')
|
||||
ptr++;
|
||||
ptr++;
|
||||
if (*ptr == '"')
|
||||
{
|
||||
ptr++;
|
||||
while (*ptr != '"' && *ptr != '\0')
|
||||
ptr++;
|
||||
ptr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (*ptr != ',' && *ptr != ',')
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
while (ISSPACE(*ptr))
|
||||
++ptr;
|
||||
if (*ptr == ',')
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (!webdav_st->ha1hash || !webdav_st->nonce)
|
||||
return false;
|
||||
|
||||
webdav_st->cnonce = "1a2b3c4f";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *webdav_create_ha1(void)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
char *hash;
|
||||
MD5_CTX md5;
|
||||
unsigned char digest[16];
|
||||
|
||||
if (!string_is_equal(webdav_st->algo, "MD5-sess"))
|
||||
return strdup(webdav_st->ha1hash);
|
||||
|
||||
hash = malloc(33);
|
||||
|
||||
MD5_Init(&md5);
|
||||
MD5_Update(&md5, webdav_st->ha1hash, 32);
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, webdav_st->nonce, strlen(webdav_st->nonce));
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, webdav_st->cnonce, strlen(webdav_st->cnonce));
|
||||
MD5_Final(digest, &md5);
|
||||
|
||||
snprintf(hash, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
|
||||
);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static char *webdav_create_ha2(const char *method, const char *path)
|
||||
{
|
||||
/* no attempt at supporting auth-int, everything else uses this */
|
||||
char *hash = malloc(33);
|
||||
MD5_CTX md5;
|
||||
unsigned char digest[16];
|
||||
|
||||
MD5_Init(&md5);
|
||||
MD5_Update(&md5, method, strlen(method));
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, path, strlen(path));
|
||||
MD5_Final(digest, &md5);
|
||||
|
||||
snprintf(hash, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
|
||||
);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static char *webdav_create_digest_response(const char *method, const char *path)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
char *ha1 = webdav_create_ha1();
|
||||
char *ha2 = webdav_create_ha2(method, path);
|
||||
char *hash = malloc(33);
|
||||
MD5_CTX md5;
|
||||
unsigned char digest[16];
|
||||
|
||||
MD5_Init(&md5);
|
||||
MD5_Update(&md5, ha1, 32);
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, webdav_st->nonce, strlen(webdav_st->nonce));
|
||||
if (webdav_st->qop_auth)
|
||||
{
|
||||
char nonceCount[10];
|
||||
snprintf(nonceCount, sizeof(nonceCount), "%08x", webdav_st->nc);
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, nonceCount, strlen(nonceCount));
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, webdav_st->cnonce, strlen(webdav_st->cnonce));
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, "auth", strlen("auth"));
|
||||
}
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, ha2, 32);
|
||||
MD5_Final(digest, &md5);
|
||||
|
||||
snprintf(hash, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
|
||||
);
|
||||
|
||||
free(ha1);
|
||||
free(ha2);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static char *webdav_create_digest_auth_header(const char *method, const char *url)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
char *header;
|
||||
char *response;
|
||||
char nonceCount[10];
|
||||
const char *path = url;
|
||||
int count = 0;
|
||||
size_t len = 0;
|
||||
size_t total = 0;
|
||||
|
||||
do
|
||||
{
|
||||
path++;
|
||||
path = strchr(path, '/');
|
||||
count++;
|
||||
} while (count < 3 && *path != '\0');
|
||||
|
||||
response = webdav_create_digest_response(method, path);
|
||||
snprintf(nonceCount, sizeof(nonceCount), "%08x", webdav_st->nc);
|
||||
|
||||
len = strlen("Authorization: Digest ");
|
||||
len += strlen("username=\"") + strlen(webdav_st->username) + strlen("\", ");
|
||||
len += strlen("realm=\"") + strlen(webdav_st->realm) + strlen("\", ");
|
||||
len += strlen("nonce=\"") + strlen(webdav_st->nonce) + strlen("\", ");
|
||||
len += strlen("uri=\"") + strlen(path) + strlen("\", ");
|
||||
len += strlen("nc=\"") + strlen(nonceCount) + strlen("\", ");
|
||||
len += strlen("cnonce=\"") + strlen(webdav_st->cnonce) + strlen("\", ");
|
||||
if (webdav_st->qop_auth)
|
||||
len += strlen("qop=\"auth\", ");
|
||||
if (webdav_st->opaque)
|
||||
len += strlen("opaque=\"") + strlen(webdav_st->opaque) + strlen("\", ");
|
||||
len += strlen("response=\"") + strlen(response) + strlen("\"\r\n");
|
||||
len += 1;
|
||||
|
||||
total = len;
|
||||
len = 0;
|
||||
header = malloc(total);
|
||||
len = strlcpy(header, "Authorization: Digest username=\"", total - len);
|
||||
len += strlcpy(header + len, webdav_st->username, total - len);
|
||||
len += strlcpy(header + len, "\", realm=\"", total - len);
|
||||
len += strlcpy(header + len, webdav_st->realm, total - len);
|
||||
len += strlcpy(header + len, "\", nonce=\"", total - len);
|
||||
len += strlcpy(header + len, webdav_st->nonce, total - len);
|
||||
len += strlcpy(header + len, "\", uri=\"", total - len);
|
||||
len += strlcpy(header + len, path, total - len);
|
||||
len += strlcpy(header + len, "\", nc=\"", total - len);
|
||||
len += strlcpy(header + len, nonceCount, total - len);
|
||||
len += strlcpy(header + len, "\", cnonce=\"", total - len);
|
||||
len += strlcpy(header + len, webdav_st->cnonce, total - len);
|
||||
if (webdav_st->qop_auth)
|
||||
len += strlcpy(header + len, "\", qop=\"auth", total - len);
|
||||
if (webdav_st->opaque)
|
||||
{
|
||||
len += strlcpy(header + len, "\", opaque=\"", total - len);
|
||||
len += strlcpy(header + len, webdav_st->opaque, total - len);
|
||||
}
|
||||
len += strlcpy(header + len, "\", response=\"", total - len);
|
||||
len += strlcpy(header + len, response, total - len);
|
||||
len += strlcpy(header + len, "\"\r\n", total - len);
|
||||
|
||||
free(response);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
static char *webdav_get_auth_header(const char *method, const char *url)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
if (string_is_empty(settings->arrays.webdav_username) &&
|
||||
string_is_empty(settings->arrays.webdav_password))
|
||||
return NULL;
|
||||
|
||||
if (webdav_st->basic)
|
||||
{
|
||||
if (!webdav_st->basic_auth_header)
|
||||
webdav_st->basic_auth_header = webdav_create_basic_auth();
|
||||
return webdav_st->basic_auth_header;
|
||||
}
|
||||
|
||||
if (webdav_st->digest_auth_header)
|
||||
free(webdav_st->digest_auth_header);
|
||||
webdav_st->digest_auth_header = webdav_create_digest_auth_header(method, url);
|
||||
return webdav_st->digest_auth_header;
|
||||
}
|
||||
|
||||
static void webdav_stat_cb(retro_task_t *task, void *task_data, void *user_data, const char *err)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
webdav_cb_state_t *webdav_cb_st = (webdav_cb_state_t *)user_data;
|
||||
http_transfer_data_t *data = (http_transfer_data_t*)task_data;
|
||||
bool success = (data && data->status >= 200 && data->status < 300);
|
||||
|
||||
if (!webdav_cb_st)
|
||||
return;
|
||||
|
||||
if (data && data->status == 401 && data->headers && webdav_st->basic == true)
|
||||
{
|
||||
int i;
|
||||
webdav_st->basic = false;
|
||||
for (i = 0; i < data->headers->size; i++)
|
||||
{
|
||||
if (!string_starts_with(data->headers->elems[i].data, "WWW-Authenticate: Digest "))
|
||||
continue;
|
||||
|
||||
if (webdav_create_digest_auth(data->headers->elems[i].data))
|
||||
{
|
||||
task_push_webdav_stat(webdav_st->url, true,
|
||||
webdav_get_auth_header("OPTIONS", webdav_st->url),
|
||||
webdav_stat_cb, webdav_cb_st);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
webdav_cb_st->cb(webdav_cb_st->user_data, NULL, success, NULL);
|
||||
free(webdav_cb_st);
|
||||
}
|
||||
|
||||
static bool webdav_sync_begin(cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
const char *url = settings->arrays.webdav_url;
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
const char *auth_header;
|
||||
|
||||
if (string_is_empty(url))
|
||||
return false;
|
||||
|
||||
// TODO: LOCK?
|
||||
|
||||
strlcpy(webdav_st->url, url, sizeof(webdav_st->url));
|
||||
fill_pathname_slash(webdav_st->url, sizeof(webdav_st->url));
|
||||
|
||||
/* url/username/password may have changed, redo auth check */
|
||||
webdav_st->basic = true;
|
||||
auth_header = webdav_get_auth_header(NULL, NULL);
|
||||
|
||||
if (auth_header)
|
||||
{
|
||||
webdav_cb_state_t *webdav_cb_st = (webdav_cb_state_t*)calloc(1, sizeof(webdav_cb_state_t));
|
||||
webdav_cb_st->cb = cb;
|
||||
webdav_cb_st->user_data = user_data;
|
||||
task_push_webdav_stat(webdav_st->url, true, auth_header, webdav_stat_cb, webdav_cb_st);
|
||||
}
|
||||
else
|
||||
cb(user_data, NULL, true, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool webdav_sync_end(cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
|
||||
// TODO: UNLOCK?
|
||||
|
||||
if (webdav_st->basic_auth_header)
|
||||
free(webdav_st->basic_auth_header);
|
||||
webdav_st->basic_auth_header = NULL;
|
||||
|
||||
webdav_cleanup_digest();
|
||||
|
||||
cb(user_data, NULL, true, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void webdav_read_cb(retro_task_t *task, void *task_data, void *user_data, const char *err)
|
||||
{
|
||||
webdav_cb_state_t *webdav_cb_st = (webdav_cb_state_t *)user_data;
|
||||
http_transfer_data_t *data = (http_transfer_data_t*)task_data;
|
||||
RFILE *file = NULL;
|
||||
bool success;
|
||||
|
||||
success = (data &&
|
||||
((data->status >= 200 && data->status < 300) || data->status == 404));
|
||||
|
||||
// TODO: it's possible we get a 401 here and need to redo the auth check with this request
|
||||
if (data && data->data && webdav_cb_st)
|
||||
{
|
||||
// TODO: it would be better if writing to the file happened during the network reads
|
||||
file = filestream_open(webdav_cb_st->file,
|
||||
RETRO_VFS_FILE_ACCESS_READ_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (file)
|
||||
{
|
||||
filestream_write(file, data->data, data->len);
|
||||
filestream_seek(file, 0, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
if (webdav_cb_st)
|
||||
{
|
||||
webdav_cb_st->cb(webdav_cb_st->user_data, webdav_cb_st->path, success, file);
|
||||
free(webdav_cb_st);
|
||||
}
|
||||
}
|
||||
|
||||
static bool webdav_read(const char *path, const char *file, cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
webdav_cb_state_t *webdav_cb_st = (webdav_cb_state_t*)calloc(1, sizeof(webdav_cb_state_t));
|
||||
char url[PATH_MAX_LENGTH];
|
||||
char url_encoded[PATH_MAX_LENGTH];
|
||||
|
||||
fill_pathname_join_special(url, webdav_st->url, path, sizeof(url));
|
||||
net_http_urlencode_full(url_encoded, url, sizeof(url_encoded));
|
||||
|
||||
webdav_cb_st->cb = cb;
|
||||
webdav_cb_st->user_data = user_data;
|
||||
strlcpy(webdav_cb_st->path, path, sizeof(webdav_cb_st->path));
|
||||
strlcpy(webdav_cb_st->file, file, sizeof(webdav_cb_st->file));
|
||||
|
||||
task_push_http_transfer_with_headers(url_encoded, true, NULL,
|
||||
webdav_get_auth_header("GET", url_encoded),
|
||||
webdav_read_cb, webdav_cb_st);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void webdav_mkdir_cb(retro_task_t *task, void *task_data, void *user_data, const char *err)
|
||||
{
|
||||
webdav_mkdir_state_t *webdav_mkdir_st = (webdav_mkdir_state_t *)user_data;
|
||||
http_transfer_data_t *data = (http_transfer_data_t*)task_data;
|
||||
|
||||
if (!webdav_mkdir_st)
|
||||
return;
|
||||
|
||||
// TODO: it's possible we get a 401 here and need to redo the auth check with this request
|
||||
if (!data || data->status < 200 || data->status >= 400)
|
||||
{
|
||||
webdav_mkdir_st->cb(false, webdav_mkdir_st->cb_st);
|
||||
free(webdav_mkdir_st);
|
||||
return;
|
||||
}
|
||||
|
||||
*webdav_mkdir_st->last_slash++ = '/';
|
||||
webdav_mkdir_st->last_slash = strchr(webdav_mkdir_st->last_slash, '/');
|
||||
if (webdav_mkdir_st->last_slash)
|
||||
{
|
||||
*webdav_mkdir_st->last_slash = '\0';
|
||||
task_push_webdav_mkdir(webdav_mkdir_st->url, true,
|
||||
webdav_get_auth_header("MVCOL", webdav_mkdir_st->url),
|
||||
webdav_mkdir_cb, webdav_mkdir_st);
|
||||
}
|
||||
else
|
||||
{
|
||||
webdav_mkdir_st->cb(true, webdav_mkdir_st->cb_st);
|
||||
free(webdav_mkdir_st);
|
||||
}
|
||||
}
|
||||
|
||||
static void webdav_ensure_dir(const char *dir, webdav_mkdir_cb_t cb, webdav_cb_state_t *webdav_cb_st)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
webdav_mkdir_state_t *webdav_mkdir_st = (webdav_mkdir_state_t *)malloc(sizeof(webdav_mkdir_state_t));
|
||||
http_transfer_data_t data;
|
||||
char url[PATH_MAX_LENGTH];
|
||||
|
||||
fill_pathname_join_special(url, webdav_st->url, dir, sizeof(url));
|
||||
net_http_urlencode_full(webdav_mkdir_st->url, url, sizeof(webdav_mkdir_st->url));
|
||||
webdav_mkdir_st->last_slash = strchr(webdav_mkdir_st->url + strlen(webdav_st->url) - 1, '/');
|
||||
webdav_mkdir_st->cb = cb;
|
||||
webdav_mkdir_st->cb_st = webdav_cb_st;
|
||||
|
||||
/* this is a recursive callback, set it up so it looks like it's still proceeding */
|
||||
data.status = 200;
|
||||
webdav_mkdir_cb(NULL, &data, webdav_mkdir_st, NULL);
|
||||
}
|
||||
|
||||
static void webdav_update_cb(retro_task_t *task, void *task_data, void *user_data, const char *err)
|
||||
{
|
||||
webdav_cb_state_t *webdav_cb_st = (webdav_cb_state_t *)user_data;
|
||||
http_transfer_data_t *data = (http_transfer_data_t*)task_data;
|
||||
bool success = (data && data->status >= 200 && data->status < 300);
|
||||
|
||||
// TODO: it's possible we get a 401 here and need to redo the auth check with this request
|
||||
if (webdav_cb_st)
|
||||
{
|
||||
webdav_cb_st->cb(webdav_cb_st->user_data, webdav_cb_st->path, success, webdav_cb_st->rfile);
|
||||
free(webdav_cb_st);
|
||||
}
|
||||
}
|
||||
|
||||
static void webdav_do_update(bool success, webdav_cb_state_t *webdav_cb_st)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
char url_encoded[PATH_MAX_LENGTH];
|
||||
char url[PATH_MAX_LENGTH];
|
||||
void *buf;
|
||||
int64_t len;
|
||||
|
||||
if (!webdav_cb_st)
|
||||
return;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
webdav_cb_st->cb(webdav_cb_st->user_data, webdav_cb_st->path, false, webdav_cb_st->rfile);
|
||||
free(webdav_cb_st);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: would be better to read file as it's being written to wire, this is very inefficient
|
||||
len = filestream_get_size(webdav_cb_st->rfile);
|
||||
buf = malloc((size_t)(len + 1));
|
||||
filestream_read(webdav_cb_st->rfile, buf, len);
|
||||
|
||||
fill_pathname_join_special(url, webdav_st->url, webdav_cb_st->path, sizeof(url));
|
||||
net_http_urlencode_full(url_encoded, url, sizeof(url_encoded));
|
||||
|
||||
task_push_webdav_put(url_encoded, buf, len, true,
|
||||
webdav_get_auth_header("PUT", url_encoded),
|
||||
webdav_update_cb, webdav_cb_st);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static bool webdav_update(const char *path, RFILE *rfile, cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
webdav_cb_state_t *webdav_cb_st = (webdav_cb_state_t*)calloc(1, sizeof(webdav_cb_state_t));
|
||||
char dir[PATH_MAX_LENGTH];
|
||||
|
||||
// TODO: if !settings->bools.cloud_sync_destructive, should move to deleted/ first
|
||||
|
||||
webdav_cb_st->cb = cb;
|
||||
webdav_cb_st->user_data = user_data;
|
||||
strlcpy(webdav_cb_st->path, path, sizeof(webdav_cb_st->path));
|
||||
webdav_cb_st->rfile = rfile;
|
||||
|
||||
if (strchr(path, '/'))
|
||||
{
|
||||
fill_pathname_basedir(dir, path, sizeof(dir));
|
||||
webdav_ensure_dir(dir, webdav_do_update, webdav_cb_st);
|
||||
}
|
||||
else
|
||||
webdav_do_update(true, webdav_cb_st);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void webdav_delete_cb(retro_task_t *task, void *task_data, void *user_data, const char *err)
|
||||
{
|
||||
webdav_cb_state_t *webdav_cb_st = (webdav_cb_state_t *)user_data;
|
||||
http_transfer_data_t *data = (http_transfer_data_t*)task_data;
|
||||
bool success = (data != NULL && data->status >= 200 && data->status < 300);
|
||||
|
||||
// TODO: it's possible we get a 401 here and need to redo the auth check with this request
|
||||
if (webdav_cb_st)
|
||||
{
|
||||
webdav_cb_st->cb(webdav_cb_st->user_data, webdav_cb_st->path, success, NULL);
|
||||
free(webdav_cb_st);
|
||||
}
|
||||
}
|
||||
|
||||
static void webdav_backup_cb(retro_task_t *task, void *task_data, void *user_data, const char *err)
|
||||
{
|
||||
webdav_cb_state_t *webdav_cb_st = (webdav_cb_state_t *)user_data;
|
||||
http_transfer_data_t *data = (http_transfer_data_t*)task_data;
|
||||
bool success = (data != NULL && data->status >= 200 && data->status < 300);
|
||||
|
||||
// TODO: it's possible we get a 401 here and need to redo the auth check with this request
|
||||
if (webdav_cb_st)
|
||||
{
|
||||
webdav_cb_st->cb(webdav_cb_st->user_data, webdav_cb_st->path, success, NULL);
|
||||
free(webdav_cb_st);
|
||||
}
|
||||
}
|
||||
|
||||
static void webdav_do_backup(bool success, webdav_cb_state_t *webdav_cb_st)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
char dest_encoded[PATH_MAX_LENGTH];
|
||||
char dest[PATH_MAX_LENGTH];
|
||||
char url_encoded[PATH_MAX_LENGTH];
|
||||
char url[PATH_MAX_LENGTH];
|
||||
size_t len;
|
||||
struct tm tm_;
|
||||
time_t cur_time = time(NULL);
|
||||
|
||||
if (!webdav_cb_st)
|
||||
return;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
webdav_cb_st->cb(webdav_cb_st->user_data, webdav_cb_st->path, false, NULL);
|
||||
free(webdav_cb_st);
|
||||
return;
|
||||
}
|
||||
|
||||
fill_pathname_join_special(url, webdav_st->url, webdav_cb_st->path, sizeof(url));
|
||||
net_http_urlencode_full(url_encoded, url, sizeof(url_encoded));
|
||||
|
||||
fill_pathname_join_special(url, webdav_st->url, "deleted/", sizeof(url));
|
||||
len = fill_pathname_join_special(dest, url, webdav_cb_st->path, sizeof(dest));
|
||||
rtime_localtime(&cur_time, &tm_);
|
||||
strftime(dest + len, sizeof(dest) - len, "-%y%m%d-%H%M%S", &tm_);
|
||||
net_http_urlencode_full(dest_encoded, dest, sizeof(dest_encoded));
|
||||
|
||||
task_push_webdav_move(url_encoded, dest_encoded, true,
|
||||
webdav_get_auth_header("MOVE", url_encoded),
|
||||
webdav_backup_cb, webdav_cb_st);
|
||||
}
|
||||
|
||||
static bool webdav_delete(const char *path, cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
webdav_cb_state_t *webdav_cb_st = (webdav_cb_state_t*)calloc(1, sizeof(webdav_cb_state_t));
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
webdav_cb_st->cb = cb;
|
||||
webdav_cb_st->user_data = user_data;
|
||||
strlcpy(webdav_cb_st->path, path, sizeof(webdav_cb_st->path));
|
||||
|
||||
/*
|
||||
* Should all cloud_sync_destructive handling be done in task_cloudsync? I
|
||||
* think not because it gives each driver a chance to do a move rather than a
|
||||
* delete/update. Or we could add a cloud_sync_move() API to the driver.
|
||||
*/
|
||||
if (settings->bools.cloud_sync_destructive)
|
||||
{
|
||||
webdav_state_t *webdav_st = webdav_state_get_ptr();
|
||||
char url_encoded[PATH_MAX_LENGTH];
|
||||
char url[PATH_MAX_LENGTH];
|
||||
|
||||
fill_pathname_join_special(url, webdav_st->url, path, sizeof(url));
|
||||
net_http_urlencode_full(url_encoded, url, sizeof(url_encoded));
|
||||
|
||||
task_push_webdav_delete(url_encoded, true,
|
||||
webdav_get_auth_header("DELETE", url_encoded),
|
||||
webdav_delete_cb, webdav_cb_st);
|
||||
}
|
||||
else
|
||||
{
|
||||
char dir[PATH_MAX_LENGTH] = {0};
|
||||
size_t _len;
|
||||
_len = strlcat(dir, "deleted/", sizeof(dir));
|
||||
fill_pathname_basedir(dir + _len, path, sizeof(dir) - _len);
|
||||
webdav_ensure_dir(dir, webdav_do_backup, webdav_cb_st);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cloud_sync_driver_t cloud_sync_webdav = {
|
||||
webdav_sync_begin,
|
||||
webdav_sync_end,
|
||||
webdav_read,
|
||||
webdav_update,
|
||||
webdav_delete,
|
||||
"webdav" /* ident */
|
||||
};
|
126
network/cloud_sync_driver.c
Normal file
126
network/cloud_sync_driver.c
Normal file
@ -0,0 +1,126 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cloud_sync_driver.h"
|
||||
#include "../list_special.h"
|
||||
#include "../retroarch.h"
|
||||
#include "../verbosity.h"
|
||||
|
||||
static cloud_sync_driver_t cloud_sync_null = {
|
||||
NULL, /* sync_begin */
|
||||
NULL, /* sync_end */
|
||||
NULL, /* read */
|
||||
NULL, /* update */
|
||||
NULL, /* delete */
|
||||
"null" /* ident */
|
||||
};
|
||||
|
||||
const cloud_sync_driver_t *cloud_sync_drivers[] = {
|
||||
&cloud_sync_webdav,
|
||||
&cloud_sync_null,
|
||||
NULL
|
||||
};
|
||||
|
||||
static cloud_sync_driver_state_t cloud_sync_driver_st = {0};
|
||||
|
||||
cloud_sync_driver_state_t *cloud_sync_state_get_ptr(void)
|
||||
{
|
||||
return &cloud_sync_driver_st;
|
||||
}
|
||||
|
||||
/**
|
||||
* config_get_cloud_sync_driver_options:
|
||||
*
|
||||
* Get an enumerated list of all cloud sync driver names, separated by '|'.
|
||||
*
|
||||
* @return string listing of all cloud sync driver names, separated by '|'.
|
||||
**/
|
||||
const char* config_get_cloud_sync_driver_options(void)
|
||||
{
|
||||
return char_list_new_special(STRING_LIST_CLOUD_SYNC_DRIVERS, NULL);
|
||||
}
|
||||
|
||||
void cloud_sync_find_driver(
|
||||
settings_t *settings,
|
||||
const char *prefix,
|
||||
bool verbosity_enabled)
|
||||
{
|
||||
cloud_sync_driver_state_t
|
||||
*cloud_sync_st = &cloud_sync_driver_st;
|
||||
int i = (int)driver_find_index(
|
||||
"cloud_sync_driver",
|
||||
settings->arrays.cloud_sync_driver);
|
||||
|
||||
if (i >= 0)
|
||||
cloud_sync_st->driver = (const cloud_sync_driver_t*)
|
||||
cloud_sync_drivers[i];
|
||||
else
|
||||
{
|
||||
if (verbosity_enabled)
|
||||
{
|
||||
unsigned d;
|
||||
RARCH_ERR("Couldn't find any %s named \"%s\"\n", prefix,
|
||||
settings->arrays.cloud_sync_driver);
|
||||
|
||||
RARCH_LOG_OUTPUT("Available %ss are:\n", prefix);
|
||||
for (d = 0; cloud_sync_drivers[d]; d++)
|
||||
RARCH_LOG_OUTPUT("\t%s\n", cloud_sync_drivers[d]->ident);
|
||||
|
||||
RARCH_WARN("Going to default to first %s...\n", prefix);
|
||||
}
|
||||
|
||||
cloud_sync_st->driver = (const cloud_sync_driver_t*)cloud_sync_drivers[0];
|
||||
}
|
||||
}
|
||||
|
||||
bool cloud_sync_begin(cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
const cloud_sync_driver_t *driver = cloud_sync_state_get_ptr()->driver;
|
||||
if (driver && driver->sync_begin)
|
||||
return driver->sync_begin(cb, user_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cloud_sync_end(cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
const cloud_sync_driver_t *driver = cloud_sync_state_get_ptr()->driver;
|
||||
if (driver && driver->sync_end)
|
||||
return driver->sync_end(cb, user_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cloud_sync_read(const char *path, const char *file, cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
const cloud_sync_driver_t *driver = cloud_sync_state_get_ptr()->driver;
|
||||
if (driver && driver->read)
|
||||
return driver->read(path, file, cb, user_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cloud_sync_update(const char *path, RFILE *file,
|
||||
cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
const cloud_sync_driver_t *driver = cloud_sync_state_get_ptr()->driver;
|
||||
if (driver && driver->update)
|
||||
return driver->update(path, file, cb, user_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cloud_sync_delete(const char *path, cloud_sync_complete_handler_t cb, void *user_data)
|
||||
{
|
||||
const cloud_sync_driver_t *driver = cloud_sync_state_get_ptr()->driver;
|
||||
if (driver && driver->delete)
|
||||
return driver->delete(path, cb, user_data);
|
||||
return false;
|
||||
}
|
77
network/cloud_sync_driver.h
Normal file
77
network/cloud_sync_driver.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
*
|
||||
* RetroArch 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 Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch 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 RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __CLOUD_SYNC_DRIVER__H
|
||||
#define __CLOUD_SYNC_DRIVER__H
|
||||
|
||||
#include <boolean.h>
|
||||
#include <stddef.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#include "../configuration.h"
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/*
|
||||
* For a read, `success' indicates whether we successfully communicated with the
|
||||
* server. We may ask to read a file that doesn't exist; in that case, `success'
|
||||
* is true and `file' is NULL. `file' is expected to be close()'d by the handler
|
||||
* if non-NULL.
|
||||
*/
|
||||
typedef void (*cloud_sync_complete_handler_t)(void *user_data, const char *path, bool success, RFILE *file);
|
||||
|
||||
typedef struct cloud_sync_driver
|
||||
{
|
||||
bool (*sync_begin)(cloud_sync_complete_handler_t cb, void *user_data);
|
||||
bool (*sync_end)(cloud_sync_complete_handler_t cb, void *user_data);
|
||||
|
||||
bool (*read)(const char *path, const char *file, cloud_sync_complete_handler_t cb, void *user_data);
|
||||
bool (*update)(const char *path, RFILE *file, cloud_sync_complete_handler_t cb, void *user_data);
|
||||
bool (*delete)(const char *path, cloud_sync_complete_handler_t cb, void *user_data);
|
||||
|
||||
const char *ident;
|
||||
} cloud_sync_driver_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const cloud_sync_driver_t *driver;
|
||||
} cloud_sync_driver_state_t;
|
||||
|
||||
cloud_sync_driver_state_t *cloud_sync_state_get_ptr(void);
|
||||
|
||||
extern cloud_sync_driver_t cloud_sync_webdav;
|
||||
|
||||
extern const cloud_sync_driver_t *cloud_sync_drivers[];
|
||||
|
||||
/**
|
||||
* config_get_cloud_sync_driver_options:
|
||||
*
|
||||
* Get an enumerated list of all cloud_sync driver names, separated by '|'.
|
||||
*
|
||||
* Returns: string listing of all cloud_sync driver names, separated by '|'.
|
||||
**/
|
||||
const char* config_get_cloud_sync_driver_options(void);
|
||||
|
||||
void cloud_sync_find_driver(settings_t *settings, const char *prefix, bool verbosity_enabled);
|
||||
|
||||
bool cloud_sync_begin(cloud_sync_complete_handler_t cb, void *user_data);
|
||||
bool cloud_sync_end(cloud_sync_complete_handler_t cb, void *user_data);
|
||||
|
||||
bool cloud_sync_read(const char *path, const char *file, cloud_sync_complete_handler_t cb, void *user_data);
|
||||
bool cloud_sync_update(const char *path, RFILE *file, cloud_sync_complete_handler_t cb, void *user_data);
|
||||
bool cloud_sync_delete(const char *path, cloud_sync_complete_handler_t cb, void *user_data);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
@ -18,6 +18,7 @@ OTHER_CFLAGS = $(inherited) -DHAVE_CC_RESAMPLER
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_CHD
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_CHEATS
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_CHEEVOS
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_CLOUDSYNC
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_COCOA_METAL
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_COMMAND
|
||||
OTHER_CFLAGS = $(inherited) -DHAVE_CONFIGFILE
|
||||
|
@ -1596,6 +1596,7 @@
|
||||
"-DHAVE_CC_RESAMPLER",
|
||||
"-DHAVE_CHEATS",
|
||||
"-DHAVE_CHEEVOS",
|
||||
"-DHAVE_CLOUDSYNC",
|
||||
"-DHAVE_COCOATOUCH",
|
||||
"-DHAVE_COCOA_METAL",
|
||||
"-DHAVE_CONFIGFILE",
|
||||
@ -1743,6 +1744,7 @@
|
||||
"-DHAVE_CC_RESAMPLER",
|
||||
"-DHAVE_CHEATS",
|
||||
"-DHAVE_CHEEVOS",
|
||||
"-DHAVE_CLOUDSYNC",
|
||||
"-DHAVE_COCOATOUCH",
|
||||
"-DHAVE_COCOA_METAL",
|
||||
"-DHAVE_CONFIGFILE",
|
||||
@ -1918,6 +1920,7 @@
|
||||
"-DHAVE_CC_RESAMPLER",
|
||||
"-DHAVE_CHEATS",
|
||||
"-DHAVE_CHEEVOS",
|
||||
"-DHAVE_CLOUDSYNC",
|
||||
"-DHAVE_COCOATOUCH",
|
||||
"-DHAVE_COCOA_METAL",
|
||||
"-DHAVE_CONFIGFILE",
|
||||
@ -2082,6 +2085,7 @@
|
||||
"-DHAVE_CC_RESAMPLER",
|
||||
"-DHAVE_CHEATS",
|
||||
"-DHAVE_CHEEVOS",
|
||||
"-DHAVE_CLOUDSYNC",
|
||||
"-DHAVE_COCOATOUCH",
|
||||
"-DHAVE_COCOA_METAL",
|
||||
"-DHAVE_CONFIGFILE",
|
||||
|
36
retroarch.c
36
retroarch.c
@ -164,6 +164,9 @@
|
||||
#ifdef HAVE_WIFI
|
||||
#include "network/wifi_driver.h"
|
||||
#endif
|
||||
#ifdef HAVE_CLOUDSYNC
|
||||
#include "network/cloud_sync_driver.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
@ -498,6 +501,18 @@ static const void *find_driver_nonempty(
|
||||
return wifi_drivers[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_CLOUDSYNC
|
||||
else if (string_is_equal(label, "cloud_sync_driver"))
|
||||
{
|
||||
if (cloud_sync_drivers[i])
|
||||
{
|
||||
const char *ident = cloud_sync_drivers[i]->ident;
|
||||
|
||||
strlcpy(s, ident, len);
|
||||
return cloud_sync_drivers[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
@ -1606,6 +1621,17 @@ struct string_list *string_list_new_special(enum string_list_type type,
|
||||
string_list_append(s, opt, attr);
|
||||
}
|
||||
break;
|
||||
case STRING_LIST_CLOUD_SYNC_DRIVERS:
|
||||
#ifdef HAVE_CLOUDSYNC
|
||||
for (i = 0; cloud_sync_drivers[i]; i++)
|
||||
{
|
||||
const char *opt = cloud_sync_drivers[i]->ident;
|
||||
*len += STRLEN_CONST(cloud_sync_drivers[i]->ident) + 1;
|
||||
|
||||
string_list_append(s, opt, attr);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
#ifdef HAVE_LAKKA
|
||||
case STRING_LIST_TIMEZONES:
|
||||
{
|
||||
@ -2796,6 +2822,9 @@ bool command_event(enum event_command cmd, void *data)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_CLOUDSYNC
|
||||
task_push_cloud_sync();
|
||||
#endif
|
||||
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
||||
runloop_st->runtime_shader_preset_path[0] = '\0';
|
||||
#endif
|
||||
@ -4883,6 +4912,9 @@ int rarch_main(int argc, char *argv[], void *data)
|
||||
}
|
||||
|
||||
ui_companion_driver_init_first();
|
||||
#if HAVE_CLOUDSYNC
|
||||
task_push_cloud_sync();
|
||||
#endif
|
||||
#if !defined(HAVE_MAIN) || defined(HAVE_QT)
|
||||
for (;;)
|
||||
{
|
||||
@ -6519,6 +6551,10 @@ bool retroarch_main_init(int argc, char *argv[])
|
||||
#endif
|
||||
#ifdef HAVE_WIFI
|
||||
wifi_driver_ctl(RARCH_WIFI_CTL_FIND_DRIVER, NULL);
|
||||
#endif
|
||||
#ifdef HAVE_CLOUDSYNC
|
||||
cloud_sync_find_driver(settings,
|
||||
"cloud sync driver", verbosity_enabled);
|
||||
#endif
|
||||
location_driver_find_driver(settings,
|
||||
"location driver", verbosity_enabled);
|
||||
|
1064
tasks/task_cloudsync.c
Normal file
1064
tasks/task_cloudsync.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -177,6 +177,7 @@ task_finished:
|
||||
{
|
||||
size_t len = 0;
|
||||
char *tmp = (char*)net_http_data(http->handle, &len, false);
|
||||
struct string_list *headers = net_http_headers(http->handle);
|
||||
|
||||
if (tmp && http->cb)
|
||||
http->cb(tmp, len);
|
||||
@ -188,16 +189,18 @@ task_finished:
|
||||
{
|
||||
if (tmp)
|
||||
free(tmp);
|
||||
string_list_free(headers);
|
||||
|
||||
task_set_error(task,
|
||||
strldup("Task cancelled.", sizeof("Task cancelled.")));
|
||||
}
|
||||
else
|
||||
{
|
||||
data = (http_transfer_data_t*)malloc(sizeof(*data));
|
||||
data->data = tmp;
|
||||
data->len = len;
|
||||
data->status = net_http_status(http->handle);
|
||||
data = (http_transfer_data_t*)malloc(sizeof(*data));
|
||||
data->data = tmp;
|
||||
data->headers = headers;
|
||||
data->len = len;
|
||||
data->status = net_http_status(http->handle);
|
||||
|
||||
task_set_data(task, data);
|
||||
|
||||
@ -219,6 +222,7 @@ static void task_http_transfer_cleanup(retro_task_t *task)
|
||||
http_transfer_data_t* data = (http_transfer_data_t*)task_get_data(task);
|
||||
if (data)
|
||||
{
|
||||
string_list_free(data->headers);
|
||||
if (data->data)
|
||||
free(data->data);
|
||||
free(data);
|
||||
@ -346,6 +350,112 @@ void* task_push_http_transfer(const char *url, bool mute,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *task_push_webdav_stat(const char *url, bool mute, const char *headers,
|
||||
retro_task_callback_t cb, void *user_data)
|
||||
{
|
||||
struct http_connection_t *conn;
|
||||
|
||||
if (string_is_empty(url))
|
||||
return NULL;
|
||||
|
||||
if (!(conn = net_http_connection_new(url, "OPTIONS", NULL)))
|
||||
return NULL;
|
||||
|
||||
if (headers)
|
||||
net_http_connection_set_headers(conn, headers);
|
||||
|
||||
return task_push_http_transfer_generic(conn, url, mute, NULL, cb, user_data);
|
||||
}
|
||||
|
||||
void* task_push_webdav_mkdir(const char *url, bool mute,
|
||||
const char *headers,
|
||||
retro_task_callback_t cb, void *user_data)
|
||||
{
|
||||
struct http_connection_t *conn;
|
||||
|
||||
if (string_is_empty(url))
|
||||
return NULL;
|
||||
|
||||
if (!(conn = net_http_connection_new(url, "MKCOL", NULL)))
|
||||
return NULL;
|
||||
|
||||
if (headers)
|
||||
net_http_connection_set_headers(conn, headers);
|
||||
|
||||
return task_push_http_transfer_generic(conn, url, mute, NULL, cb, user_data);
|
||||
}
|
||||
|
||||
void* task_push_webdav_put(const char *url,
|
||||
const void *put_data, size_t len, bool mute,
|
||||
const char *headers, retro_task_callback_t cb, void *user_data)
|
||||
{
|
||||
struct http_connection_t *conn;
|
||||
char expect[1024];
|
||||
size_t _len;
|
||||
|
||||
if (string_is_empty(url))
|
||||
return NULL;
|
||||
|
||||
if (!(conn = net_http_connection_new(url, "PUT", NULL)))
|
||||
return NULL;
|
||||
|
||||
_len = strlcpy(expect, "Expect: 100-continue\r\n", sizeof(expect));
|
||||
if (headers)
|
||||
{
|
||||
strlcpy(expect + _len, headers, sizeof(expect) - _len);
|
||||
net_http_connection_set_headers(conn, expect);
|
||||
}
|
||||
|
||||
if (put_data)
|
||||
net_http_connection_set_content(conn, NULL, len, put_data);
|
||||
|
||||
return task_push_http_transfer_generic(conn, url, mute, NULL, cb, user_data);
|
||||
}
|
||||
|
||||
void* task_push_webdav_delete(const char *url, bool mute,
|
||||
const char *headers,
|
||||
retro_task_callback_t cb, void *user_data)
|
||||
{
|
||||
struct http_connection_t *conn;
|
||||
|
||||
if (string_is_empty(url))
|
||||
return NULL;
|
||||
|
||||
if (!(conn = net_http_connection_new(url, "DELETE", NULL)))
|
||||
return NULL;
|
||||
|
||||
if (headers)
|
||||
net_http_connection_set_headers(conn, headers);
|
||||
|
||||
return task_push_http_transfer_generic(conn, url, mute, NULL, cb, user_data);
|
||||
}
|
||||
|
||||
void *task_push_webdav_move(const char *url,
|
||||
const char *dest, bool mute, const char *headers,
|
||||
retro_task_callback_t cb, void *userdata)
|
||||
{
|
||||
struct http_connection_t *conn;
|
||||
char dest_header[PATH_MAX_LENGTH + 512];
|
||||
size_t len;
|
||||
|
||||
if (string_is_empty(url))
|
||||
return NULL;
|
||||
|
||||
if (!(conn = net_http_connection_new(url, "MOVE", NULL)))
|
||||
return NULL;
|
||||
|
||||
len = strlcpy(dest_header, "Destination: ", sizeof(dest_header));
|
||||
len += strlcpy(dest_header + len, dest, sizeof(dest_header) - len);
|
||||
len += strlcpy(dest_header + len, "\r\n", sizeof(dest_header) - len);
|
||||
|
||||
if (headers)
|
||||
strlcpy(dest_header + len, headers, sizeof(dest_header) - len);
|
||||
|
||||
net_http_connection_set_headers(conn, dest_header);
|
||||
|
||||
return task_push_http_transfer_generic(conn, url, mute, NULL, cb, userdata);
|
||||
}
|
||||
|
||||
void* task_push_http_transfer_file(const char* url, bool mute,
|
||||
const char* type,
|
||||
retro_task_callback_t cb, file_transfer_t* transfer_data)
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <boolean.h>
|
||||
#include <retro_common_api.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <lists/string_list.h>
|
||||
|
||||
#include <queues/task_queue.h>
|
||||
#include <gfx/scaler/scaler.h>
|
||||
@ -85,6 +86,7 @@ struct screenshot_task_state
|
||||
typedef struct
|
||||
{
|
||||
char *data;
|
||||
struct string_list *headers;
|
||||
size_t len;
|
||||
int status;
|
||||
} http_transfer_data_t;
|
||||
@ -109,6 +111,17 @@ void *task_push_http_post_transfer_with_headers(const char *url, const char *pos
|
||||
|
||||
task_retriever_info_t *http_task_get_transfer_list(void);
|
||||
|
||||
void *task_push_webdav_stat(const char *url, bool mute, const char *headers,
|
||||
retro_task_callback_t cb, void *userdata);
|
||||
void *task_push_webdav_mkdir(const char *url, bool mute, const char *headers,
|
||||
retro_task_callback_t cb, void *userdata);
|
||||
void *task_push_webdav_put(const char *url, const void *put_data, size_t len, bool mute, const char *headers,
|
||||
retro_task_callback_t cb, void *userdata);
|
||||
void *task_push_webdav_delete(const char *url, bool mute, const char *headers,
|
||||
retro_task_callback_t cb, void *userdata);
|
||||
void *task_push_webdav_move(const char *url, const char *dest, bool mute, const char *headers,
|
||||
retro_task_callback_t cb, void *userdata);
|
||||
|
||||
bool task_push_bluetooth_scan(retro_task_callback_t cb);
|
||||
|
||||
bool task_push_wifi_scan(retro_task_callback_t cb);
|
||||
@ -292,6 +305,9 @@ void menu_explore_wait_for_init_task(void);
|
||||
|
||||
extern const char* const input_builtin_autoconfs[];
|
||||
|
||||
/* cloud sync tasks */
|
||||
void task_push_cloud_sync(void);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user