diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c index ca9eff1da..b2b9a3bf8 100644 --- a/desktop/gui_factory.c +++ b/desktop/gui_factory.c @@ -679,10 +679,14 @@ static nserror gui_default_cert_verify(nsurl *url, return NSERROR_NOT_IMPLEMENTED; } -static void gui_default_401login_open(nsurl *url, const char *realm, - nserror (*cb)(bool proceed, void *pw), void *cbpw) +static nserror gui_default_401login_open(nsurl *url, const char *realm, + const char *username, const char *password, + nserror (*cb)(const char *username, + const char *password, + void *pw), + void *cbpw) { - cb(false, cbpw); + return NSERROR_NOT_IMPLEMENTED; } static void diff --git a/desktop/netsurf.c b/desktop/netsurf.c index f39a6baef..3757a2a34 100644 --- a/desktop/netsurf.c +++ b/desktop/netsurf.c @@ -30,6 +30,7 @@ #include "utils/nsoption.h" #include "utils/corestrings.h" #include "utils/log.h" +#include "utils/string.h" #include "utils/utf8.h" #include "utils/messages.h" #include "content/content_factory.h" @@ -93,6 +94,198 @@ static void netsurf_lwc_iterator(lwc_string *str, void *pw) (int)lwc_string_length(str), lwc_string_data(str)); } +/** + * Build a "username:password" from components. + * + * \param[in] username The username component. + * \param[in] password The password component. + * \param[out] userpass_out Returns combined string on success. + * Owned by caller. + * \return NSERROR_OK, or appropriate error code. + */ +static nserror netsurf__build_userpass( + const char *username, + const char *password, + char **userpass_out) +{ + char *userpass; + size_t len; + + len = strlen(username) + 1 + strlen(password) + 1; + + userpass = malloc(len); + if (userpass == NULL) { + return NSERROR_NOMEM; + } + + snprintf(userpass, len, "%s:%s", username, password); + + *userpass_out = userpass; + return NSERROR_OK; +} + +/** + * Unpack a "username:password" to components. + * + * \param[in] userpass The input string to split. + * \param[in] username_out Returns username on success. Owned by caller. + * \param[out] password_out Returns password on success. Owned by caller. + * \return NSERROR_OK, or appropriate error code. + */ +static nserror netsurf__unpack_userpass( + const char *userpass, + char **username_out, + char **password_out) +{ + const char *tmp; + char *username; + char *password; + size_t len; + + if (userpass == NULL) { + username = malloc(1); + password = malloc(1); + if (username == NULL || password == NULL) { + return NSERROR_NOMEM; + } + username[0] = '\0'; + password[0] = '\0'; + + *username_out = username; + *password_out = password; + return NSERROR_OK; + } + + tmp = strchr(userpass, ':'); + if (tmp == NULL) { + return NSERROR_BAD_PARAMETER; + } else { + size_t len2; + len = tmp - userpass; + len2 = strlen(++tmp); + + username = malloc(len + 1); + password = malloc(len2 + 1); + if (username == NULL || password == NULL) { + return NSERROR_NOMEM; + } + memcpy(username, userpass, len); + username[len] = '\0'; + memcpy(password, tmp, len2 + 1); + } + + *username_out = username; + *password_out = password; + return NSERROR_OK; +} + +/** + * Contect for login callbacks to front ends. + */ +struct auth_data { + char *realm; + nsurl *url; + + llcache_query_response cb; + void *pw; +}; + +/** + * Callback function passed to front ends for handling logins. + * + * \param[in] username The username. + * \param[in] password The password. + * \param[in] cbpw Our context. + * \return NSERROR_OK, or appropriate error code. + */ +static nserror netsurf__handle_login_response( + const char *username, + const char *password, + void *cbpw) +{ + struct auth_data *ctx = cbpw; + bool proceed = false; + nserror err; + + if (username != NULL && password != NULL) { + char *userpass; + + err = netsurf__build_userpass(username, password, &userpass); + if (err != NSERROR_OK) { + return err; + } + + urldb_set_auth_details(ctx->url, ctx->realm, userpass); + free(userpass); + proceed = true; + } + + err = ctx->cb(proceed, ctx->pw); + nsurl_unref(ctx->url); + free(ctx->realm); + free(ctx); + return err; +} + +/** + * Helper for getting front end to handle logins. + * + * \param[in] query Query descriptor + * \param[in] pw Private data + * \param[in] cb Continuation callback + * \param[in] cbpw Private data for continuation + * \return NSERROR_OK, or appropriate error code. + */ +static nserror netsurf__handle_login(const llcache_query *query, + void *pw, llcache_query_response cb, void *cbpw) +{ + struct auth_data *ctx; + char *username; + char *password; + nserror err; + + NSLOG(llcache, INFO, "HTTP Auth for: %s: %s", + query->data.auth.realm, nsurl_access(query->url)); + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + return NSERROR_NOMEM; + } + + ctx->realm = strdup(query->data.auth.realm); + if (ctx->realm == NULL) { + free(ctx); + return NSERROR_NOMEM; + } + ctx->url = nsurl_ref(query->url); + ctx->cb = cb; + ctx->pw = cbpw; + + err = netsurf__unpack_userpass( + urldb_get_auth_details(ctx->url, ctx->realm), + &username, &password); + if (err != NSERROR_OK) { + nsurl_unref(ctx->url); + free(ctx->realm); + free(ctx); + return err; + } + + err = guit->misc->login(ctx->url, ctx->realm, username, password, + netsurf__handle_login_response, ctx); + free(username); + free(password); + if (err != NSERROR_OK) { + ctx->cb(false, ctx->pw); + nsurl_unref(ctx->url); + free(ctx->realm); + free(ctx); + return err; + } + + return NSERROR_OK; +} + /** * Dispatch a low-level cache query to the frontend * @@ -109,10 +302,7 @@ static nserror netsurf_llcache_query_handler(const llcache_query *query, switch (query->type) { case LLCACHE_QUERY_AUTH: - NSLOG(llcache, INFO, "HTTP Auth for: %s: %s", - query->data.auth.realm, - nsurl_access(query->url)); - guit->misc->login(query->url, query->data.auth.realm, cb, cbpw); + res = netsurf__handle_login(query, pw, cb, cbpw); break; case LLCACHE_QUERY_REDIRECT: diff --git a/include/netsurf/misc.h b/include/netsurf/misc.h index 2647b9a1c..cd86cf644 100644 --- a/include/netsurf/misc.h +++ b/include/netsurf/misc.h @@ -91,13 +91,40 @@ struct gui_misc_table { * \param cbpw Context pointer passed to cb * \return NSERROR_OK on sucess else error and cb never called */ - nserror (*cert_verify)(struct nsurl *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw); + nserror (*cert_verify)(struct nsurl *url, + const struct ssl_cert_info *certs, + unsigned long num, + nserror (*cb)(bool proceed, void *pw), + void *cbpw); /** * Prompt user for login + * + * To cancel a login, clients should call the `cb` callback passing + * NULL for username, and password. Otherwise, for logins, username + * and password should both be non-NULL. Pass "" if the empty string + * is required. + * + * If the front end returns NSERROR_OK for this function, they must, + * at some future time, call the `cb` with `cbpw` callback exactly once. + * + * If ther front end returns other than NSERROR_OK, they should not + * call the `cb` callback. + * + * \param url The URL being verified. + * \param realm The authorization realm. + * \param username Any current username (or empty string). + * \param password Any current password (or empty string). + * \param cb Callback upon user decision. + * \param cbpw Context pointer passed to cb + * \return NSERROR_OK on sucess else error and cb never called */ - void (*login)(struct nsurl *url, const char *realm, - nserror (*cb)(bool proceed, void *pw), void *cbpw); + nserror (*login)(struct nsurl *url, const char *realm, + const char *username, const char *password, + nserror (*cb)(const char *username, + const char *password, + void *pw), + void *cbpw); /** * Prompt the user for a password for a PDF.