| #include "cache.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, const char **argv) | 
 | { | 
 | 	const char * const usage[] = { | 
 | 		"git credential-store [options] <action>", | 
 | 		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 <path>"), | 
 | 		OPT_END() | 
 | 	}; | 
 |  | 
 | 	umask(077); | 
 |  | 
 | 	argc = parse_options(argc, 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; | 
 | } |