Skip to content

Commit

Permalink
webdav: fixes for reauth and parallelism
Browse files Browse the repository at this point in the history
  • Loading branch information
warmenhoven committed Oct 29, 2024
1 parent cb7cd6e commit 07b4db6
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 54 deletions.
163 changes: 113 additions & 50 deletions network/cloud_sync/webdav.c
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -267,6 +262,7 @@ static bool webdav_create_digest_auth(char *digest)
return false;

webdav_st->cnonce = "1a2b3c4f";
webdav_st->basic = false;

return true;
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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;
Expand All @@ -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
{
Expand Down Expand Up @@ -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 */
Expand All @@ -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));
Expand All @@ -605,21 +627,31 @@ 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;
}

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;
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))
{
Expand All @@ -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
{
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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)
Expand All @@ -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
{
Expand Down
7 changes: 4 additions & 3 deletions network/cloud_sync_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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];
}
}

Expand Down
2 changes: 1 addition & 1 deletion tasks/task_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 07b4db6

Please sign in to comment.