diff --git a/network/cloud_sync/webdav.c b/network/cloud_sync/webdav.c index 86009788524..7620ee7aa36 100644 --- a/network/cloud_sync/webdav.c +++ b/network/cloud_sync/webdav.c @@ -59,7 +59,6 @@ typedef struct char *cnonce; bool qop_auth; unsigned nc; - char *digest_auth_header; } webdav_state_t; static webdav_state_t webdav_driver_st = {0}; @@ -120,10 +119,6 @@ static void webdav_cleanup_digest(void) 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) @@ -267,6 +262,7 @@ static bool webdav_create_digest_auth(char *digest) return false; webdav_st->cnonce = "1a2b3c4f"; + webdav_st->basic = false; return true; } @@ -438,13 +434,10 @@ static char *webdav_get_auth_header(const char *method, const char *url) { if (!webdav_st->basic_auth_header) webdav_st->basic_auth_header = webdav_create_basic_auth(); - return webdav_st->basic_auth_header; + return strdup(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; + return webdav_create_digest_auth_header(method, url); } static void webdav_log_http_failure(const char *path, http_transfer_data_t *data) @@ -454,7 +447,32 @@ static void webdav_log_http_failure(const char *path, http_transfer_data_t *data for (i = 0; data->headers && i < data->headers->size; i++) RARCH_WARN("%s\n", data->headers->elems[i].data); if (data->data) + { + data->data[data->len] = 0; RARCH_WARN("%s\n", data->data); + } +} + +static bool webdav_needs_reauth(http_transfer_data_t *data) +{ + size_t i; + + if (!data || data->status != 401 || !data->headers) + return false; + + for (i = 0; i < data->headers->size; i++) + { + if (!string_starts_with(data->headers->elems[i].data, "WWW-Authenticate: Digest ")) + continue; + + RARCH_DBG("[webdav] found WWW-Authenticate: Digest header\n"); + if (webdav_create_digest_auth(data->headers->elems[i].data)) + return true; + else + RARCH_WARN("[webdav] failure creating WWW-Authenticate: Digest header\n"); + } + + return false; } static void webdav_stat_cb(retro_task_t *task, void *task_data, void *user_data, const char *err) @@ -470,26 +488,12 @@ static void webdav_stat_cb(retro_task_t *task, void *task_data, void *user_data, if (!data) RARCH_WARN("[webdav] did not get data for stat, is the server down?\n"); - if (data && data->status == 401 && data->headers && webdav_st->basic == true) + if (webdav_needs_reauth(data)) { - size_t 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; - - RARCH_DBG("[webdav] found WWW-Authenticate: Digest header\n"); - 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; - } - else - RARCH_WARN("[webdav] failure creating WWW-Authenticate: Digest header\n"); - } + char *auth_header = webdav_get_auth_header("OPTIONS", webdav_st->url); + task_push_webdav_stat(webdav_st->url, true, auth_header, webdav_stat_cb, webdav_cb_st); + free(auth_header); + return; } if (!success && data) @@ -505,7 +509,7 @@ static bool webdav_sync_begin(cloud_sync_complete_handler_t cb, void *user_data) const char *url = settings->arrays.webdav_url; webdav_state_t *webdav_st = webdav_state_get_ptr(); size_t len = 0; - const char *auth_header; + char *auth_header; if (string_is_empty(url)) return false; @@ -531,6 +535,7 @@ static bool webdav_sync_begin(cloud_sync_complete_handler_t cb, void *user_data) 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); + free(auth_header); } else { @@ -569,7 +574,23 @@ static void webdav_read_cb(retro_task_t *task, void *task_data, void *user_data, if (!success && data) webdav_log_http_failure(webdav_cb_st->path, data); - /* TODO: it's possible we get a 401 here and need to redo the auth check with this request */ + if (webdav_needs_reauth(data)) + { + webdav_state_t *webdav_st = webdav_state_get_ptr(); + char url[PATH_MAX_LENGTH]; + char url_encoded[PATH_MAX_LENGTH]; + char *auth_header; + + fill_pathname_join_special(url, webdav_st->url, webdav_cb_st->path, sizeof(url)); + net_http_urlencode_full(url_encoded, url, sizeof(url_encoded)); + + RARCH_DBG("[webdav] GET %s\n", url_encoded); + auth_header = webdav_get_auth_header("GET", url_encoded); + task_push_http_transfer_with_headers(url_encoded, true, NULL, auth_header, webdav_read_cb, webdav_cb_st); + free(auth_header); + return; + } + if (success && data->data && webdav_cb_st) { /* TODO: it would be better if writing to the file happened during the network reads */ @@ -596,6 +617,7 @@ static bool webdav_read(const char *path, const char *file, cloud_sync_complete_ 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]; + char *auth_header; fill_pathname_join_special(url, webdav_st->url, path, sizeof(url)); net_http_urlencode_full(url_encoded, url, sizeof(url_encoded)); @@ -605,9 +627,10 @@ static bool webdav_read(const char *path, const char *file, cloud_sync_complete_ 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); + RARCH_DBG("[webdav] GET %s\n", url_encoded); + auth_header = webdav_get_auth_header("GET", url_encoded); + task_push_http_transfer_with_headers(url_encoded, true, NULL, auth_header, webdav_read_cb, webdav_cb_st); + free(auth_header); return true; } @@ -615,11 +638,20 @@ static void webdav_mkdir_cb(retro_task_t *task, void *task_data, void *user_data { webdav_mkdir_state_t *webdav_mkdir_st = (webdav_mkdir_state_t *)user_data; http_transfer_data_t *data = (http_transfer_data_t*)task_data; + char *auth_header; 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 (webdav_needs_reauth(data)) + { + RARCH_DBG("[webdav] MKCOL %s\n", webdav_mkdir_st->url); + auth_header = webdav_get_auth_header("MKCOL", webdav_mkdir_st->url); + task_push_webdav_mkdir(webdav_mkdir_st->url, true, auth_header, webdav_mkdir_cb, webdav_mkdir_st); + free(auth_header); + return; + } + /* HTTP 405 on MKCOL means it's already there */ if (!data || data->status < 200 || (data->status >= 400 && data->status != 405)) { @@ -638,9 +670,9 @@ static void webdav_mkdir_cb(retro_task_t *task, void *task_data, void *user_data { *webdav_mkdir_st->last_slash = '\0'; RARCH_DBG("[webdav] MKCOL %s\n", webdav_mkdir_st->url); - task_push_webdav_mkdir(webdav_mkdir_st->url, true, - webdav_get_auth_header("MKCOL", webdav_mkdir_st->url), - webdav_mkdir_cb, webdav_mkdir_st); + auth_header = webdav_get_auth_header("MKCOL", webdav_mkdir_st->url); + task_push_webdav_mkdir(webdav_mkdir_st->url, true, auth_header, webdav_mkdir_cb, webdav_mkdir_st); + free(auth_header); } else { @@ -668,6 +700,7 @@ static void webdav_ensure_dir(const char *dir, webdav_mkdir_cb_t cb, webdav_cb_s webdav_mkdir_cb(NULL, &data, webdav_mkdir_st, NULL); } +static void webdav_do_update(bool success, webdav_cb_state_t *webdav_cb_st); 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; @@ -679,7 +712,12 @@ static void webdav_update_cb(retro_task_t *task, void *task_data, void *user_dat else if (!data) RARCH_WARN("[webdav] could not upload %s\n", webdav_cb_st ? webdav_cb_st->path : ""); - /* TODO: it's possible we get a 401 here and need to redo the auth check with this request */ + if (webdav_needs_reauth(data)) + { + webdav_do_update(true, webdav_cb_st); + return; + } + if (webdav_cb_st) { webdav_cb_st->cb(webdav_cb_st->user_data, webdav_cb_st->path, success, webdav_cb_st->rfile); @@ -696,6 +734,7 @@ static void webdav_do_update(bool success, webdav_cb_state_t *webdav_cb_st) char url[PATH_MAX_LENGTH]; void *buf; int64_t len; + char *auth_header; if (!webdav_cb_st) return; @@ -717,9 +756,9 @@ static void webdav_do_update(bool success, webdav_cb_state_t *webdav_cb_st) net_http_urlencode_full(url_encoded, url, sizeof(url_encoded)); RARCH_DBG("[webdav] PUT %s\n", url_encoded); - task_push_webdav_put(url_encoded, buf, len, true, - webdav_get_auth_header("PUT", url_encoded), - webdav_update_cb, webdav_cb_st); + auth_header = webdav_get_auth_header("PUT", url_encoded); + task_push_webdav_put(url_encoded, buf, len, true, auth_header, webdav_update_cb, webdav_cb_st); + free(auth_header); free(buf); } @@ -758,7 +797,23 @@ static void webdav_delete_cb(retro_task_t *task, void *task_data, void *user_dat else if (!data) RARCH_WARN("[webdav] could not delete %s\n", webdav_cb_st ? webdav_cb_st->path : ""); - /* TODO: it's possible we get a 401 here and need to redo the auth check with this request */ + if (webdav_needs_reauth(data)) + { + webdav_state_t *webdav_st = webdav_state_get_ptr(); + char url[PATH_MAX_LENGTH]; + char url_encoded[PATH_MAX_LENGTH]; + char *auth_header; + + fill_pathname_join_special(url, webdav_st->url, webdav_cb_st->path, sizeof(url)); + net_http_urlencode_full(url_encoded, url, sizeof(url_encoded)); + + RARCH_DBG("[webdav] DELETE %s\n", url_encoded); + auth_header = webdav_get_auth_header("DELETE", url_encoded); + task_push_webdav_delete(url_encoded, true, auth_header, webdav_delete_cb, webdav_cb_st); + free(auth_header); + return; + } + if (webdav_cb_st) { webdav_cb_st->cb(webdav_cb_st->user_data, webdav_cb_st->path, success, NULL); @@ -768,6 +823,7 @@ static void webdav_delete_cb(retro_task_t *task, void *task_data, void *user_dat RARCH_WARN("[webdav] missing cb data in delete?\n"); } +static void webdav_do_backup(bool success, webdav_cb_state_t *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; @@ -779,7 +835,12 @@ static void webdav_backup_cb(retro_task_t *task, void *task_data, void *user_dat else if (!data) RARCH_WARN("[webdav] could not backup %s\n", webdav_cb_st ? webdav_cb_st->path : ""); - /* TODO: it's possible we get a 401 here and need to redo the auth check with this request */ + if (webdav_needs_reauth(data)) + { + webdav_do_backup(true, webdav_cb_st); + return; + } + if (webdav_cb_st) { webdav_cb_st->cb(webdav_cb_st->user_data, webdav_cb_st->path, success, NULL); @@ -799,6 +860,7 @@ static void webdav_do_backup(bool success, webdav_cb_state_t *webdav_cb_st) size_t len; struct tm tm_; time_t cur_time = time(NULL); + char *auth_header; if (!webdav_cb_st) return; @@ -821,9 +883,9 @@ static void webdav_do_backup(bool success, webdav_cb_state_t *webdav_cb_st) net_http_urlencode_full(dest_encoded, dest, sizeof(dest_encoded)); RARCH_DBG("[webdav] MOVE %s -> %s\n", url_encoded, dest_encoded); - task_push_webdav_move(url_encoded, dest_encoded, true, - webdav_get_auth_header("MOVE", url_encoded), - webdav_backup_cb, webdav_cb_st); + auth_header = webdav_get_auth_header("MOVE", url_encoded); + task_push_webdav_move(url_encoded, dest_encoded, true, auth_header, webdav_backup_cb, webdav_cb_st); + free(auth_header); } static bool webdav_delete(const char *path, cloud_sync_complete_handler_t cb, void *user_data) @@ -845,14 +907,15 @@ static bool webdav_delete(const char *path, cloud_sync_complete_handler_t cb, vo webdav_state_t *webdav_st = webdav_state_get_ptr(); char url_encoded[PATH_MAX_LENGTH]; char url[PATH_MAX_LENGTH]; + char *auth_header; fill_pathname_join_special(url, webdav_st->url, path, sizeof(url)); net_http_urlencode_full(url_encoded, url, sizeof(url_encoded)); RARCH_DBG("[webdav] DELETE %s\n", url_encoded); - task_push_webdav_delete(url_encoded, true, - webdav_get_auth_header("DELETE", url_encoded), - webdav_delete_cb, webdav_cb_st); + auth_header = webdav_get_auth_header("DELETE", url_encoded); + task_push_webdav_delete(url_encoded, true, auth_header, webdav_delete_cb, webdav_cb_st); + free(auth_header); } else { diff --git a/network/cloud_sync_driver.c b/network/cloud_sync_driver.c index e722533c51c..4bf166c8165 100644 --- a/network/cloud_sync_driver.c +++ b/network/cloud_sync_driver.c @@ -70,7 +70,7 @@ void cloud_sync_find_driver( cloud_sync_drivers[i]; else { - if (verbosity_enabled) + if (verbosity_enabled && settings->arrays.cloud_sync_driver[0]) { unsigned d; RARCH_ERR("Couldn't find any %s named \"%s\"\n", prefix, @@ -80,10 +80,11 @@ void cloud_sync_find_driver( 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); + RARCH_WARN("Going to default to null...\n"); } - cloud_sync_st->driver = (const cloud_sync_driver_t*)cloud_sync_drivers[0]; + i = (int)driver_find_index("cloud_sync_driver", "null"); + cloud_sync_st->driver = (const cloud_sync_driver_t*)cloud_sync_drivers[i]; } } diff --git a/tasks/task_http.c b/tasks/task_http.c index e94a8c1e034..683a03dd79e 100644 --- a/tasks/task_http.c +++ b/tasks/task_http.c @@ -279,7 +279,7 @@ static void *task_push_http_transfer_generic( return NULL; method = net_http_connection_method(conn); - if (method && (method[0] == 'P' || method[0] == 'p')) + if (!string_is_equal(method, "GET")) { /* POST requests usually mutate the server, so assume multiple calls are * intended, even if they're duplicated. Additionally, they may differ