summaryrefslogtreecommitdiff
path: root/credential.c
diff options
context:
space:
mode:
Diffstat (limited to 'credential.c')
-rw-r--r--credential.c82
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)