submodule: Use cat instead of echo to avoid DOS line-endings
[git/dscho.git] / credential.c
blob7a0c751363b6989984ff6887380a2e62e6130b49
1 #include "cache.h"
2 #include "credential.h"
3 #include "quote.h"
4 #include "string-list.h"
5 #include "run-command.h"
7 static struct string_list default_methods;
9 static int credential_config_callback(const char *var, const char *value,
10 void *data)
12 struct credential *c = data;
14 if (!value)
15 return 0;
17 var = skip_prefix(var, "credential.");
18 if (!var)
19 return 0;
21 var = skip_prefix(var, c->unique);
22 if (!var)
23 return 0;
25 if (*var != '.')
26 return 0;
27 var++;
29 if (!strcmp(var, "username")) {
30 if (!c->username)
31 c->username = xstrdup(value);
33 else if (!strcmp(var, "password")) {
34 free(c->password);
35 c->password = xstrdup(value);
37 return 0;
40 void credential_from_config(struct credential *c)
42 if (c->unique)
43 git_config(credential_config_callback, c);
46 static char *credential_ask_one(const char *what, const char *desc)
48 struct strbuf prompt = STRBUF_INIT;
49 char *r;
51 if (desc)
52 strbuf_addf(&prompt, "%s for '%s': ", what, desc);
53 else
54 strbuf_addf(&prompt, "%s: ", what);
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);
62 strbuf_release(&prompt);
63 return xstrdup(r);
66 int credential_getpass(struct credential *c)
68 credential_from_config(c);
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;
77 static int read_credential_response(struct credential *c, FILE *fp)
79 struct strbuf response = STRBUF_INIT;
81 while (strbuf_getline(&response, fp, '\n') != EOF) {
82 char *key = response.buf;
83 char *value = strchr(key, '=');
85 if (!value) {
86 warning("bad output from credential helper: %s", key);
87 strbuf_release(&response);
88 return -1;
90 *value++ = '\0';
92 if (!strcmp(key, "username")) {
93 free(c->username);
94 c->username = xstrdup(value);
96 else if (!strcmp(key, "password")) {
97 free(c->password);
98 c->password = xstrdup(value);
100 /* ignore other responses; we don't know what they mean */
103 strbuf_release(&response);
104 return 0;
107 static int run_credential_helper(struct credential *c, const char *cmd)
109 struct child_process helper;
110 const char *argv[] = { NULL, NULL };
111 FILE *fp;
112 int r;
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;
121 if (start_command(&helper))
122 return -1;
123 fp = xfdopen(helper.out, "r");
125 r = read_credential_response(c, fp);
127 fclose(fp);
128 if (finish_command(&helper))
129 r = -1;
131 return r;
134 static void add_item(struct strbuf *out, const char *key, const char *value)
136 if (!value)
137 return;
138 strbuf_addf(out, " --%s=", key);
139 sq_quote_buf(out, value);
142 static int first_word_is_alnum(const char *s)
144 for (; *s && *s != ' '; s++)
145 if (!isalnum(*s))
146 return 0;
147 return 1;
150 static int credential_do(struct credential *c, const char *method,
151 const char *extra)
153 struct strbuf cmd = STRBUF_INIT;
154 int r;
156 if (first_word_is_alnum(method))
157 strbuf_addf(&cmd, "git credential-%s", method);
158 else
159 strbuf_addstr(&cmd, method);
161 if (extra)
162 strbuf_addf(&cmd, " %s", extra);
164 add_item(&cmd, "description", c->description);
165 add_item(&cmd, "unique", c->unique);
166 add_item(&cmd, "username", c->username);
168 r = run_credential_helper(c, cmd.buf);
170 strbuf_release(&cmd);
171 return r;
174 void credential_fill(struct credential *c, const struct string_list *methods)
176 struct strbuf err = STRBUF_INIT;
178 if (!methods)
179 methods = &default_methods;
181 if (!credential_fill_gently(c, methods))
182 return;
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);
195 die("%s", err.buf);
198 int credential_fill_gently(struct credential *c,
199 const struct string_list *methods)
201 int i;
203 if (c->username && c->password)
204 return 0;
206 if (!methods)
207 methods = &default_methods;
209 if (!methods->nr)
210 return credential_getpass(c);
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;
218 return -1;
221 void credential_reject(struct credential *c, const struct string_list *methods)
223 int i;
225 if (!methods)
226 methods = &default_methods;
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");
235 free(c->username);
236 c->username = NULL;
237 free(c->password);
238 c->password = NULL;
241 int git_default_credential_config(const char *var, const char *value)
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;
250 return 0;