http-backend: more explict LocationMatch
[git/git.git] / remote-curl.c
1 #include "cache.h"
2 #include "remote.h"
3 #include "strbuf.h"
4 #include "walker.h"
5 #include "http.h"
6 #include "exec_cmd.h"
7 #include "run-command.h"
8
9 static struct remote *remote;
10 static const char *url;
11 static struct walker *walker;
12
13 struct options {
14 int verbosity;
15 unsigned long depth;
16 unsigned progress : 1,
17 followtags : 1,
18 dry_run : 1;
19 };
20 static struct options options;
21
22 static void init_walker(void)
23 {
24 if (!walker)
25 walker = get_http_walker(url, remote);
26 }
27
28 static int set_option(const char *name, const char *value)
29 {
30 if (!strcmp(name, "verbosity")) {
31 char *end;
32 int v = strtol(value, &end, 10);
33 if (value == end || *end)
34 return -1;
35 options.verbosity = v;
36 return 0;
37 }
38 else if (!strcmp(name, "progress")) {
39 if (!strcmp(value, "true"))
40 options.progress = 1;
41 else if (!strcmp(value, "false"))
42 options.progress = 0;
43 else
44 return -1;
45 return 1 /* TODO implement later */;
46 }
47 else if (!strcmp(name, "depth")) {
48 char *end;
49 unsigned long v = strtoul(value, &end, 10);
50 if (value == end || *end)
51 return -1;
52 options.depth = v;
53 return 1 /* TODO implement later */;
54 }
55 else if (!strcmp(name, "followtags")) {
56 if (!strcmp(value, "true"))
57 options.followtags = 1;
58 else if (!strcmp(value, "false"))
59 options.followtags = 0;
60 else
61 return -1;
62 return 1 /* TODO implement later */;
63 }
64 else if (!strcmp(name, "dry-run")) {
65 if (!strcmp(value, "true"))
66 options.dry_run = 1;
67 else if (!strcmp(value, "false"))
68 options.dry_run = 0;
69 else
70 return -1;
71 return 0;
72 }
73 else {
74 return 1 /* unsupported */;
75 }
76 }
77
78 static struct ref *get_refs(void)
79 {
80 struct strbuf buffer = STRBUF_INIT;
81 char *data, *start, *mid;
82 char *ref_name;
83 char *refs_url;
84 int i = 0;
85 int http_ret;
86
87 struct ref *refs = NULL;
88 struct ref *ref = NULL;
89 struct ref *last_ref = NULL;
90
91 refs_url = xmalloc(strlen(url) + 11);
92 sprintf(refs_url, "%s/info/refs", url);
93
94 init_walker();
95 http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
96 switch (http_ret) {
97 case HTTP_OK:
98 break;
99 case HTTP_MISSING_TARGET:
100 die("%s not found: did you run git update-server-info on the"
101 " server?", refs_url);
102 default:
103 http_error(refs_url, http_ret);
104 die("HTTP request failed");
105 }
106
107 data = buffer.buf;
108 start = NULL;
109 mid = data;
110 while (i < buffer.len) {
111 if (!start) {
112 start = &data[i];
113 }
114 if (data[i] == '\t')
115 mid = &data[i];
116 if (data[i] == '\n') {
117 data[i] = 0;
118 ref_name = mid + 1;
119 ref = xmalloc(sizeof(struct ref) +
120 strlen(ref_name) + 1);
121 memset(ref, 0, sizeof(struct ref));
122 strcpy(ref->name, ref_name);
123 get_sha1_hex(start, ref->old_sha1);
124 if (!refs)
125 refs = ref;
126 if (last_ref)
127 last_ref->next = ref;
128 last_ref = ref;
129 start = NULL;
130 }
131 i++;
132 }
133
134 strbuf_release(&buffer);
135
136 ref = alloc_ref("HEAD");
137 if (!walker->fetch_ref(walker, ref) &&
138 !resolve_remote_symref(ref, refs)) {
139 ref->next = refs;
140 refs = ref;
141 } else {
142 free(ref);
143 }
144
145 strbuf_release(&buffer);
146 free(refs_url);
147 return refs;
148 }
149
150 static void output_refs(struct ref *refs)
151 {
152 struct ref *posn;
153 for (posn = refs; posn; posn = posn->next) {
154 if (posn->symref)
155 printf("@%s %s\n", posn->symref, posn->name);
156 else
157 printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
158 }
159 printf("\n");
160 fflush(stdout);
161 free_refs(refs);
162 }
163
164 static int fetch_dumb(int nr_heads, struct ref **to_fetch)
165 {
166 char **targets = xmalloc(nr_heads * sizeof(char*));
167 int ret, i;
168
169 for (i = 0; i < nr_heads; i++)
170 targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
171
172 init_walker();
173 walker->get_all = 1;
174 walker->get_tree = 1;
175 walker->get_history = 1;
176 walker->get_verbosely = options.verbosity >= 3;
177 walker->get_recover = 0;
178 ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
179
180 for (i = 0; i < nr_heads; i++)
181 free(targets[i]);
182 free(targets);
183
184 return ret ? error("Fetch failed.") : 0;
185 }
186
187 static void parse_fetch(struct strbuf *buf)
188 {
189 struct ref **to_fetch = NULL;
190 struct ref *list_head = NULL;
191 struct ref **list = &list_head;
192 int alloc_heads = 0, nr_heads = 0;
193
194 do {
195 if (!prefixcmp(buf->buf, "fetch ")) {
196 char *p = buf->buf + strlen("fetch ");
197 char *name;
198 struct ref *ref;
199 unsigned char old_sha1[20];
200
201 if (strlen(p) < 40 || get_sha1_hex(p, old_sha1))
202 die("protocol error: expected sha/ref, got %s'", p);
203 if (p[40] == ' ')
204 name = p + 41;
205 else if (!p[40])
206 name = "";
207 else
208 die("protocol error: expected sha/ref, got %s'", p);
209
210 ref = alloc_ref(name);
211 hashcpy(ref->old_sha1, old_sha1);
212
213 *list = ref;
214 list = &ref->next;
215
216 ALLOC_GROW(to_fetch, nr_heads + 1, alloc_heads);
217 to_fetch[nr_heads++] = ref;
218 }
219 else
220 die("http transport does not support %s", buf->buf);
221
222 strbuf_reset(buf);
223 if (strbuf_getline(buf, stdin, '\n') == EOF)
224 return;
225 if (!*buf->buf)
226 break;
227 } while (1);
228
229 if (fetch_dumb(nr_heads, to_fetch))
230 exit(128); /* error already reported */
231 free_refs(list_head);
232 free(to_fetch);
233
234 printf("\n");
235 fflush(stdout);
236 strbuf_reset(buf);
237 }
238
239 static int push_dav(int nr_spec, char **specs)
240 {
241 const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
242 int argc = 0, i;
243
244 argv[argc++] = "http-push";
245 argv[argc++] = "--helper-status";
246 if (options.dry_run)
247 argv[argc++] = "--dry-run";
248 if (options.verbosity > 1)
249 argv[argc++] = "--verbose";
250 argv[argc++] = url;
251 for (i = 0; i < nr_spec; i++)
252 argv[argc++] = specs[i];
253 argv[argc++] = NULL;
254
255 if (run_command_v_opt(argv, RUN_GIT_CMD))
256 die("git-%s failed", argv[0]);
257 free(argv);
258 return 0;
259 }
260
261 static void parse_push(struct strbuf *buf)
262 {
263 char **specs = NULL;
264 int alloc_spec = 0, nr_spec = 0, i;
265
266 do {
267 if (!prefixcmp(buf->buf, "push ")) {
268 ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
269 specs[nr_spec++] = xstrdup(buf->buf + 5);
270 }
271 else
272 die("http transport does not support %s", buf->buf);
273
274 strbuf_reset(buf);
275 if (strbuf_getline(buf, stdin, '\n') == EOF)
276 return;
277 if (!*buf->buf)
278 break;
279 } while (1);
280
281 if (push_dav(nr_spec, specs))
282 exit(128); /* error already reported */
283 for (i = 0; i < nr_spec; i++)
284 free(specs[i]);
285 free(specs);
286
287 printf("\n");
288 fflush(stdout);
289 }
290
291 int main(int argc, const char **argv)
292 {
293 struct strbuf buf = STRBUF_INIT;
294
295 git_extract_argv0_path(argv[0]);
296 setup_git_directory();
297 if (argc < 2) {
298 fprintf(stderr, "Remote needed\n");
299 return 1;
300 }
301
302 options.verbosity = 1;
303 options.progress = !!isatty(2);
304
305 remote = remote_get(argv[1]);
306
307 if (argc > 2) {
308 url = argv[2];
309 } else {
310 url = remote->url[0];
311 }
312
313 do {
314 if (strbuf_getline(&buf, stdin, '\n') == EOF)
315 break;
316 if (!prefixcmp(buf.buf, "fetch ")) {
317 parse_fetch(&buf);
318
319 } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
320 output_refs(get_refs());
321
322 } else if (!prefixcmp(buf.buf, "push ")) {
323 parse_push(&buf);
324
325 } else if (!prefixcmp(buf.buf, "option ")) {
326 char *name = buf.buf + strlen("option ");
327 char *value = strchr(name, ' ');
328 int result;
329
330 if (value)
331 *value++ = '\0';
332 else
333 value = "true";
334
335 result = set_option(name, value);
336 if (!result)
337 printf("ok\n");
338 else if (result < 0)
339 printf("error invalid value\n");
340 else
341 printf("unsupported\n");
342 fflush(stdout);
343
344 } else if (!strcmp(buf.buf, "capabilities")) {
345 printf("fetch\n");
346 printf("option\n");
347 printf("push\n");
348 printf("\n");
349 fflush(stdout);
350 } else {
351 return 1;
352 }
353 strbuf_reset(&buf);
354 } while (1);
355 return 0;
356 }