FIXME: hotpatch for compatibility with latest hg
[git] / credential.c
1 #include "cache.h"
2 #include "credential.h"
3 #include "quote.h"
4 #include "string-list.h"
5 #include "run-command.h"
6
7 static struct string_list default_methods;
8
9 static int credential_config_callback(const char *var, const char *value,
10                                       void *data)
11 {
12         struct credential *c = data;
13
14         if (!value)
15                 return 0;
16
17         var = skip_prefix(var, "credential.");
18         if (!var)
19                 return 0;
20
21         var = skip_prefix(var, c->unique);
22         if (!var)
23                 return 0;
24
25         if (*var != '.')
26                 return 0;
27         var++;
28
29         if (!strcmp(var, "username")) {
30                 if (!c->username)
31                         c->username = xstrdup(value);
32         }
33         else if (!strcmp(var, "password")) {
34                 free(c->password);
35                 c->password = xstrdup(value);
36         }
37         return 0;
38 }
39
40 void credential_from_config(struct credential *c)
41 {
42         if (c->unique)
43                 git_config(credential_config_callback, c);
44 }
45
46 static char *credential_ask_one(const char *what, const char *desc)
47 {
48         struct strbuf prompt = STRBUF_INIT;
49         char *r;
50
51         if (desc)
52                 strbuf_addf(&prompt, "%s for '%s': ", what, desc);
53         else
54                 strbuf_addf(&prompt, "%s: ", what);
55
56         /* FIXME: for usernames, we should do something less magical that
57          * actually echoes the characters. However, we need to read from
58          * /dev/tty and not stdio, which is not portable (but getpass will do
59          * it for us). http.c uses the same workaround. */
60         r = git_getpass(prompt.buf);
61
62         strbuf_release(&prompt);
63         return xstrdup(r);
64 }
65
66 int credential_getpass(struct credential *c)
67 {
68         credential_from_config(c);
69
70         if (!c->username)
71                 c->username = credential_ask_one("Username", c->description);
72         if (!c->password)
73                 c->password = credential_ask_one("Password", c->description);
74         return 0;
75 }
76
77 static int read_credential_response(struct credential *c, FILE *fp)
78 {
79         struct strbuf response = STRBUF_INIT;
80
81         while (strbuf_getline(&response, fp, '\n') != EOF) {
82                 char *key = response.buf;
83                 char *value = strchr(key, '=');
84
85                 if (!value) {
86                         warning("bad output from credential helper: %s", key);
87                         strbuf_release(&response);
88                         return -1;
89                 }
90                 *value++ = '\0';
91
92                 if (!strcmp(key, "username")) {
93                         free(c->username);
94                         c->username = xstrdup(value);
95                 }
96                 else if (!strcmp(key, "password")) {
97                         free(c->password);
98                         c->password = xstrdup(value);
99                 }
100                 /* ignore other responses; we don't know what they mean */
101         }
102
103         strbuf_release(&response);
104         return 0;
105 }
106
107 static int run_credential_helper(struct credential *c, const char *cmd)
108 {
109         struct child_process helper;
110         const char *argv[] = { NULL, NULL };
111         FILE *fp;
112         int r;
113
114         memset(&helper, 0, sizeof(helper));
115         argv[0] = cmd;
116         helper.argv = argv;
117         helper.use_shell = 1;
118         helper.no_stdin = 1;
119         helper.out = -1;
120
121         if (start_command(&helper))
122                 return -1;
123         fp = xfdopen(helper.out, "r");
124
125         r = read_credential_response(c, fp);
126
127         fclose(fp);
128         if (finish_command(&helper))
129                 r = -1;
130
131         return r;
132 }
133
134 static void add_item(struct strbuf *out, const char *key, const char *value)
135 {
136         if (!value)
137                 return;
138         strbuf_addf(out, " --%s=", key);
139         sq_quote_buf(out, value);
140 }
141
142 static int first_word_is_alnum(const char *s)
143 {
144         for (; *s && *s != ' '; s++)
145                 if (!isalnum(*s))
146                         return 0;
147         return 1;
148 }
149
150 static int credential_do(struct credential *c, const char *method,
151                          const char *extra)
152 {
153         struct strbuf cmd = STRBUF_INIT;
154         int r;
155
156         if (first_word_is_alnum(method))
157                 strbuf_addf(&cmd, "git credential-%s", method);
158         else
159                 strbuf_addstr(&cmd, method);
160
161         if (extra)
162                 strbuf_addf(&cmd, " %s", extra);
163
164         add_item(&cmd, "description", c->description);
165         add_item(&cmd, "unique", c->unique);
166         add_item(&cmd, "username", c->username);
167
168         r = run_credential_helper(c, cmd.buf);
169
170         strbuf_release(&cmd);
171         return r;
172 }
173
174 void credential_fill(struct credential *c, const struct string_list *methods)
175 {
176         struct strbuf err = STRBUF_INIT;
177
178         if (!methods)
179                 methods = &default_methods;
180
181         if (!credential_fill_gently(c, methods))
182                 return;
183
184         strbuf_addstr(&err, "unable to get credentials");
185         if (c->description)
186                 strbuf_addf(&err, "for '%s'", c->description);
187         if (methods->nr == 1)
188                 strbuf_addf(&err, "; tried '%s'", methods->items[0].string);
189         else {
190                 int i;
191                 strbuf_addstr(&err, "; tried:");
192                 for (i = 0; i < methods->nr; i++)
193                         strbuf_addf(&err, "\n  %s", methods->items[i].string);
194         }
195         die("%s", err.buf);
196 }
197
198 int credential_fill_gently(struct credential *c,
199                            const struct string_list *methods)
200 {
201         int i;
202
203         if (c->username && c->password)
204                 return 0;
205
206         if (!methods)
207                 methods = &default_methods;
208
209         if (!methods->nr)
210                 return credential_getpass(c);
211
212         for (i = 0; i < methods->nr; i++) {
213                 if (!credential_do(c, methods->items[i].string, NULL) &&
214                     c->username && c->password)
215                         return 0;
216         }
217
218         return -1;
219 }
220
221 void credential_reject(struct credential *c, const struct string_list *methods)
222 {
223         int i;
224
225         if (!methods)
226                 methods = &default_methods;
227
228         if (c->username) {
229                 for (i = 0; i < methods->nr; i++) {
230                         /* ignore errors, there's nothing we can do */
231                         credential_do(c, methods->items[i].string, "--reject");
232                 }
233         }
234
235         free(c->username);
236         c->username = NULL;
237         free(c->password);
238         c->password = NULL;
239 }
240
241 int git_default_credential_config(const char *var, const char *value)
242 {
243         if (!strcmp(var, "credential.helper")) {
244                 if (!value)
245                         return config_error_nonbool(var);
246                 string_list_append(&default_methods, xstrdup(value));
247                 return 0;
248         }
249
250         return 0;
251 }