webdav: fixes for reauth and parallelism (#17132)

This commit is contained in:
Eric Warmenhoven 2024-10-29 15:03:46 -04:00 committed by GitHub
parent 8ee6561a26
commit a26f6f6b45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 118 additions and 54 deletions

View File

@ -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,8 +447,33 @@ 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,27 +488,13 @@ 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);
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;
}
else
RARCH_WARN("[webdav] failure creating WWW-Authenticate: Digest header\n");
}
}
if (!success && data)
webdav_log_http_failure(webdav_st->url, 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 : "<unknown>");
/* 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 : "<unknown>");
/* 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 : "<unknown>");
/* 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
{

View File

@ -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];
}
}

View File

@ -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