remote-curl: move ref-parsing code up in file
[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 #include "pkt-line.h"
9 #include "sideband.h"
10
11 static struct remote *remote;
12 static const char *url; /* always ends with a trailing slash */
13
14 struct options {
15         int verbosity;
16         unsigned long depth;
17         unsigned progress : 1,
18                 followtags : 1,
19                 dry_run : 1,
20                 thin : 1;
21 };
22 static struct options options;
23
24 static int set_option(const char *name, const char *value)
25 {
26         if (!strcmp(name, "verbosity")) {
27                 char *end;
28                 int v = strtol(value, &end, 10);
29                 if (value == end || *end)
30                         return -1;
31                 options.verbosity = v;
32                 return 0;
33         }
34         else if (!strcmp(name, "progress")) {
35                 if (!strcmp(value, "true"))
36                         options.progress = 1;
37                 else if (!strcmp(value, "false"))
38                         options.progress = 0;
39                 else
40                         return -1;
41                 return 0;
42         }
43         else if (!strcmp(name, "depth")) {
44                 char *end;
45                 unsigned long v = strtoul(value, &end, 10);
46                 if (value == end || *end)
47                         return -1;
48                 options.depth = v;
49                 return 0;
50         }
51         else if (!strcmp(name, "followtags")) {
52                 if (!strcmp(value, "true"))
53                         options.followtags = 1;
54                 else if (!strcmp(value, "false"))
55                         options.followtags = 0;
56                 else
57                         return -1;
58                 return 0;
59         }
60         else if (!strcmp(name, "dry-run")) {
61                 if (!strcmp(value, "true"))
62                         options.dry_run = 1;
63                 else if (!strcmp(value, "false"))
64                         options.dry_run = 0;
65                 else
66                         return -1;
67                 return 0;
68         }
69         else {
70                 return 1 /* unsupported */;
71         }
72 }
73
74 struct discovery {
75         const char *service;
76         char *buf_alloc;
77         char *buf;
78         size_t len;
79         unsigned proto_git : 1;
80 };
81 static struct discovery *last_discovery;
82
83 static struct ref *parse_git_refs(struct discovery *heads, int for_push)
84 {
85         struct ref *list = NULL;
86         get_remote_heads(-1, heads->buf, heads->len, &list,
87                          for_push ? REF_NORMAL : 0, NULL);
88         return list;
89 }
90
91 static struct ref *parse_info_refs(struct discovery *heads)
92 {
93         char *data, *start, *mid;
94         char *ref_name;
95         int i = 0;
96
97         struct ref *refs = NULL;
98         struct ref *ref = NULL;
99         struct ref *last_ref = NULL;
100
101         data = heads->buf;
102         start = NULL;
103         mid = data;
104         while (i < heads->len) {
105                 if (!start) {
106                         start = &data[i];
107                 }
108                 if (data[i] == '\t')
109                         mid = &data[i];
110                 if (data[i] == '\n') {
111                         if (mid - start != 40)
112                                 die("%sinfo/refs not valid: is this a git repository?", url);
113                         data[i] = 0;
114                         ref_name = mid + 1;
115                         ref = xmalloc(sizeof(struct ref) +
116                                       strlen(ref_name) + 1);
117                         memset(ref, 0, sizeof(struct ref));
118                         strcpy(ref->name, ref_name);
119                         get_sha1_hex(start, ref->old_sha1);
120                         if (!refs)
121                                 refs = ref;
122                         if (last_ref)
123                                 last_ref->next = ref;
124                         last_ref = ref;
125                         start = NULL;
126                 }
127                 i++;
128         }
129
130         ref = alloc_ref("HEAD");
131         if (!http_fetch_ref(url, ref) &&
132             !resolve_remote_symref(ref, refs)) {
133                 ref->next = refs;
134                 refs = ref;
135         } else {
136                 free(ref);
137         }
138
139         return refs;
140 }
141
142 static void free_discovery(struct discovery *d)
143 {
144         if (d) {
145                 if (d == last_discovery)
146                         last_discovery = NULL;
147                 free(d->buf_alloc);
148                 free(d);
149         }
150 }
151
152 static struct discovery* discover_refs(const char *service)
153 {
154         struct strbuf exp = STRBUF_INIT;
155         struct strbuf type = STRBUF_INIT;
156         struct strbuf buffer = STRBUF_INIT;
157         struct discovery *last = last_discovery;
158         char *refs_url;
159         int http_ret, maybe_smart = 0;
160
161         if (last && !strcmp(service, last->service))
162                 return last;
163         free_discovery(last);
164
165         strbuf_addf(&buffer, "%sinfo/refs", url);
166         if ((!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) &&
167              git_env_bool("GIT_SMART_HTTP", 1)) {
168                 maybe_smart = 1;
169                 if (!strchr(url, '?'))
170                         strbuf_addch(&buffer, '?');
171                 else
172                         strbuf_addch(&buffer, '&');
173                 strbuf_addf(&buffer, "service=%s", service);
174         }
175         refs_url = strbuf_detach(&buffer, NULL);
176
177         http_ret = http_get_strbuf(refs_url, &type, &buffer, HTTP_NO_CACHE);
178         switch (http_ret) {
179         case HTTP_OK:
180                 break;
181         case HTTP_MISSING_TARGET:
182                 die("%s not found: did you run git update-server-info on the"
183                     " server?", refs_url);
184         case HTTP_NOAUTH:
185                 die("Authentication failed");
186         default:
187                 http_error(refs_url, http_ret);
188                 die("HTTP request failed");
189         }
190
191         last= xcalloc(1, sizeof(*last_discovery));
192         last->service = service;
193         last->buf_alloc = strbuf_detach(&buffer, &last->len);
194         last->buf = last->buf_alloc;
195
196         strbuf_addf(&exp, "application/x-%s-advertisement", service);
197         if (maybe_smart &&
198             (5 <= last->len && last->buf[4] == '#') &&
199             !strbuf_cmp(&exp, &type)) {
200                 char *line;
201
202                 /*
203                  * smart HTTP response; validate that the service
204                  * pkt-line matches our request.
205                  */
206                 line = packet_read_line_buf(&last->buf, &last->len, NULL);
207
208                 strbuf_reset(&exp);
209                 strbuf_addf(&exp, "# service=%s", service);
210                 if (strcmp(line, exp.buf))
211                         die("invalid server response; got '%s'", line);
212                 strbuf_release(&exp);
213
214                 /* The header can include additional metadata lines, up
215                  * until a packet flush marker.  Ignore these now, but
216                  * in the future we might start to scan them.
217                  */
218                 while (packet_read_line_buf(&last->buf, &last->len, NULL))
219                         ;
220
221                 last->proto_git = 1;
222         }
223
224         free(refs_url);
225         strbuf_release(&exp);
226         strbuf_release(&type);
227         strbuf_release(&buffer);
228         last_discovery = last;
229         return last;
230 }
231
232 static struct ref *get_refs(int for_push)
233 {
234         struct discovery *heads;
235
236         if (for_push)
237                 heads = discover_refs("git-receive-pack");
238         else
239                 heads = discover_refs("git-upload-pack");
240
241         if (heads->proto_git)
242                 return parse_git_refs(heads, for_push);
243         return parse_info_refs(heads);
244 }
245
246 static void output_refs(struct ref *refs)
247 {
248         struct ref *posn;
249         for (posn = refs; posn; posn = posn->next) {
250                 if (posn->symref)
251                         printf("@%s %s\n", posn->symref, posn->name);
252                 else
253                         printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
254         }
255         printf("\n");
256         fflush(stdout);
257         free_refs(refs);
258 }
259
260 struct rpc_state {
261         const char *service_name;
262         const char **argv;
263         struct strbuf *stdin_preamble;
264         char *service_url;
265         char *hdr_content_type;
266         char *hdr_accept;
267         char *buf;
268         size_t alloc;
269         size_t len;
270         size_t pos;
271         int in;
272         int out;
273         struct strbuf result;
274         unsigned gzip_request : 1;
275         unsigned initial_buffer : 1;
276 };
277
278 static size_t rpc_out(void *ptr, size_t eltsize,
279                 size_t nmemb, void *buffer_)
280 {
281         size_t max = eltsize * nmemb;
282         struct rpc_state *rpc = buffer_;
283         size_t avail = rpc->len - rpc->pos;
284
285         if (!avail) {
286                 rpc->initial_buffer = 0;
287                 avail = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
288                 if (!avail)
289                         return 0;
290                 rpc->pos = 0;
291                 rpc->len = avail;
292         }
293
294         if (max < avail)
295                 avail = max;
296         memcpy(ptr, rpc->buf + rpc->pos, avail);
297         rpc->pos += avail;
298         return avail;
299 }
300
301 #ifndef NO_CURL_IOCTL
302 static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
303 {
304         struct rpc_state *rpc = clientp;
305
306         switch (cmd) {
307         case CURLIOCMD_NOP:
308                 return CURLIOE_OK;
309
310         case CURLIOCMD_RESTARTREAD:
311                 if (rpc->initial_buffer) {
312                         rpc->pos = 0;
313                         return CURLIOE_OK;
314                 }
315                 fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n");
316                 return CURLIOE_FAILRESTART;
317
318         default:
319                 return CURLIOE_UNKNOWNCMD;
320         }
321 }
322 #endif
323
324 static size_t rpc_in(char *ptr, size_t eltsize,
325                 size_t nmemb, void *buffer_)
326 {
327         size_t size = eltsize * nmemb;
328         struct rpc_state *rpc = buffer_;
329         write_or_die(rpc->in, ptr, size);
330         return size;
331 }
332
333 static int run_slot(struct active_request_slot *slot)
334 {
335         int err;
336         struct slot_results results;
337
338         slot->results = &results;
339         slot->curl_result = curl_easy_perform(slot->curl);
340         finish_active_slot(slot);
341
342         err = handle_curl_result(&results);
343         if (err != HTTP_OK && err != HTTP_REAUTH) {
344                 error("RPC failed; result=%d, HTTP code = %ld",
345                       results.curl_result, results.http_code);
346         }
347
348         return err;
349 }
350
351 static int probe_rpc(struct rpc_state *rpc)
352 {
353         struct active_request_slot *slot;
354         struct curl_slist *headers = NULL;
355         struct strbuf buf = STRBUF_INIT;
356         int err;
357
358         slot = get_active_slot();
359
360         headers = curl_slist_append(headers, rpc->hdr_content_type);
361         headers = curl_slist_append(headers, rpc->hdr_accept);
362
363         curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
364         curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
365         curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
366         curl_easy_setopt(slot->curl, CURLOPT_ENCODING, NULL);
367         curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, "0000");
368         curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, 4);
369         curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
370         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
371         curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf);
372
373         err = run_slot(slot);
374
375         curl_slist_free_all(headers);
376         strbuf_release(&buf);
377         return err;
378 }
379
380 static int post_rpc(struct rpc_state *rpc)
381 {
382         struct active_request_slot *slot;
383         struct curl_slist *headers = NULL;
384         int use_gzip = rpc->gzip_request;
385         char *gzip_body = NULL;
386         size_t gzip_size = 0;
387         int err, large_request = 0;
388
389         /* Try to load the entire request, if we can fit it into the
390          * allocated buffer space we can use HTTP/1.0 and avoid the
391          * chunked encoding mess.
392          */
393         while (1) {
394                 size_t left = rpc->alloc - rpc->len;
395                 char *buf = rpc->buf + rpc->len;
396                 int n;
397
398                 if (left < LARGE_PACKET_MAX) {
399                         large_request = 1;
400                         use_gzip = 0;
401                         break;
402                 }
403
404                 n = packet_read(rpc->out, NULL, NULL, buf, left, 0);
405                 if (!n)
406                         break;
407                 rpc->len += n;
408         }
409
410         if (large_request) {
411                 do {
412                         err = probe_rpc(rpc);
413                 } while (err == HTTP_REAUTH);
414                 if (err != HTTP_OK)
415                         return -1;
416         }
417
418         headers = curl_slist_append(headers, rpc->hdr_content_type);
419         headers = curl_slist_append(headers, rpc->hdr_accept);
420         headers = curl_slist_append(headers, "Expect:");
421
422 retry:
423         slot = get_active_slot();
424
425         curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
426         curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
427         curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
428         curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip");
429
430         if (large_request) {
431                 /* The request body is large and the size cannot be predicted.
432                  * We must use chunked encoding to send it.
433                  */
434                 headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
435                 rpc->initial_buffer = 1;
436                 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
437                 curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
438 #ifndef NO_CURL_IOCTL
439                 curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
440                 curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
441 #endif
442                 if (options.verbosity > 1) {
443                         fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
444                         fflush(stderr);
445                 }
446
447         } else if (gzip_body) {
448                 /*
449                  * If we are looping to retry authentication, then the previous
450                  * run will have set up the headers and gzip buffer already,
451                  * and we just need to send it.
452                  */
453                 curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
454                 curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, gzip_size);
455
456         } else if (use_gzip && 1024 < rpc->len) {
457                 /* The client backend isn't giving us compressed data so
458                  * we can try to deflate it ourselves, this may save on.
459                  * the transfer time.
460                  */
461                 git_zstream stream;
462                 int ret;
463
464                 memset(&stream, 0, sizeof(stream));
465                 git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION);
466                 gzip_size = git_deflate_bound(&stream, rpc->len);
467                 gzip_body = xmalloc(gzip_size);
468
469                 stream.next_in = (unsigned char *)rpc->buf;
470                 stream.avail_in = rpc->len;
471                 stream.next_out = (unsigned char *)gzip_body;
472                 stream.avail_out = gzip_size;
473
474                 ret = git_deflate(&stream, Z_FINISH);
475                 if (ret != Z_STREAM_END)
476                         die("cannot deflate request; zlib deflate error %d", ret);
477
478                 ret = git_deflate_end_gently(&stream);
479                 if (ret != Z_OK)
480                         die("cannot deflate request; zlib end error %d", ret);
481
482                 gzip_size = stream.total_out;
483
484                 headers = curl_slist_append(headers, "Content-Encoding: gzip");
485                 curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
486                 curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, gzip_size);
487
488                 if (options.verbosity > 1) {
489                         fprintf(stderr, "POST %s (gzip %lu to %lu bytes)\n",
490                                 rpc->service_name,
491                                 (unsigned long)rpc->len, (unsigned long)gzip_size);
492                         fflush(stderr);
493                 }
494         } else {
495                 /* We know the complete request size in advance, use the
496                  * more normal Content-Length approach.
497                  */
498                 curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, rpc->buf);
499                 curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, rpc->len);
500                 if (options.verbosity > 1) {
501                         fprintf(stderr, "POST %s (%lu bytes)\n",
502                                 rpc->service_name, (unsigned long)rpc->len);
503                         fflush(stderr);
504                 }
505         }
506
507         curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
508         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
509         curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
510
511         err = run_slot(slot);
512         if (err == HTTP_REAUTH && !large_request)
513                 goto retry;
514         if (err != HTTP_OK)
515                 err = -1;
516
517         curl_slist_free_all(headers);
518         free(gzip_body);
519         return err;
520 }
521
522 static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
523 {
524         const char *svc = rpc->service_name;
525         struct strbuf buf = STRBUF_INIT;
526         struct strbuf *preamble = rpc->stdin_preamble;
527         struct child_process client;
528         int err = 0;
529
530         memset(&client, 0, sizeof(client));
531         client.in = -1;
532         client.out = -1;
533         client.git_cmd = 1;
534         client.argv = rpc->argv;
535         if (start_command(&client))
536                 exit(1);
537         if (preamble)
538                 write_or_die(client.in, preamble->buf, preamble->len);
539         if (heads)
540                 write_or_die(client.in, heads->buf, heads->len);
541
542         rpc->alloc = http_post_buffer;
543         rpc->buf = xmalloc(rpc->alloc);
544         rpc->in = client.in;
545         rpc->out = client.out;
546         strbuf_init(&rpc->result, 0);
547
548         strbuf_addf(&buf, "%s%s", url, svc);
549         rpc->service_url = strbuf_detach(&buf, NULL);
550
551         strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
552         rpc->hdr_content_type = strbuf_detach(&buf, NULL);
553
554         strbuf_addf(&buf, "Accept: application/x-%s-result", svc);
555         rpc->hdr_accept = strbuf_detach(&buf, NULL);
556
557         while (!err) {
558                 int n = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
559                 if (!n)
560                         break;
561                 rpc->pos = 0;
562                 rpc->len = n;
563                 err |= post_rpc(rpc);
564         }
565
566         close(client.in);
567         client.in = -1;
568         if (!err) {
569                 strbuf_read(&rpc->result, client.out, 0);
570         } else {
571                 char buf[4096];
572                 for (;;)
573                         if (xread(client.out, buf, sizeof(buf)) <= 0)
574                                 break;
575         }
576
577         close(client.out);
578         client.out = -1;
579
580         err |= finish_command(&client);
581         free(rpc->service_url);
582         free(rpc->hdr_content_type);
583         free(rpc->hdr_accept);
584         free(rpc->buf);
585         strbuf_release(&buf);
586         return err;
587 }
588
589 static int fetch_dumb(int nr_heads, struct ref **to_fetch)
590 {
591         struct walker *walker;
592         char **targets = xmalloc(nr_heads * sizeof(char*));
593         int ret, i;
594
595         if (options.depth)
596                 die("dumb http transport does not support --depth");
597         for (i = 0; i < nr_heads; i++)
598                 targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
599
600         walker = get_http_walker(url);
601         walker->get_all = 1;
602         walker->get_tree = 1;
603         walker->get_history = 1;
604         walker->get_verbosely = options.verbosity >= 3;
605         walker->get_recover = 0;
606         ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
607         walker_free(walker);
608
609         for (i = 0; i < nr_heads; i++)
610                 free(targets[i]);
611         free(targets);
612
613         return ret ? error("Fetch failed.") : 0;
614 }
615
616 static int fetch_git(struct discovery *heads,
617         int nr_heads, struct ref **to_fetch)
618 {
619         struct rpc_state rpc;
620         struct strbuf preamble = STRBUF_INIT;
621         char *depth_arg = NULL;
622         int argc = 0, i, err;
623         const char *argv[15];
624
625         argv[argc++] = "fetch-pack";
626         argv[argc++] = "--stateless-rpc";
627         argv[argc++] = "--stdin";
628         argv[argc++] = "--lock-pack";
629         if (options.followtags)
630                 argv[argc++] = "--include-tag";
631         if (options.thin)
632                 argv[argc++] = "--thin";
633         if (options.verbosity >= 3) {
634                 argv[argc++] = "-v";
635                 argv[argc++] = "-v";
636         }
637         if (!options.progress)
638                 argv[argc++] = "--no-progress";
639         if (options.depth) {
640                 struct strbuf buf = STRBUF_INIT;
641                 strbuf_addf(&buf, "--depth=%lu", options.depth);
642                 depth_arg = strbuf_detach(&buf, NULL);
643                 argv[argc++] = depth_arg;
644         }
645         argv[argc++] = url;
646         argv[argc++] = NULL;
647
648         for (i = 0; i < nr_heads; i++) {
649                 struct ref *ref = to_fetch[i];
650                 if (!ref->name || !*ref->name)
651                         die("cannot fetch by sha1 over smart http");
652                 packet_buf_write(&preamble, "%s\n", ref->name);
653         }
654         packet_buf_flush(&preamble);
655
656         memset(&rpc, 0, sizeof(rpc));
657         rpc.service_name = "git-upload-pack",
658         rpc.argv = argv;
659         rpc.stdin_preamble = &preamble;
660         rpc.gzip_request = 1;
661
662         err = rpc_service(&rpc, heads);
663         if (rpc.result.len)
664                 write_or_die(1, rpc.result.buf, rpc.result.len);
665         strbuf_release(&rpc.result);
666         strbuf_release(&preamble);
667         free(depth_arg);
668         return err;
669 }
670
671 static int fetch(int nr_heads, struct ref **to_fetch)
672 {
673         struct discovery *d = discover_refs("git-upload-pack");
674         if (d->proto_git)
675                 return fetch_git(d, nr_heads, to_fetch);
676         else
677                 return fetch_dumb(nr_heads, to_fetch);
678 }
679
680 static void parse_fetch(struct strbuf *buf)
681 {
682         struct ref **to_fetch = NULL;
683         struct ref *list_head = NULL;
684         struct ref **list = &list_head;
685         int alloc_heads = 0, nr_heads = 0;
686
687         do {
688                 if (!prefixcmp(buf->buf, "fetch ")) {
689                         char *p = buf->buf + strlen("fetch ");
690                         char *name;
691                         struct ref *ref;
692                         unsigned char old_sha1[20];
693
694                         if (strlen(p) < 40 || get_sha1_hex(p, old_sha1))
695                                 die("protocol error: expected sha/ref, got %s'", p);
696                         if (p[40] == ' ')
697                                 name = p + 41;
698                         else if (!p[40])
699                                 name = "";
700                         else
701                                 die("protocol error: expected sha/ref, got %s'", p);
702
703                         ref = alloc_ref(name);
704                         hashcpy(ref->old_sha1, old_sha1);
705
706                         *list = ref;
707                         list = &ref->next;
708
709                         ALLOC_GROW(to_fetch, nr_heads + 1, alloc_heads);
710                         to_fetch[nr_heads++] = ref;
711                 }
712                 else
713                         die("http transport does not support %s", buf->buf);
714
715                 strbuf_reset(buf);
716                 if (strbuf_getline(buf, stdin, '\n') == EOF)
717                         return;
718                 if (!*buf->buf)
719                         break;
720         } while (1);
721
722         if (fetch(nr_heads, to_fetch))
723                 exit(128); /* error already reported */
724         free_refs(list_head);
725         free(to_fetch);
726
727         printf("\n");
728         fflush(stdout);
729         strbuf_reset(buf);
730 }
731
732 static int push_dav(int nr_spec, char **specs)
733 {
734         const char **argv = xmalloc((10 + nr_spec) * sizeof(char*));
735         int argc = 0, i;
736
737         argv[argc++] = "http-push";
738         argv[argc++] = "--helper-status";
739         if (options.dry_run)
740                 argv[argc++] = "--dry-run";
741         if (options.verbosity > 1)
742                 argv[argc++] = "--verbose";
743         argv[argc++] = url;
744         for (i = 0; i < nr_spec; i++)
745                 argv[argc++] = specs[i];
746         argv[argc++] = NULL;
747
748         if (run_command_v_opt(argv, RUN_GIT_CMD))
749                 die("git-%s failed", argv[0]);
750         free(argv);
751         return 0;
752 }
753
754 static int push_git(struct discovery *heads, int nr_spec, char **specs)
755 {
756         struct rpc_state rpc;
757         const char **argv;
758         int argc = 0, i, err;
759
760         argv = xmalloc((10 + nr_spec) * sizeof(char*));
761         argv[argc++] = "send-pack";
762         argv[argc++] = "--stateless-rpc";
763         argv[argc++] = "--helper-status";
764         if (options.thin)
765                 argv[argc++] = "--thin";
766         if (options.dry_run)
767                 argv[argc++] = "--dry-run";
768         if (options.verbosity == 0)
769                 argv[argc++] = "--quiet";
770         else if (options.verbosity > 1)
771                 argv[argc++] = "--verbose";
772         argv[argc++] = options.progress ? "--progress" : "--no-progress";
773         argv[argc++] = url;
774         for (i = 0; i < nr_spec; i++)
775                 argv[argc++] = specs[i];
776         argv[argc++] = NULL;
777
778         memset(&rpc, 0, sizeof(rpc));
779         rpc.service_name = "git-receive-pack",
780         rpc.argv = argv;
781
782         err = rpc_service(&rpc, heads);
783         if (rpc.result.len)
784                 write_or_die(1, rpc.result.buf, rpc.result.len);
785         strbuf_release(&rpc.result);
786         free(argv);
787         return err;
788 }
789
790 static int push(int nr_spec, char **specs)
791 {
792         struct discovery *heads = discover_refs("git-receive-pack");
793         int ret;
794
795         if (heads->proto_git)
796                 ret = push_git(heads, nr_spec, specs);
797         else
798                 ret = push_dav(nr_spec, specs);
799         free_discovery(heads);
800         return ret;
801 }
802
803 static void parse_push(struct strbuf *buf)
804 {
805         char **specs = NULL;
806         int alloc_spec = 0, nr_spec = 0, i, ret;
807
808         do {
809                 if (!prefixcmp(buf->buf, "push ")) {
810                         ALLOC_GROW(specs, nr_spec + 1, alloc_spec);
811                         specs[nr_spec++] = xstrdup(buf->buf + 5);
812                 }
813                 else
814                         die("http transport does not support %s", buf->buf);
815
816                 strbuf_reset(buf);
817                 if (strbuf_getline(buf, stdin, '\n') == EOF)
818                         goto free_specs;
819                 if (!*buf->buf)
820                         break;
821         } while (1);
822
823         ret = push(nr_spec, specs);
824         printf("\n");
825         fflush(stdout);
826
827         if (ret)
828                 exit(128); /* error already reported */
829
830  free_specs:
831         for (i = 0; i < nr_spec; i++)
832                 free(specs[i]);
833         free(specs);
834 }
835
836 int main(int argc, const char **argv)
837 {
838         struct strbuf buf = STRBUF_INIT;
839         int nongit;
840
841         git_extract_argv0_path(argv[0]);
842         setup_git_directory_gently(&nongit);
843         if (argc < 2) {
844                 fprintf(stderr, "Remote needed\n");
845                 return 1;
846         }
847
848         options.verbosity = 1;
849         options.progress = !!isatty(2);
850         options.thin = 1;
851
852         remote = remote_get(argv[1]);
853
854         if (argc > 2) {
855                 end_url_with_slash(&buf, argv[2]);
856         } else {
857                 end_url_with_slash(&buf, remote->url[0]);
858         }
859
860         url = strbuf_detach(&buf, NULL);
861
862         http_init(remote, url, 0);
863
864         do {
865                 if (strbuf_getline(&buf, stdin, '\n') == EOF) {
866                         if (ferror(stdin))
867                                 fprintf(stderr, "Error reading command stream\n");
868                         else
869                                 fprintf(stderr, "Unexpected end of command stream\n");
870                         return 1;
871                 }
872                 if (buf.len == 0)
873                         break;
874                 if (!prefixcmp(buf.buf, "fetch ")) {
875                         if (nongit)
876                                 die("Fetch attempted without a local repo");
877                         parse_fetch(&buf);
878
879                 } else if (!strcmp(buf.buf, "list") || !prefixcmp(buf.buf, "list ")) {
880                         int for_push = !!strstr(buf.buf + 4, "for-push");
881                         output_refs(get_refs(for_push));
882
883                 } else if (!prefixcmp(buf.buf, "push ")) {
884                         parse_push(&buf);
885
886                 } else if (!prefixcmp(buf.buf, "option ")) {
887                         char *name = buf.buf + strlen("option ");
888                         char *value = strchr(name, ' ');
889                         int result;
890
891                         if (value)
892                                 *value++ = '\0';
893                         else
894                                 value = "true";
895
896                         result = set_option(name, value);
897                         if (!result)
898                                 printf("ok\n");
899                         else if (result < 0)
900                                 printf("error invalid value\n");
901                         else
902                                 printf("unsupported\n");
903                         fflush(stdout);
904
905                 } else if (!strcmp(buf.buf, "capabilities")) {
906                         printf("fetch\n");
907                         printf("option\n");
908                         printf("push\n");
909                         printf("\n");
910                         fflush(stdout);
911                 } else {
912                         fprintf(stderr, "Unknown command '%s'\n", buf.buf);
913                         return 1;
914                 }
915                 strbuf_reset(&buf);
916         } while (1);
917
918         http_cleanup();
919
920         return 0;
921 }