diff --git a/configuration.c b/configuration.c index 1a9917de6ebe..5d76f2481137 100644 --- a/configuration.c +++ b/configuration.c @@ -1619,10 +1619,17 @@ static struct config_array_setting *populate_settings_array( #ifdef HAVE_NETWORKING SETTING_ARRAY("netplay_mitm_server", settings->arrays.netplay_mitm_server, false, NULL, true); +#ifdef HAVE_CLOUDSYNC 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("google_drive_refresh_token", settings->arrays.google_drive_refresh_token, false, NULL, true); +#ifdef HAVE_S3 + SETTING_ARRAY("s3_url", settings->arrays.s3_url, false, NULL, true); + SETTING_ARRAY("access_key_id", settings->arrays.access_key_id, false, NULL, true); + SETTING_ARRAY("secret_access_key", settings->arrays.secret_access_key, false, NULL, true); +#endif +#endif 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); diff --git a/configuration.h b/configuration.h index 3225a2aae2ef..82fbc33d826c 100644 --- a/configuration.h +++ b/configuration.h @@ -541,10 +541,19 @@ typedef struct settings char audio_device[NAME_MAX_LENGTH]; char camera_device[NAME_MAX_LENGTH]; char netplay_mitm_server[NAME_MAX_LENGTH]; +#ifdef HAVE_NETWORKING +#ifdef HAVE_CLOUDSYNC char webdav_url[NAME_MAX_LENGTH]; char webdav_username[NAME_MAX_LENGTH]; char webdav_password[NAME_MAX_LENGTH]; char google_drive_refresh_token[2048]; +#ifdef HAVE_S3 + char s3_url[NAME_MAX_LENGTH]; + char access_key_id[128]; + char secret_access_key[186]; /* TODO/RESEARCH - check size, ex https://github.com/winscp/winscp/pull/15/files */ +#endif +#endif +#endif char crt_switch_timings[NAME_MAX_LENGTH]; char input_reserved_devices[MAX_USERS][NAME_MAX_LENGTH]; diff --git a/griffin/griffin.c b/griffin/griffin.c index 8b84bc0ad3d6..2a3479835e2b 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1721,6 +1721,9 @@ CLOUD SYNC #ifdef HAVE_SMBCLIENT #include "../network/cloud_sync/smb.c" #endif +#ifdef HAVE_S3 +#include "../network/cloud_sync/s3.c" +#endif #endif /*============================================================ diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 383fcb3370f3..b3b0b32db9c2 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -3550,6 +3550,18 @@ MSG_HASH( MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD, "cloud_sync_password" ) +MSG_HASH( + MENU_ENUM_LABEL_CLOUD_SYNC_ACCESS_KEY_ID, + "cloud_sync_access_key_id" + ) +MSG_HASH( + MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY, + "cloud_sync_secret_access_key" + ) +MSG_HASH( + MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL, + "cloud_sync_s3_url" + ) MSG_HASH( MENU_ENUM_LABEL_SCAN_DIRECTORY, "scan_directory" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 81cde5d6cec4..ad20e4843531 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1158,6 +1158,30 @@ MSG_HASH( MENU_ENUM_SUBLABEL_CLOUD_SYNC_PASSWORD, "Your password for your cloud storage account." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_ACCESS_KEY_ID, + "Access Key ID" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_CLOUD_SYNC_ACCESS_KEY_ID, + "Your access key ID for your cloud storage account." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_SECRET_ACCESS_KEY, + "Secret Access Key" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_CLOUD_SYNC_SECRET_ACCESS_KEY, + "Your secret access key for your cloud storage account." + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_S3_URL, + "S3 URL" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_CLOUD_SYNC_S3_URL, + "Your S3 endpoint URL for cloud storage." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_LOGGING_SETTINGS, "Logging" diff --git a/libretro-common/include/net/net_http.h b/libretro-common/include/net/net_http.h index 7b68ddd6304d..a1e5c18792fe 100644 --- a/libretro-common/include/net/net_http.h +++ b/libretro-common/include/net/net_http.h @@ -34,6 +34,43 @@ RETRO_BEGIN_DECLS struct http_t; struct http_connection_t; +typedef enum net_http_phase +{ + NET_HTTP_PHASE_CONNECT = 0, + NET_HTTP_PHASE_SEND, + NET_HTTP_PHASE_RECV_HEADER, + NET_HTTP_PHASE_RECV_BODY, + NET_HTTP_PHASE_REDIRECT, + NET_HTTP_PHASE_DONE, + NET_HTTP_PHASE_ERROR +} net_http_phase_t; + +const char *net_http_phase_to_string(net_http_phase_t phase); + +/* Caller-facing, point-in-time HTTP transport snapshot. + * + * Notes: + * - Some fields intentionally mirror live http_t state (e.g. status/fd/err) + * so callers can log a single self-contained payload without issuing + * additional net_http_* calls. + * - Snapshot data is safe to copy and retain after transfer completion, + * unlike the opaque internal state which may be freed. */ +typedef struct net_http_debug_state +{ + int fd; + int status; + int last_errno; + int64_t last_io_len; + size_t progress; + size_t total; + unsigned redirects; + bool ssl; + bool connected; + bool request_sent; + bool err; + net_http_phase_t phase; +} net_http_debug_state_t; + struct http_connection_t *net_http_connection_new(const char *url, const char *method, const char *data); /** @@ -96,6 +133,12 @@ int net_http_status(struct http_t *state); **/ bool net_http_error(struct http_t *state); +/* Copies the current transport snapshot into @out. + * + * Returns false if inputs are invalid; true on success. + * The returned snapshot is independent from internal http_t lifetime. */ +bool net_http_get_debug_state(struct http_t *state, net_http_debug_state_t *out); + /** * net_http_headers: * @@ -106,6 +149,7 @@ bool net_http_error(struct http_t *state); * If the status is not 20x and accept_error is false, it returns NULL. **/ struct string_list *net_http_headers(struct http_t *state); +struct string_list *net_http_headers_ex(struct http_t *state, bool accept_error); /** * net_http_data: diff --git a/libretro-common/net/net_http.c b/libretro-common/net/net_http.c index c43c238afedf..3e8e956b3147 100644 --- a/libretro-common/net/net_http.c +++ b/libretro-common/net/net_http.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -115,6 +116,7 @@ struct http_t request_t request; response_t response; + net_http_debug_state_t debug_state; }; struct http_connection_t @@ -133,6 +135,58 @@ struct http_connection_t bool ssl; }; +const char *net_http_phase_to_string(net_http_phase_t phase) +{ + switch (phase) + { + case NET_HTTP_PHASE_CONNECT: + return "connect"; + case NET_HTTP_PHASE_SEND: + return "send"; + case NET_HTTP_PHASE_RECV_HEADER: + return "recv_header"; + case NET_HTTP_PHASE_RECV_BODY: + return "recv_body"; + case NET_HTTP_PHASE_REDIRECT: + return "redirect"; + case NET_HTTP_PHASE_DONE: + return "done"; + case NET_HTTP_PHASE_ERROR: + return "error"; + default: + return "unknown"; + } +} + +static void net_http_debug_sync(struct http_t *state) +{ + if (!state) + return; + + state->debug_state.ssl = state->ssl; + state->debug_state.request_sent = state->request_sent; + state->debug_state.err = state->err; + state->debug_state.status = state->response.status; + state->debug_state.connected = state->conn ? state->conn->connected : false; + state->debug_state.fd = state->conn ? state->conn->fd : -1; +} + +static void net_http_debug_set_phase(struct http_t *state, net_http_phase_t phase) +{ + if (!state) + return; + state->debug_state.phase = phase; +} + +static void net_http_debug_set_io(struct http_t *state, ssize_t io_len) +{ + if (!state) + return; + + state->debug_state.last_io_len = (int64_t)io_len; + state->debug_state.last_errno = errno; +} + struct dns_cache_entry { char *domain; @@ -849,6 +903,15 @@ struct http_t *net_http_new(struct http_connection_t *conn) state->response.buflen = 64 * 1024; /* Start with larger buffer to reduce reallocations */ state->response.data = (char*)malloc(state->response.buflen); state->response.headers = string_list_new(); + state->debug_state.fd = -1; + state->debug_state.status = -1; + state->debug_state.last_errno = 0; + state->debug_state.last_io_len = -1; + state->debug_state.progress = 0; + state->debug_state.total = 0; + state->debug_state.redirects = 0; + state->debug_state.phase = NET_HTTP_PHASE_CONNECT; + net_http_debug_sync(state); return state; } @@ -920,13 +983,25 @@ static bool net_http_new_socket(struct http_t *state) int fd; if (!entry->addr) { + net_http_debug_set_io(state, -1); + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); UNLOCK_DNS_CACHE(); return false; } addr = entry->addr; fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (fd >= 0) + { state->conn = net_http_conn_pool_add(state->request.domain, state->request.port, fd, state->ssl); + net_http_debug_sync(state); + } + else + { + net_http_debug_set_io(state, -1); + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); + } /* still waiting on thread */ UNLOCK_DNS_CACHE(); return (fd >= 0); @@ -969,12 +1044,19 @@ static bool net_http_connect(struct http_t *state) if (state->ssl) { if (!conn) + { + net_http_debug_set_io(state, -1); + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); return false; - + } for (next_addr = addr; conn->fd >= 0; conn->fd = socket_next((void**)&next_addr)) { if (!(conn->ssl_ctx = ssl_socket_init(conn->fd, state->request.domain))) { + net_http_debug_set_io(state, -1); + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); socket_close(conn->fd); break; } @@ -992,6 +1074,9 @@ static bool net_http_connect(struct http_t *state) if (ssl_socket_connect(conn->ssl_ctx, next_addr, timeout, true) < 0) { + net_http_debug_set_io(state, -1); + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); ssl_socket_close(conn->ssl_ctx); ssl_socket_free(conn->ssl_ctx); conn->ssl_ctx = NULL; @@ -999,6 +1084,8 @@ static bool net_http_connect(struct http_t *state) else { conn->connected = true; + net_http_debug_set_phase(state, NET_HTTP_PHASE_SEND); + net_http_debug_sync(state); return true; } } @@ -1016,9 +1103,14 @@ static bool net_http_connect(struct http_t *state) if (socket_connect_with_timeout(conn->fd, next_addr, 5000)) { conn->connected = true; + net_http_debug_set_phase(state, NET_HTTP_PHASE_SEND); + net_http_debug_sync(state); return true; } + net_http_debug_set_io(state, -1); + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); socket_close(conn->fd); } conn->fd = -1; /* already closed */ @@ -1039,14 +1131,24 @@ static void net_http_send_str( { if (!ssl_socket_send_all_blocking( state->conn->ssl_ctx, text, text_size, true)) + { state->err = true; + net_http_debug_set_io(state, -1); + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); + } } else #endif { if (!socket_send_all_blocking( state->conn->fd, text, text_size, true)) + { state->err = true; + net_http_debug_set_io(state, -1); + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); + } } } @@ -1097,9 +1199,14 @@ static bool net_http_send_request(struct http_t *state) size_t _len, len; char *len_str = NULL; - if (!request->postdata && !string_is_equal(request->method, "PUT")) + if (!request->postdata && + !string_is_equal(request->method, "PUT") && + request->contentlength > 0) { state->err = true; + net_http_debug_set_io(state, -1); + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); return true; } @@ -1143,6 +1250,8 @@ static bool net_http_send_request(struct http_t *state) request->contentlength); state->request_sent = true; + net_http_debug_set_phase(state, NET_HTTP_PHASE_RECV_HEADER); + net_http_debug_sync(state); return state->err; } @@ -1186,10 +1295,13 @@ static ssize_t net_http_receive_header(struct http_t *state, ssize_t len) { response->part = P_DONE; state->err = true; + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); return -1; } response->status = (int)strtoul(response->data + STRLEN_CONST("HTTP/1.1 "), NULL, 10); + net_http_debug_sync(state); response->part = P_HEADER; } else @@ -1404,6 +1516,9 @@ static bool net_http_redirect(struct http_t *state, const char *location) /* after this, assume location is invalid */ string_list_deinitialize(state->response.headers); string_list_initialize(state->response.headers); + state->debug_state.redirects++; + net_http_debug_set_phase(state, NET_HTTP_PHASE_REDIRECT); + net_http_debug_sync(state); /* keep going */ return false; } @@ -1422,26 +1537,43 @@ bool net_http_update(struct http_t *state, size_t* progress, size_t* total) if (!state || state->err) return true; + net_http_debug_sync(state); + if (!state->conn) { + net_http_debug_set_phase(state, NET_HTTP_PHASE_CONNECT); state->conn = net_http_conn_pool_find(state->request.domain, state->request.port); if (!state->conn) { if (!net_http_new_socket(state)) state->err = true; + if (state->err) + { + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); + } return state->err; } + net_http_debug_sync(state); } if (!state->conn->connected) { if (!net_http_connect(state)) state->err = true; + if (state->err) + { + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); + net_http_debug_sync(state); + } return state->err; } if (!state->request_sent) + { + net_http_debug_set_phase(state, NET_HTTP_PHASE_SEND); return net_http_send_request(state); + } response = (struct response*)&state->response; @@ -1455,36 +1587,49 @@ bool net_http_update(struct http_t *state, size_t* progress, size_t* total) _len = socket_receive_all_nonblocking(state->conn->fd, &state->err, (uint8_t*)response->data + response->pos, response->buflen - response->pos); + net_http_debug_set_io(state, _len); if (response->part < P_BODY) { + net_http_debug_set_phase(state, NET_HTTP_PHASE_RECV_HEADER); if (_len < 0 || state->err) { + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); net_http_conn_pool_remove(state->conn); state->conn = NULL; state->err = true; response->part = P_DONE; response->status = -1; + net_http_debug_sync(state); return true; } _len = net_http_receive_header(state, _len); + net_http_debug_set_io(state, _len); } if (response->part >= P_BODY && response->part < P_DONE) { + net_http_debug_set_phase(state, NET_HTTP_PHASE_RECV_BODY); if (!net_http_receive_body(state, _len)) { + net_http_debug_set_phase(state, NET_HTTP_PHASE_ERROR); net_http_conn_pool_remove(state->conn); state->conn = NULL; state->err = true; response->part = P_DONE; response->status = -1; + net_http_debug_sync(state); return true; } } if (progress) + { *progress = response->pos; + state->debug_state.progress = *progress; + } + else + state->debug_state.progress = response->pos; if (total) { @@ -1492,10 +1637,16 @@ bool net_http_update(struct http_t *state, size_t* progress, size_t* total) *total = response->len; else *total = 0; + state->debug_state.total = *total; } + else + state->debug_state.total = (response->bodytype == T_LEN) ? response->len : 0; if (response->part != P_DONE) + { + net_http_debug_sync(state); return false; + } for (_len = 0; (size_t)_len < response->headers->size; _len++) { @@ -1520,6 +1671,8 @@ bool net_http_update(struct http_t *state, size_t* progress, size_t* total) } } + net_http_debug_set_phase(state, NET_HTTP_PHASE_DONE); + net_http_debug_sync(state); return true; } @@ -1548,13 +1701,20 @@ int net_http_status(struct http_t *state) * caller of net_http_new; it is not freed by net_http_delete(). * If the status is not 20x and accept_err is false, it returns NULL. **/ -struct string_list *net_http_headers(struct http_t *state) +struct string_list *net_http_headers_ex(struct http_t *state, bool accept_err) { - if (!state || !state->err) + if (!state) + return NULL; + if (!accept_err && !state->err) return NULL; return state->response.headers; } +struct string_list *net_http_headers(struct http_t *state) +{ + return net_http_headers_ex(state, false); +} + /** * net_http_data: * @@ -1582,6 +1742,16 @@ uint8_t* net_http_data(struct http_t *state, size_t* len, bool accept_err) return (uint8_t*)state->response.data; } +bool net_http_get_debug_state(struct http_t *state, net_http_debug_state_t *out) +{ + if (!state || !out) + return false; + + net_http_debug_sync(state); + *out = state->debug_state; + return true; +} + /** * net_http_delete: * diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 4904e423d9a1..de2d9d2cdd08 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -270,8 +270,11 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_sync_thumbs, MENU_ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_sync_system, MENU_ENUM_SUBLABEL_CLOUD_SYNC_SYNC_SYSTEM) 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_s3_url, MENU_ENUM_SUBLABEL_CLOUD_SYNC_S3_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_cloud_sync_access_key_id, MENU_ENUM_SUBLABEL_CLOUD_SYNC_ACCESS_KEY_ID) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cloud_sync_secret_access_key, MENU_ENUM_SUBLABEL_CLOUD_SYNC_SECRET_ACCESS_KEY) 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) @@ -5254,12 +5257,21 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_CLOUD_SYNC_URL: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_url); break; + case MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_s3_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_CLOUD_SYNC_ACCESS_KEY_ID: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_access_key_id); + break; + case MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_cloud_sync_secret_access_key); + break; case MENU_ENUM_LABEL_LOGGING_SETTINGS: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_logging_settings_list); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 09730a024165..afdba82c304a 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -10364,7 +10364,7 @@ unsigned menu_displaylist_build_list( MENU_ENUM_LABEL_VIDEO_HDR_SCANLINES, PARSE_ONLY_BOOL, false) == 0) count++; - + if(settings->bools.video_hdr_scanlines) { if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, @@ -11345,22 +11345,54 @@ unsigned menu_displaylist_build_list( case DISPLAYLIST_CLOUD_SYNC_SETTINGS_LIST: { menu_displaylist_build_info_selective_t build_list[] = { - {MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE, PARSE_ONLY_BOOL, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_MODE, PARSE_ONLY_UINT, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE, PARSE_ONLY_BOOL, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SAVES, PARSE_ONLY_BOOL, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_CONFIGS, PARSE_ONLY_BOOL, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_THUMBS, PARSE_ONLY_BOOL, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SYSTEM, PARSE_ONLY_BOOL, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER, PARSE_ONLY_STRING_OPTIONS, true}, - {MENU_ENUM_LABEL_CLOUD_SYNC_URL, PARSE_ONLY_STRING, false}, - {MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME, PARSE_ONLY_STRING, false}, - {MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD, PARSE_ONLY_STRING, false}, + {MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_MODE, PARSE_ONLY_UINT, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SAVES, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_CONFIGS, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_THUMBS, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SYSTEM, PARSE_ONLY_BOOL, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER, PARSE_ONLY_STRING_OPTIONS, true}, + {MENU_ENUM_LABEL_CLOUD_SYNC_URL, PARSE_ONLY_STRING, false}, + {MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME, PARSE_ONLY_STRING, false}, + {MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD, PARSE_ONLY_STRING, false}, +#ifdef HAVE_S3 + {MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL, PARSE_ONLY_STRING, false}, + {MENU_ENUM_LABEL_CLOUD_SYNC_ACCESS_KEY_ID, PARSE_ONLY_STRING, false}, + {MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY, PARSE_ONLY_STRING, false}, +#endif }; - if (string_is_equal(settings->arrays.cloud_sync_driver, "webdav")) - for (i = 0; i < ARRAY_SIZE(build_list); i++) - build_list[i].checked = true; + for (i = 0; i < ARRAY_SIZE(build_list); i++) + switch(build_list[i].enum_idx) + { + case MENU_ENUM_LABEL_CLOUD_SYNC_ENABLE: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_MODE: + case MENU_ENUM_LABEL_CLOUD_SYNC_DESTRUCTIVE: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SAVES: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_CONFIGS: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_THUMBS: + case MENU_ENUM_LABEL_CLOUD_SYNC_SYNC_SYSTEM: + case MENU_ENUM_LABEL_CLOUD_SYNC_DRIVER: + if (string_is_equal(settings->arrays.cloud_sync_driver, "webdav") + || string_is_equal(settings->arrays.cloud_sync_driver, "s3")) + build_list[i].checked = true; + break; + case MENU_ENUM_LABEL_CLOUD_SYNC_USERNAME: + case MENU_ENUM_LABEL_CLOUD_SYNC_URL: + case MENU_ENUM_LABEL_CLOUD_SYNC_PASSWORD: + if (string_is_equal(settings->arrays.cloud_sync_driver, "webdav")) + build_list[i].checked = true; + break; + case MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL: + case MENU_ENUM_LABEL_CLOUD_SYNC_ACCESS_KEY_ID: + case MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY: + if (string_is_equal(settings->arrays.cloud_sync_driver, "s3")) + build_list[i].checked = true; + break; + default: + break; + } for (i = 0; i < ARRAY_SIZE(build_list); i++) { diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 22efacec5d84..4336fb2a3812 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -12282,6 +12282,58 @@ static bool setting_append_list( (*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; +#ifdef HAVE_S3 + /* AWS */ + CONFIG_STRING( + list, list_info, + settings->arrays.s3_url, + sizeof(settings->arrays.s3_url), + MENU_ENUM_LABEL_CLOUD_SYNC_S3_URL, + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_S3_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.access_key_id, + sizeof(settings->arrays.access_key_id), + MENU_ENUM_LABEL_CLOUD_SYNC_ACCESS_KEY_ID, + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_ACCESS_KEY_ID, + "", + &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.secret_access_key, + sizeof(settings->arrays.secret_access_key), + MENU_ENUM_LABEL_CLOUD_SYNC_SECRET_ACCESS_KEY, + MENU_ENUM_LABEL_VALUE_CLOUD_SYNC_SECRET_ACCESS_KEY, + "", + &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; +#endif END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); #endif diff --git a/msg_hash.h b/msg_hash.h index e9db968da693..05206a60cb70 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1536,7 +1536,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SUBPIXEL_LAYOUT_RGB, MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SUBPIXEL_LAYOUT_RBG, MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SUBPIXEL_LAYOUT_BGR, - + MENU_LBL_H(PARENT_DIRECTORY), MENU_LBL_H(FILE_BROWSER_OPEN_UWP_PERMISSIONS), MENU_LABEL(FILE_BROWSER_OPEN_PICKER), @@ -3212,6 +3212,9 @@ enum msg_hash_enums MENU_LABEL(CLOUD_SYNC_SYNC_NOW), MENU_LABEL(CLOUD_SYNC_RESOLVE_KEEP_LOCAL), MENU_LABEL(CLOUD_SYNC_RESOLVE_KEEP_SERVER), + MENU_LABEL(CLOUD_SYNC_S3_URL), + MENU_LABEL(CLOUD_SYNC_ACCESS_KEY_ID), + MENU_LABEL(CLOUD_SYNC_SECRET_ACCESS_KEY), MENU_LABEL(RECORDING_SETTINGS), MENU_LABEL(OVERLAY_SETTINGS), MENU_LABEL(REWIND_SETTINGS), diff --git a/network/cloud_sync/google_drive.c b/network/cloud_sync/google_drive.c index 54c10e2f86bc..bc4843e44a4d 100644 --- a/network/cloud_sync/google_drive.c +++ b/network/cloud_sync/google_drive.c @@ -1356,7 +1356,7 @@ static void gdrive_do_patch(gdrive_cb_state_t *cb_st) cb_st->path, (long long)len); task_push_http_transfer_with_content(url, "PATCH", buf, (size_t)len, "application/octet-stream", - true, headers, gdrive_upload_cb, cb_st); + true, false, headers, gdrive_upload_cb, cb_st); free(buf); free(headers); } diff --git a/network/cloud_sync/s3.c b/network/cloud_sync/s3.c new file mode 100644 index 000000000000..6e0a8034b502 --- /dev/null +++ b/network/cloud_sync/s3.c @@ -0,0 +1,2103 @@ +/* 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 . + */ + +#include +#include +#include +#include