From: Junio C Hamano Date: Mon, 11 May 2015 21:23:38 +0000 (-0700) Subject: Merge branch 'pt/credential-xdg' X-Git-Tag: v2.5.0-rc0~144 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/17c7f4d8e4e0e54148f77db4cf73e07aae484ae9?ds=inline;hp=-c Merge branch 'pt/credential-xdg' Tweak the sample "store" backend of the credential helper to honor XDG configuration file locations when specified. * pt/credential-xdg: t0302: "unreadable" test needs POSIXPERM t0302: test credential-store support for XDG_CONFIG_HOME git-credential-store: support XDG_CONFIG_HOME git-credential-store: support multiple credential files --- 17c7f4d8e4e0e54148f77db4cf73e07aae484ae9 diff --combined credential-store.c index 925d3f4024,d62dc29d06..8b222513cb --- a/credential-store.c +++ b/credential-store.c @@@ -6,7 -6,7 +6,7 @@@ static struct lock_file credential_lock; - static void parse_credential_file(const char *fn, + static int parse_credential_file(const char *fn, struct credential *c, void (*match_cb)(struct credential *), void (*other_cb)(struct strbuf *)) @@@ -14,18 -14,20 +14,20 @@@ FILE *fh; struct strbuf line = STRBUF_INIT; struct credential entry = CREDENTIAL_INIT; + int found_credential = 0; fh = fopen(fn, "r"); if (!fh) { - if (errno != ENOENT) + if (errno != ENOENT && errno != EACCES) die_errno("unable to open %s", fn); - return; + return found_credential; } while (strbuf_getline(&line, fh, '\n') != EOF) { credential_from_url(&entry, line.buf); if (entry.username && entry.password && credential_match(c, &entry)) { + found_credential = 1; if (match_cb) { match_cb(&entry); break; @@@ -38,6 -40,7 +40,7 @@@ credential_clear(&entry); strbuf_release(&line); fclose(fh); + return found_credential; } static void print_entry(struct credential *c) @@@ -64,21 -67,10 +67,10 @@@ static void rewrite_credential_file(con die_errno("unable to commit credential store"); } - static void store_credential(const char *fn, struct credential *c) + static void store_credential_file(const char *fn, struct credential *c) { struct strbuf buf = STRBUF_INIT; - /* - * Sanity check that what we are storing is actually sensible. - * In particular, we can't make a URL without a protocol field. - * Without either a host or pathname (depending on the scheme), - * we have no primary key. And without a username and password, - * we are not actually storing a credential. - */ - if (!c->protocol || !(c->host || c->path) || - !c->username || !c->password) - return; - strbuf_addf(&buf, "%s://", c->protocol); strbuf_addstr_urlencode(&buf, c->username, 1); strbuf_addch(&buf, ':'); @@@ -95,8 -87,37 +87,37 @@@ strbuf_release(&buf); } - static void remove_credential(const char *fn, struct credential *c) + static void store_credential(const struct string_list *fns, struct credential *c) + { + struct string_list_item *fn; + + /* + * Sanity check that what we are storing is actually sensible. + * In particular, we can't make a URL without a protocol field. + * Without either a host or pathname (depending on the scheme), + * we have no primary key. And without a username and password, + * we are not actually storing a credential. + */ + if (!c->protocol || !(c->host || c->path) || !c->username || !c->password) + return; + + for_each_string_list_item(fn, fns) + if (!access(fn->string, F_OK)) { + store_credential_file(fn->string, c); + return; + } + /* + * Write credential to the filename specified by fns->items[0], thus + * creating it + */ + if (fns->nr) + store_credential_file(fns->items[0].string, c); + } + + static void remove_credential(const struct string_list *fns, struct credential *c) { + struct string_list_item *fn; + /* * Sanity check that we actually have something to match * against. The input we get is a restrictive pattern, @@@ -105,24 -126,31 +126,31 @@@ * to empty input. So explicitly disallow it, and require that the * pattern have some actual content to match. */ - if (c->protocol || c->host || c->path || c->username) - rewrite_credential_file(fn, c, NULL); + if (!c->protocol && !c->host && !c->path && !c->username) + return; + for_each_string_list_item(fn, fns) + if (!access(fn->string, F_OK)) + rewrite_credential_file(fn->string, c, NULL); } - static int lookup_credential(const char *fn, struct credential *c) + static void lookup_credential(const struct string_list *fns, struct credential *c) { - parse_credential_file(fn, c, print_entry, NULL); - return c->username && c->password; + struct string_list_item *fn; + + for_each_string_list_item(fn, fns) + if (parse_credential_file(fn->string, c, print_entry, NULL)) + return; /* Found credential */ } int main(int argc, char **argv) { const char * const usage[] = { - "git credential-store [options] ", + "git credential-store [] ", NULL }; const char *op; struct credential c = CREDENTIAL_INIT; + struct string_list fns = STRING_LIST_INIT_DUP; char *file = NULL; struct option options[] = { OPT_STRING(0, "file", &file, "path", @@@ -137,22 -165,30 +165,30 @@@ usage_with_options(usage, options); op = argv[0]; - if (!file) - file = expand_user_path("~/.git-credentials"); - if (!file) + if (file) { + string_list_append(&fns, file); + } else { + if ((file = expand_user_path("~/.git-credentials"))) + string_list_append_nodup(&fns, file); + home_config_paths(NULL, &file, "credentials"); + if (file) + string_list_append_nodup(&fns, file); + } + if (!fns.nr) die("unable to set up default path; use --file"); if (credential_read(&c, stdin) < 0) die("unable to read credential"); if (!strcmp(op, "get")) - lookup_credential(file, &c); + lookup_credential(&fns, &c); else if (!strcmp(op, "erase")) - remove_credential(file, &c); + remove_credential(&fns, &c); else if (!strcmp(op, "store")) - store_credential(file, &c); + store_credential(&fns, &c); else ; /* Ignore unknown operation. */ + string_list_clear(&fns, 0); return 0; }