|  | #include "cache.h" | 
|  | #include "lockfile.h" | 
|  | #include "credential.h" | 
|  | #include "string-list.h" | 
|  | #include "parse-options.h" | 
|  |  | 
|  | static struct lock_file credential_lock; | 
|  |  | 
|  | static int 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; | 
|  | int found_credential = 0; | 
|  |  | 
|  | fh = fopen(fn, "r"); | 
|  | if (!fh) { | 
|  | if (errno != ENOENT && errno != EACCES) | 
|  | die_errno("unable to open %s", fn); | 
|  | return found_credential; | 
|  | } | 
|  |  | 
|  | while (strbuf_getline_lf(&line, fh) != 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; | 
|  | } | 
|  | } | 
|  | else if (other_cb) | 
|  | other_cb(&line); | 
|  | } | 
|  |  | 
|  | credential_clear(&entry); | 
|  | strbuf_release(&line); | 
|  | fclose(fh); | 
|  | return found_credential; | 
|  | } | 
|  |  | 
|  | 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(get_lock_file_fd(&credential_lock), 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 write credential store"); | 
|  | } | 
|  |  | 
|  | static void store_credential_file(const char *fn, struct credential *c) | 
|  | { | 
|  | struct strbuf buf = STRBUF_INIT; | 
|  |  | 
|  | 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 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, | 
|  | * 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) | 
|  | return; | 
|  | for_each_string_list_item(fn, fns) | 
|  | if (!access(fn->string, F_OK)) | 
|  | rewrite_credential_file(fn->string, c, NULL); | 
|  | } | 
|  |  | 
|  | static void lookup_credential(const struct string_list *fns, struct credential *c) | 
|  | { | 
|  | 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 cmd_main(int argc, const char **argv) | 
|  | { | 
|  | const char * const usage[] = { | 
|  | "git credential-store [<options>] <action>", | 
|  | 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", | 
|  | "fetch and store credentials in <path>"), | 
|  | 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) { | 
|  | string_list_append(&fns, file); | 
|  | } else { | 
|  | if ((file = expand_user_path("~/.git-credentials", 0))) | 
|  | string_list_append_nodup(&fns, file); | 
|  | file = xdg_config_home("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(&fns, &c); | 
|  | else if (!strcmp(op, "erase")) | 
|  | remove_credential(&fns, &c); | 
|  | else if (!strcmp(op, "store")) | 
|  | store_credential(&fns, &c); | 
|  | else | 
|  | ; /* Ignore unknown operation. */ | 
|  |  | 
|  | string_list_clear(&fns, 0); | 
|  | return 0; | 
|  | } |