Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 368 lines (309 sloc) 7.756 kb
abca927 Jeff King introduce credentials API
peff authored
1 #include "cache.h"
2 #include "credential.h"
3 #include "string-list.h"
4 #include "run-command.h"
d3e847c Jeff King credential: add function for parsing url components
peff authored
5 #include "url.h"
d3c58b8 Jeff King move git_getpass to its own source file
peff authored
6 #include "prompt.h"
abca927 Jeff King introduce credentials API
peff authored
7
8 void credential_init(struct credential *c)
9 {
10 memset(c, 0, sizeof(*c));
11 c->helpers.strdup_strings = 1;
12 }
13
14 void credential_clear(struct credential *c)
15 {
16 free(c->protocol);
17 free(c->host);
18 free(c->path);
19 free(c->username);
20 free(c->password);
21 string_list_clear(&c->helpers, 0);
22
23 credential_init(c);
24 }
25
1182507 Jeff King credential: apply helper config
peff authored
26 int credential_match(const struct credential *want,
27 const struct credential *have)
28 {
29 #define CHECK(x) (!want->x || (have->x && !strcmp(want->x, have->x)))
30 return CHECK(protocol) &&
31 CHECK(host) &&
32 CHECK(path) &&
33 CHECK(username);
34 #undef CHECK
35 }
36
37 static int credential_config_callback(const char *var, const char *value,
38 void *data)
39 {
40 struct credential *c = data;
41 const char *key, *dot;
42
43 key = skip_prefix(var, "credential.");
44 if (!key)
45 return 0;
46
47 if (!value)
48 return config_error_nonbool(var);
49
50 dot = strrchr(key, '.');
51 if (dot) {
52 struct credential want = CREDENTIAL_INIT;
53 char *url = xmemdupz(key, dot - key);
54 int matched;
55
56 credential_from_url(&want, url);
57 matched = credential_match(&want, c);
58
59 credential_clear(&want);
60 free(url);
61
62 if (!matched)
63 return 0;
64 key = dot + 1;
65 }
66
67 if (!strcmp(key, "helper"))
68 string_list_append(&c->helpers, value);
d574242 Jeff King credential: add credential.*.username
peff authored
69 else if (!strcmp(key, "username")) {
70 if (!c->username)
71 c->username = xstrdup(value);
72 }
a78fbb4 Jeff King credential: make relevance of http path configurable
peff authored
73 else if (!strcmp(key, "usehttppath"))
74 c->use_http_path = git_config_bool(var, value);
1182507 Jeff King credential: apply helper config
peff authored
75
76 return 0;
77 }
78
a78fbb4 Jeff King credential: make relevance of http path configurable
peff authored
79 static int proto_is_http(const char *s)
80 {
81 if (!s)
82 return 0;
83 return !strcmp(s, "https") || !strcmp(s, "http");
84 }
85
1182507 Jeff King credential: apply helper config
peff authored
86 static void credential_apply_config(struct credential *c)
87 {
88 if (c->configured)
89 return;
90 git_config(credential_config_callback, c);
91 c->configured = 1;
a78fbb4 Jeff King credential: make relevance of http path configurable
peff authored
92
93 if (!c->use_http_path && proto_is_http(c->protocol)) {
94 free(c->path);
95 c->path = NULL;
96 }
1182507 Jeff King credential: apply helper config
peff authored
97 }
98
abca927 Jeff King introduce credentials API
peff authored
99 static void credential_describe(struct credential *c, struct strbuf *out)
100 {
101 if (!c->protocol)
102 return;
103 strbuf_addf(out, "%s://", c->protocol);
104 if (c->username && *c->username)
105 strbuf_addf(out, "%s@", c->username);
106 if (c->host)
107 strbuf_addstr(out, c->host);
108 if (c->path)
109 strbuf_addf(out, "/%s", c->path);
110 }
111
ce77aa4 Jeff King credential: use git_prompt instead of git_getpass
peff authored
112 static char *credential_ask_one(const char *what, struct credential *c,
113 int flags)
abca927 Jeff King introduce credentials API
peff authored
114 {
115 struct strbuf desc = STRBUF_INIT;
116 struct strbuf prompt = STRBUF_INIT;
117 char *r;
118
119 credential_describe(c, &desc);
120 if (desc.len)
121 strbuf_addf(&prompt, "%s for '%s': ", what, desc.buf);
122 else
123 strbuf_addf(&prompt, "%s: ", what);
124
ce77aa4 Jeff King credential: use git_prompt instead of git_getpass
peff authored
125 r = git_prompt(prompt.buf, flags);
abca927 Jeff King introduce credentials API
peff authored
126
127 strbuf_release(&desc);
128 strbuf_release(&prompt);
129 return xstrdup(r);
130 }
131
132 static void credential_getpass(struct credential *c)
133 {
134 if (!c->username)
ce77aa4 Jeff King credential: use git_prompt instead of git_getpass
peff authored
135 c->username = credential_ask_one("Username", c,
136 PROMPT_ASKPASS|PROMPT_ECHO);
abca927 Jeff King introduce credentials API
peff authored
137 if (!c->password)
ce77aa4 Jeff King credential: use git_prompt instead of git_getpass
peff authored
138 c->password = credential_ask_one("Password", c,
139 PROMPT_ASKPASS);
abca927 Jeff King introduce credentials API
peff authored
140 }
141
142 int credential_read(struct credential *c, FILE *fp)
143 {
144 struct strbuf line = STRBUF_INIT;
145
146 while (strbuf_getline(&line, fp, '\n') != EOF) {
147 char *key = line.buf;
148 char *value = strchr(key, '=');
149
150 if (!line.len)
151 break;
152
153 if (!value) {
154 warning("invalid credential line: %s", key);
155 strbuf_release(&line);
156 return -1;
157 }
158 *value++ = '\0';
159
160 if (!strcmp(key, "username")) {
161 free(c->username);
162 c->username = xstrdup(value);
163 } else if (!strcmp(key, "password")) {
164 free(c->password);
165 c->password = xstrdup(value);
166 } else if (!strcmp(key, "protocol")) {
167 free(c->protocol);
168 c->protocol = xstrdup(value);
169 } else if (!strcmp(key, "host")) {
170 free(c->host);
171 c->host = xstrdup(value);
172 } else if (!strcmp(key, "path")) {
173 free(c->path);
174 c->path = xstrdup(value);
9c183a7 Jeff King credential: convert "url" attribute into its parsed subparts
peff authored
175 } else if (!strcmp(key, "url")) {
176 credential_from_url(c, value);
abca927 Jeff King introduce credentials API
peff authored
177 }
178 /*
179 * Ignore other lines; we don't know what they mean, but
180 * this future-proofs us when later versions of git do
181 * learn new lines, and the helpers are updated to match.
182 */
183 }
184
185 strbuf_release(&line);
186 return 0;
187 }
188
189 static void credential_write_item(FILE *fp, const char *key, const char *value)
190 {
191 if (!value)
192 return;
193 fprintf(fp, "%s=%s\n", key, value);
194 }
195
2d6dc18 Matthieu Moy git credential fill: output the whole 'struct credential'
moy authored
196 void credential_write(const struct credential *c, FILE *fp)
abca927 Jeff King introduce credentials API
peff authored
197 {
198 credential_write_item(fp, "protocol", c->protocol);
199 credential_write_item(fp, "host", c->host);
200 credential_write_item(fp, "path", c->path);
201 credential_write_item(fp, "username", c->username);
202 credential_write_item(fp, "password", c->password);
203 }
204
205 static int run_credential_helper(struct credential *c,
206 const char *cmd,
207 int want_output)
208 {
209 struct child_process helper;
210 const char *argv[] = { NULL, NULL };
211 FILE *fp;
212
213 memset(&helper, 0, sizeof(helper));
214 argv[0] = cmd;
215 helper.argv = argv;
216 helper.use_shell = 1;
217 helper.in = -1;
218 if (want_output)
219 helper.out = -1;
220 else
221 helper.no_stdout = 1;
222
223 if (start_command(&helper) < 0)
224 return -1;
225
226 fp = xfdopen(helper.in, "w");
227 credential_write(c, fp);
228 fclose(fp);
229
230 if (want_output) {
231 int r;
232 fp = xfdopen(helper.out, "r");
233 r = credential_read(c, fp);
234 fclose(fp);
235 if (r < 0) {
236 finish_command(&helper);
237 return -1;
238 }
239 }
240
241 if (finish_command(&helper))
242 return -1;
243 return 0;
244 }
245
246 static int credential_do(struct credential *c, const char *helper,
247 const char *operation)
248 {
249 struct strbuf cmd = STRBUF_INIT;
250 int r;
251
252 if (helper[0] == '!')
253 strbuf_addstr(&cmd, helper + 1);
254 else if (is_absolute_path(helper))
255 strbuf_addstr(&cmd, helper);
256 else
257 strbuf_addf(&cmd, "git credential-%s", helper);
258
259 strbuf_addf(&cmd, " %s", operation);
260 r = run_credential_helper(c, cmd.buf, !strcmp(operation, "get"));
261
262 strbuf_release(&cmd);
263 return r;
264 }
265
266 void credential_fill(struct credential *c)
267 {
268 int i;
269
270 if (c->username && c->password)
271 return;
272
1182507 Jeff King credential: apply helper config
peff authored
273 credential_apply_config(c);
274
abca927 Jeff King introduce credentials API
peff authored
275 for (i = 0; i < c->helpers.nr; i++) {
276 credential_do(c, c->helpers.items[i].string, "get");
277 if (c->username && c->password)
278 return;
279 }
280
281 credential_getpass(c);
282 if (!c->username && !c->password)
283 die("unable to get password from user");
284 }
285
286 void credential_approve(struct credential *c)
287 {
288 int i;
289
290 if (c->approved)
291 return;
292 if (!c->username || !c->password)
293 return;
294
1182507 Jeff King credential: apply helper config
peff authored
295 credential_apply_config(c);
296
abca927 Jeff King introduce credentials API
peff authored
297 for (i = 0; i < c->helpers.nr; i++)
298 credential_do(c, c->helpers.items[i].string, "store");
299 c->approved = 1;
300 }
301
302 void credential_reject(struct credential *c)
303 {
304 int i;
305
1182507 Jeff King credential: apply helper config
peff authored
306 credential_apply_config(c);
307
abca927 Jeff King introduce credentials API
peff authored
308 for (i = 0; i < c->helpers.nr; i++)
309 credential_do(c, c->helpers.items[i].string, "erase");
310
311 free(c->username);
312 c->username = NULL;
313 free(c->password);
314 c->password = NULL;
315 c->approved = 0;
316 }
d3e847c Jeff King credential: add function for parsing url components
peff authored
317
318 void credential_from_url(struct credential *c, const char *url)
319 {
320 const char *at, *colon, *cp, *slash, *host, *proto_end;
321
322 credential_clear(c);
323
324 /*
325 * Match one of:
326 * (1) proto://<host>/...
327 * (2) proto://<user>@<host>/...
328 * (3) proto://<user>:<pass>@<host>/...
329 */
330 proto_end = strstr(url, "://");
331 if (!proto_end)
332 return;
333 cp = proto_end + 3;
334 at = strchr(cp, '@');
335 colon = strchr(cp, ':');
336 slash = strchrnul(cp, '/');
337
338 if (!at || slash <= at) {
339 /* Case (1) */
340 host = cp;
341 }
342 else if (!colon || at <= colon) {
343 /* Case (2) */
344 c->username = url_decode_mem(cp, at - cp);
345 host = at + 1;
346 } else {
347 /* Case (3) */
348 c->username = url_decode_mem(cp, colon - cp);
349 c->password = url_decode_mem(colon + 1, at - (colon + 1));
350 host = at + 1;
351 }
352
353 if (proto_end - url > 0)
354 c->protocol = xmemdupz(url, proto_end - url);
355 if (slash - host > 0)
356 c->host = url_decode_mem(host, slash - host);
357 /* Trim leading and trailing slashes from path */
358 while (*slash == '/')
359 slash++;
360 if (*slash) {
361 char *p;
362 c->path = url_decode(slash);
363 p = c->path + strlen(c->path) - 1;
364 while (p > c->path && *p == '/')
365 *p-- = '\0';
366 }
367 }
Something went wrong with that request. Please try again.