diff options
Diffstat (limited to 'credential.c')
-rw-r--r-- | credential.c | 82 |
1 files changed, 73 insertions, 9 deletions
diff --git a/credential.c b/credential.c index 108d9e183a..d8d226b97e 100644 --- a/credential.c +++ b/credential.c @@ -37,6 +37,10 @@ int credential_match(const struct credential *want, #undef CHECK } + +static int credential_from_potentially_partial_url(struct credential *c, + const char *url); + static int credential_config_callback(const char *var, const char *value, void *data) { @@ -82,6 +86,22 @@ static int select_all(const struct urlmatch_item *a, return 0; } +static int match_partial_url(const char *url, void *cb) +{ + struct credential *c = cb; + struct credential want = CREDENTIAL_INIT; + int matches = 0; + + if (credential_from_potentially_partial_url(&want, url) < 0) + warning(_("skipping credential lookup for key: credential.%s"), + url); + else + matches = credential_match(&want, c); + credential_clear(&want); + + return matches; +} + static void credential_apply_config(struct credential *c) { char *normalized_url; @@ -101,6 +121,7 @@ static void credential_apply_config(struct credential *c) config.collect_fn = credential_config_callback; config.cascade_fn = NULL; config.select_fn = select_all; + config.fallback_match_fn = match_partial_url; config.cb = c; credential_format(c, &url); @@ -136,14 +157,14 @@ static void credential_format(struct credential *c, struct strbuf *out) return; strbuf_addf(out, "%s://", c->protocol); if (c->username && *c->username) { - strbuf_add_percentencode(out, c->username); + strbuf_add_percentencode(out, c->username, STRBUF_ENCODE_SLASH); strbuf_addch(out, '@'); } if (c->host) strbuf_addstr(out, c->host); if (c->path) { strbuf_addch(out, '/'); - strbuf_add_percentencode(out, c->path); + strbuf_add_percentencode(out, c->path, 0); } } @@ -377,8 +398,31 @@ static int check_url_component(const char *url, int quiet, return -1; } -int credential_from_url_gently(struct credential *c, const char *url, - int quiet) +/* + * Potentially-partial URLs can, but do not have to, contain + * + * - a protocol (or scheme) of the form "<protocol>://" + * + * - a host name (the part after the protocol and before the first slash after + * that, if any) + * + * - a user name and potentially a password (as "<user>[:<password>]@" part of + * the host name) + * + * - a path (the part after the host name, if any, starting with the slash) + * + * Missing parts will be left unset in `struct credential`. Thus, `https://` + * will have only the `protocol` set, `example.com` only the host name, and + * `/git` only the path. + * + * Note that an empty host name in an otherwise fully-qualified URL (e.g. + * `cert:///path/to/cert.pem`) will be treated as unset if we expect the URL to + * be potentially partial, and only then (otherwise, the empty string is used). + * + * The credential_from_url() function does not allow partial URLs. + */ +static int credential_from_url_1(struct credential *c, const char *url, + int allow_partial_url, int quiet) { const char *at, *colon, *cp, *slash, *host, *proto_end; @@ -391,15 +435,22 @@ int credential_from_url_gently(struct credential *c, const char *url, * (3) proto://<user>:<pass>@<host>/... */ proto_end = strstr(url, "://"); - if (!proto_end || proto_end == url) { + if (!allow_partial_url && (!proto_end || proto_end == url)) { if (!quiet) warning(_("url has no scheme: %s"), url); return -1; } - cp = proto_end + 3; + cp = proto_end ? proto_end + 3 : url; at = strchr(cp, '@'); colon = strchr(cp, ':'); - slash = strchrnul(cp, '/'); + + /* + * A query or fragment marker before the slash ends the host portion. + * We'll just continue to call this "slash" for simplicity. Notably our + * "trim leading slashes" part won't skip over this part of the path, + * but that's what we'd want. + */ + slash = cp + strcspn(cp, "/?#"); if (!at || slash <= at) { /* Case (1) */ @@ -420,8 +471,10 @@ int credential_from_url_gently(struct credential *c, const char *url, host = at + 1; } - c->protocol = xmemdupz(url, proto_end - url); - c->host = url_decode_mem(host, slash - host); + if (proto_end && proto_end - url > 0) + c->protocol = xmemdupz(url, proto_end - url); + if (!allow_partial_url || slash - host > 0) + c->host = url_decode_mem(host, slash - host); /* Trim leading and trailing slashes from path */ while (*slash == '/') slash++; @@ -443,6 +496,17 @@ int credential_from_url_gently(struct credential *c, const char *url, return 0; } +static int credential_from_potentially_partial_url(struct credential *c, + const char *url) +{ + return credential_from_url_1(c, url, 1, 0); +} + +int credential_from_url_gently(struct credential *c, const char *url, int quiet) +{ + return credential_from_url_1(c, url, 0, quiet); +} + void credential_from_url(struct credential *c, const char *url) { if (credential_from_url_gently(c, url, 0) < 0) |