diff --git a/credential-store.c b/credential-store.c index d435514cbe..c519f8025e 100644 --- a/credential-store.c +++ b/credential-store.c @@ -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 @@ static void parse_credential_file(const char *fn, 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 @@ static void parse_credential_file(const char *fn, credential_clear(&entry); strbuf_release(&line); fclose(fh); + return found_credential; } static void print_entry(struct credential *c) @@ -64,21 +67,10 @@ static void rewrite_credential_file(const char *fn, struct credential *c, 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 @@ static void store_credential(const char *fn, struct credential *c) 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,14 +126,20 @@ static void remove_credential(const char *fn, struct credential *c) * 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) @@ -123,6 +150,7 @@ int main(int argc, char **argv) }; 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", @@ -139,20 +167,23 @@ int main(int argc, char **argv) if (!file) file = expand_user_path("~/.git-credentials"); - if (!file) + if (file) + string_list_append(&fns, file); + else 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; }