Merge branch 'nd/split-index'
[git] / credential-store.c
1 #include "cache.h"
2 #include "lockfile.h"
3 #include "credential.h"
4 #include "string-list.h"
5 #include "parse-options.h"
6
7 static struct lock_file credential_lock;
8
9 static void parse_credential_file(const char *fn,
10                                   struct credential *c,
11                                   void (*match_cb)(struct credential *),
12                                   void (*other_cb)(struct strbuf *))
13 {
14         FILE *fh;
15         struct strbuf line = STRBUF_INIT;
16         struct credential entry = CREDENTIAL_INIT;
17
18         fh = fopen(fn, "r");
19         if (!fh) {
20                 if (errno != ENOENT)
21                         die_errno("unable to open %s", fn);
22                 return;
23         }
24
25         while (strbuf_getline(&line, fh, '\n') != EOF) {
26                 credential_from_url(&entry, line.buf);
27                 if (entry.username && entry.password &&
28                     credential_match(c, &entry)) {
29                         if (match_cb) {
30                                 match_cb(&entry);
31                                 break;
32                         }
33                 }
34                 else if (other_cb)
35                         other_cb(&line);
36         }
37
38         credential_clear(&entry);
39         strbuf_release(&line);
40         fclose(fh);
41 }
42
43 static void print_entry(struct credential *c)
44 {
45         printf("username=%s\n", c->username);
46         printf("password=%s\n", c->password);
47 }
48
49 static void print_line(struct strbuf *buf)
50 {
51         strbuf_addch(buf, '\n');
52         write_or_die(credential_lock.fd, buf->buf, buf->len);
53 }
54
55 static void rewrite_credential_file(const char *fn, struct credential *c,
56                                     struct strbuf *extra)
57 {
58         if (hold_lock_file_for_update(&credential_lock, fn, 0) < 0)
59                 die_errno("unable to get credential storage lock");
60         if (extra)
61                 print_line(extra);
62         parse_credential_file(fn, c, NULL, print_line);
63         if (commit_lock_file(&credential_lock) < 0)
64                 die_errno("unable to commit credential store");
65 }
66
67 static void store_credential(const char *fn, struct credential *c)
68 {
69         struct strbuf buf = STRBUF_INIT;
70
71         /*
72          * Sanity check that what we are storing is actually sensible.
73          * In particular, we can't make a URL without a protocol field.
74          * Without either a host or pathname (depending on the scheme),
75          * we have no primary key. And without a username and password,
76          * we are not actually storing a credential.
77          */
78         if (!c->protocol || !(c->host || c->path) ||
79             !c->username || !c->password)
80                 return;
81
82         strbuf_addf(&buf, "%s://", c->protocol);
83         strbuf_addstr_urlencode(&buf, c->username, 1);
84         strbuf_addch(&buf, ':');
85         strbuf_addstr_urlencode(&buf, c->password, 1);
86         strbuf_addch(&buf, '@');
87         if (c->host)
88                 strbuf_addstr_urlencode(&buf, c->host, 1);
89         if (c->path) {
90                 strbuf_addch(&buf, '/');
91                 strbuf_addstr_urlencode(&buf, c->path, 0);
92         }
93
94         rewrite_credential_file(fn, c, &buf);
95         strbuf_release(&buf);
96 }
97
98 static void remove_credential(const char *fn, struct credential *c)
99 {
100         /*
101          * Sanity check that we actually have something to match
102          * against. The input we get is a restrictive pattern,
103          * so technically a blank credential means "erase everything".
104          * But it is too easy to accidentally send this, since it is equivalent
105          * to empty input. So explicitly disallow it, and require that the
106          * pattern have some actual content to match.
107          */
108         if (c->protocol || c->host || c->path || c->username)
109                 rewrite_credential_file(fn, c, NULL);
110 }
111
112 static int lookup_credential(const char *fn, struct credential *c)
113 {
114         parse_credential_file(fn, c, print_entry, NULL);
115         return c->username && c->password;
116 }
117
118 int main(int argc, char **argv)
119 {
120         const char * const usage[] = {
121                 "git credential-store [options] <action>",
122                 NULL
123         };
124         const char *op;
125         struct credential c = CREDENTIAL_INIT;
126         char *file = NULL;
127         struct option options[] = {
128                 OPT_STRING(0, "file", &file, "path",
129                            "fetch and store credentials in <path>"),
130                 OPT_END()
131         };
132
133         umask(077);
134
135         argc = parse_options(argc, (const char **)argv, NULL, options, usage, 0);
136         if (argc != 1)
137                 usage_with_options(usage, options);
138         op = argv[0];
139
140         if (!file)
141                 file = expand_user_path("~/.git-credentials");
142         if (!file)
143                 die("unable to set up default path; use --file");
144
145         if (credential_read(&c, stdin) < 0)
146                 die("unable to read credential");
147
148         if (!strcmp(op, "get"))
149                 lookup_credential(file, &c);
150         else if (!strcmp(op, "erase"))
151                 remove_credential(file, &c);
152         else if (!strcmp(op, "store"))
153                 store_credential(file, &c);
154         else
155                 ; /* Ignore unknown operation. */
156
157         return 0;
158 }