#include "cache.h" #include "lockfile.h" #include "credential.h" #include "string-list.h" #include "parse-options.h" static struct lock_file credential_lock; static void parse_credential_file(const char *fn, struct credential *c, void (*match_cb)(struct credential *), void (*other_cb)(struct strbuf *)) { FILE *fh; struct strbuf line = STRBUF_INIT; struct credential entry = CREDENTIAL_INIT; fh = fopen(fn, "r"); if (!fh) { if (errno != ENOENT) die_errno("unable to open %s", fn); return; } while (strbuf_getline(&line, fh, '\n') != EOF) { credential_from_url(&entry, line.buf); if (entry.username && entry.password && credential_match(c, &entry)) { if (match_cb) { match_cb(&entry); break; } } else if (other_cb) other_cb(&line); } credential_clear(&entry); strbuf_release(&line); fclose(fh); } static void print_entry(struct credential *c) { printf("username=%s\n", c->username); printf("password=%s\n", c->password); } static void print_line(struct strbuf *buf) { strbuf_addch(buf, '\n'); write_or_die(credential_lock.fd, buf->buf, buf->len); } static void rewrite_credential_file(const char *fn, struct credential *c, struct strbuf *extra) { if (hold_lock_file_for_update(&credential_lock, fn, 0) < 0) die_errno("unable to get credential storage lock"); if (extra) print_line(extra); parse_credential_file(fn, c, NULL, print_line); if (commit_lock_file(&credential_lock) < 0) die_errno("unable to commit credential store"); } static void store_credential(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, ':'); strbuf_addstr_urlencode(&buf, c->password, 1); strbuf_addch(&buf, '@'); if (c->host) strbuf_addstr_urlencode(&buf, c->host, 1); if (c->path) { strbuf_addch(&buf, '/'); strbuf_addstr_urlencode(&buf, c->path, 0); } rewrite_credential_file(fn, c, &buf); strbuf_release(&buf); } static void remove_credential(const char *fn, struct credential *c) { /* * Sanity check that we actually have something to match * against. The input we get is a restrictive pattern, * so technically a blank credential means "erase everything". * But it is too easy to accidentally send this, since it is equivalent * 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); } static int lookup_credential(const char *fn, struct credential *c) { parse_credential_file(fn, c, print_entry, NULL); return c->username && c->password; } int main(int argc, char **argv) { const char * const usage[] = { "git credential-store [options] ", NULL }; const char *op; struct credential c = CREDENTIAL_INIT; char *file = NULL; struct option options[] = { OPT_STRING(0, "file", &file, "path", "fetch and store credentials in "), OPT_END() }; umask(077); argc = parse_options(argc, (const char **)argv, NULL, options, usage, 0); if (argc != 1) usage_with_options(usage, options); op = argv[0]; if (!file) file = expand_user_path("~/.git-credentials"); if (!file) 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); else if (!strcmp(op, "erase")) remove_credential(file, &c); else if (!strcmp(op, "store")) store_credential(file, &c); else ; /* Ignore unknown operation. */ return 0; }