f3c92c971e65e9df72fafdab52b4935866a0a794
[git/git.git] / http-push.c
1 #include "cache.h"
2 #include "commit.h"
3 #include "pack.h"
4 #include "fetch.h"
5 #include "tag.h"
6 #include "blob.h"
7 #include "http.h"
8
9 #ifdef USE_CURL_MULTI
10
11 #include <expat.h>
12
13 static const char http_push_usage[] =
14 "git-http-push [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\n";
15
16 #ifndef XML_STATUS_OK
17 enum XML_Status {
18 XML_STATUS_OK = 1,
19 XML_STATUS_ERROR = 0
20 };
21 #define XML_STATUS_OK 1
22 #define XML_STATUS_ERROR 0
23 #endif
24
25 #define RANGE_HEADER_SIZE 30
26
27 /* DAV methods */
28 #define DAV_LOCK "LOCK"
29 #define DAV_MKCOL "MKCOL"
30 #define DAV_MOVE "MOVE"
31 #define DAV_PROPFIND "PROPFIND"
32 #define DAV_PUT "PUT"
33 #define DAV_UNLOCK "UNLOCK"
34
35 /* DAV lock flags */
36 #define DAV_PROP_LOCKWR (1u << 0)
37 #define DAV_PROP_LOCKEX (1u << 1)
38 #define DAV_LOCK_OK (1u << 2)
39
40 /* DAV XML properties */
41 #define DAV_CTX_LOCKENTRY ".multistatus.response.propstat.prop.supportedlock.lockentry"
42 #define DAV_CTX_LOCKTYPE_WRITE ".multistatus.response.propstat.prop.supportedlock.lockentry.locktype.write"
43 #define DAV_CTX_LOCKTYPE_EXCLUSIVE ".multistatus.response.propstat.prop.supportedlock.lockentry.lockscope.exclusive"
44 #define DAV_ACTIVELOCK_OWNER ".prop.lockdiscovery.activelock.owner.href"
45 #define DAV_ACTIVELOCK_TIMEOUT ".prop.lockdiscovery.activelock.timeout"
46 #define DAV_ACTIVELOCK_TOKEN ".prop.lockdiscovery.activelock.locktoken.href"
47
48 /* DAV request body templates */
49 #define PROPFIND_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>"
50 #define LOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:lockinfo xmlns:D=\"DAV:\">\n<D:lockscope><D:exclusive/></D:lockscope>\n<D:locktype><D:write/></D:locktype>\n<D:owner>\n<D:href>mailto:%s</D:href>\n</D:owner>\n</D:lockinfo>"
51
52 #define LOCK_TIME 600
53 #define LOCK_REFRESH 30
54
55 static int pushing = 0;
56 static int aborted = 0;
57 static char remote_dir_exists[256];
58
59 static struct curl_slist *no_pragma_header;
60 static struct curl_slist *default_headers;
61
62 static int push_verbosely = 0;
63 static int push_all = 0;
64 static int force_all = 0;
65
66 struct repo
67 {
68 char *url;
69 struct packed_git *packs;
70 };
71
72 static struct repo *remote = NULL;
73
74 enum transfer_state {
75 NEED_CHECK,
76 RUN_HEAD,
77 NEED_PUSH,
78 RUN_MKCOL,
79 RUN_PUT,
80 RUN_MOVE,
81 ABORTED,
82 COMPLETE,
83 };
84
85 struct transfer_request
86 {
87 unsigned char sha1[20];
88 char *url;
89 char *dest;
90 struct active_lock *lock;
91 struct curl_slist *headers;
92 struct buffer buffer;
93 char filename[PATH_MAX];
94 char tmpfile[PATH_MAX];
95 enum transfer_state state;
96 CURLcode curl_result;
97 char errorstr[CURL_ERROR_SIZE];
98 long http_code;
99 unsigned char real_sha1[20];
100 SHA_CTX c;
101 z_stream stream;
102 int zret;
103 int rename;
104 struct active_request_slot *slot;
105 struct transfer_request *next;
106 };
107
108 static struct transfer_request *request_queue_head = NULL;
109
110 struct xml_ctx
111 {
112 char *name;
113 int len;
114 char *cdata;
115 void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
116 void *userData;
117 };
118
119 struct active_lock
120 {
121 char *url;
122 char *owner;
123 char *token;
124 time_t start_time;
125 long timeout;
126 int refreshing;
127 };
128
129 static void finish_request(struct transfer_request *request);
130
131 static void process_response(void *callback_data)
132 {
133 struct transfer_request *request =
134 (struct transfer_request *)callback_data;
135
136 finish_request(request);
137 }
138
139 static void start_check(struct transfer_request *request)
140 {
141 char *hex = sha1_to_hex(request->sha1);
142 struct active_request_slot *slot;
143 char *posn;
144
145 request->url = xmalloc(strlen(remote->url) + 55);
146 strcpy(request->url, remote->url);
147 posn = request->url + strlen(remote->url);
148 strcpy(posn, "objects/");
149 posn += 8;
150 memcpy(posn, hex, 2);
151 posn += 2;
152 *(posn++) = '/';
153 strcpy(posn, hex + 2);
154
155 slot = get_active_slot();
156 slot->callback_func = process_response;
157 slot->callback_data = request;
158 curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
159 curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
160 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
161
162 if (start_active_slot(slot)) {
163 request->slot = slot;
164 request->state = RUN_HEAD;
165 } else {
166 request->state = ABORTED;
167 free(request->url);
168 request->url = NULL;
169 }
170 }
171
172 static void start_mkcol(struct transfer_request *request)
173 {
174 char *hex = sha1_to_hex(request->sha1);
175 struct active_request_slot *slot;
176 char *posn;
177
178 request->url = xmalloc(strlen(remote->url) + 13);
179 strcpy(request->url, remote->url);
180 posn = request->url + strlen(remote->url);
181 strcpy(posn, "objects/");
182 posn += 8;
183 memcpy(posn, hex, 2);
184 posn += 2;
185 strcpy(posn, "/");
186
187 slot = get_active_slot();
188 slot->callback_func = process_response;
189 slot->callback_data = request;
190 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
191 curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
192 curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
193 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
194 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
195
196 if (start_active_slot(slot)) {
197 request->slot = slot;
198 request->state = RUN_MKCOL;
199 } else {
200 request->state = ABORTED;
201 free(request->url);
202 request->url = NULL;
203 }
204 }
205
206 static void start_put(struct transfer_request *request)
207 {
208 char *hex = sha1_to_hex(request->sha1);
209 struct active_request_slot *slot;
210 char *posn;
211 char type[20];
212 char hdr[50];
213 void *unpacked;
214 unsigned long len;
215 int hdrlen;
216 ssize_t size;
217 z_stream stream;
218
219 unpacked = read_sha1_file(request->sha1, type, &len);
220 hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
221
222 /* Set it up */
223 memset(&stream, 0, sizeof(stream));
224 deflateInit(&stream, Z_BEST_COMPRESSION);
225 size = deflateBound(&stream, len + hdrlen);
226 request->buffer.buffer = xmalloc(size);
227
228 /* Compress it */
229 stream.next_out = request->buffer.buffer;
230 stream.avail_out = size;
231
232 /* First header.. */
233 stream.next_in = (void *)hdr;
234 stream.avail_in = hdrlen;
235 while (deflate(&stream, 0) == Z_OK)
236 /* nothing */;
237
238 /* Then the data itself.. */
239 stream.next_in = unpacked;
240 stream.avail_in = len;
241 while (deflate(&stream, Z_FINISH) == Z_OK)
242 /* nothing */;
243 deflateEnd(&stream);
244 free(unpacked);
245
246 request->buffer.size = stream.total_out;
247 request->buffer.posn = 0;
248
249 request->url = xmalloc(strlen(remote->url) +
250 strlen(request->lock->token) + 51);
251 strcpy(request->url, remote->url);
252 posn = request->url + strlen(remote->url);
253 strcpy(posn, "objects/");
254 posn += 8;
255 memcpy(posn, hex, 2);
256 posn += 2;
257 *(posn++) = '/';
258 strcpy(posn, hex + 2);
259 request->dest = xmalloc(strlen(request->url) + 14);
260 sprintf(request->dest, "Destination: %s", request->url);
261 posn += 38;
262 *(posn++) = '.';
263 strcpy(posn, request->lock->token);
264
265 slot = get_active_slot();
266 slot->callback_func = process_response;
267 slot->callback_data = request;
268 curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer);
269 curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.size);
270 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
271 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
272 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
273 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
274 curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
275 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
276 curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
277
278 if (start_active_slot(slot)) {
279 request->slot = slot;
280 request->state = RUN_PUT;
281 } else {
282 request->state = ABORTED;
283 free(request->url);
284 request->url = NULL;
285 }
286 }
287
288 static void start_move(struct transfer_request *request)
289 {
290 struct active_request_slot *slot;
291 struct curl_slist *dav_headers = NULL;
292
293 slot = get_active_slot();
294 slot->callback_func = process_response;
295 slot->callback_data = request;
296 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
297 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE);
298 dav_headers = curl_slist_append(dav_headers, request->dest);
299 dav_headers = curl_slist_append(dav_headers, "Overwrite: T");
300 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
301 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
302 curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
303
304 if (start_active_slot(slot)) {
305 request->slot = slot;
306 request->state = RUN_MOVE;
307 } else {
308 request->state = ABORTED;
309 free(request->url);
310 request->url = NULL;
311 }
312 }
313
314 static int refresh_lock(struct active_lock *lock)
315 {
316 struct active_request_slot *slot;
317 char *if_header;
318 char timeout_header[25];
319 struct curl_slist *dav_headers = NULL;
320 int rc = 0;
321
322 lock->refreshing = 1;
323
324 if_header = xmalloc(strlen(lock->token) + 25);
325 sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
326 sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout);
327 dav_headers = curl_slist_append(dav_headers, if_header);
328 dav_headers = curl_slist_append(dav_headers, timeout_header);
329
330 slot = get_active_slot();
331 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
332 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
333 curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
334 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
335 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
336
337 if (start_active_slot(slot)) {
338 run_active_slot(slot);
339 if (slot->curl_result != CURLE_OK) {
340 fprintf(stderr, "Got HTTP error %ld\n", slot->http_code);
341 } else {
342 lock->start_time = time(NULL);
343 rc = 1;
344 }
345 }
346
347 lock->refreshing = 0;
348 curl_slist_free_all(dav_headers);
349 free(if_header);
350
351 return rc;
352 }
353
354 static void finish_request(struct transfer_request *request)
355 {
356 time_t current_time = time(NULL);
357 int time_remaining;
358
359 request->curl_result = request->slot->curl_result;
360 request->http_code = request->slot->http_code;
361 request->slot = NULL;
362
363 /* Refresh the lock if it is close to timing out */
364 time_remaining = request->lock->start_time + request->lock->timeout
365 - current_time;
366 if (time_remaining < LOCK_REFRESH && !request->lock->refreshing) {
367 if (!refresh_lock(request->lock)) {
368 fprintf(stderr, "Unable to refresh remote lock\n");
369 aborted = 1;
370 }
371 }
372
373 if (request->headers != NULL)
374 curl_slist_free_all(request->headers);
375
376 /* URL is reused for MOVE after PUT */
377 if (request->state != RUN_PUT) {
378 free(request->url);
379 request->url = NULL;
380 }
381
382 if (request->state == RUN_HEAD) {
383 if (request->http_code == 404) {
384 request->state = NEED_PUSH;
385 } else if (request->curl_result == CURLE_OK) {
386 remote_dir_exists[request->sha1[0]] = 1;
387 request->state = COMPLETE;
388 } else {
389 fprintf(stderr, "HEAD %s failed, aborting (%d/%ld)\n",
390 sha1_to_hex(request->sha1),
391 request->curl_result, request->http_code);
392 request->state = ABORTED;
393 aborted = 1;
394 }
395 } else if (request->state == RUN_MKCOL) {
396 if (request->curl_result == CURLE_OK ||
397 request->http_code == 405) {
398 remote_dir_exists[request->sha1[0]] = 1;
399 start_put(request);
400 } else {
401 fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n",
402 sha1_to_hex(request->sha1),
403 request->curl_result, request->http_code);
404 request->state = ABORTED;
405 aborted = 1;
406 }
407 } else if (request->state == RUN_PUT) {
408 if (request->curl_result == CURLE_OK) {
409 start_move(request);
410 } else {
411 fprintf(stderr, "PUT %s failed, aborting (%d/%ld)\n",
412 sha1_to_hex(request->sha1),
413 request->curl_result, request->http_code);
414 request->state = ABORTED;
415 aborted = 1;
416 }
417 } else if (request->state == RUN_MOVE) {
418 if (request->curl_result == CURLE_OK) {
419 if (push_verbosely)
420 fprintf(stderr,
421 "sent %s\n",
422 sha1_to_hex(request->sha1));
423 request->state = COMPLETE;
424 } else {
425 fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n",
426 sha1_to_hex(request->sha1),
427 request->curl_result, request->http_code);
428 request->state = ABORTED;
429 aborted = 1;
430 }
431 }
432 }
433
434 static void release_request(struct transfer_request *request)
435 {
436 struct transfer_request *entry = request_queue_head;
437
438 if (request == request_queue_head) {
439 request_queue_head = request->next;
440 } else {
441 while (entry->next != NULL && entry->next != request)
442 entry = entry->next;
443 if (entry->next == request)
444 entry->next = entry->next->next;
445 }
446
447 if (request->url != NULL)
448 free(request->url);
449 free(request);
450 }
451
452 void fill_active_slots(void)
453 {
454 struct transfer_request *request = request_queue_head;
455 struct active_request_slot *slot = active_queue_head;
456 int num_transfers;
457
458 if (aborted)
459 return;
460
461 while (active_requests < max_requests && request != NULL) {
462 if (!pushing && request->state == NEED_CHECK) {
463 start_check(request);
464 curl_multi_perform(curlm, &num_transfers);
465 } else if (pushing && request->state == NEED_PUSH) {
466 if (remote_dir_exists[request->sha1[0]])
467 start_put(request);
468 else
469 start_mkcol(request);
470 curl_multi_perform(curlm, &num_transfers);
471 }
472 request = request->next;
473 }
474
475 while (slot != NULL) {
476 if (!slot->in_use && slot->curl != NULL) {
477 curl_easy_cleanup(slot->curl);
478 slot->curl = NULL;
479 }
480 slot = slot->next;
481 }
482 }
483
484 static void add_request(unsigned char *sha1, struct active_lock *lock)
485 {
486 struct transfer_request *request = request_queue_head;
487 struct packed_git *target;
488
489 while (request != NULL && memcmp(request->sha1, sha1, 20))
490 request = request->next;
491 if (request != NULL)
492 return;
493
494 target = find_sha1_pack(sha1, remote->packs);
495 if (target)
496 return;
497
498 request = xmalloc(sizeof(*request));
499 memcpy(request->sha1, sha1, 20);
500 request->url = NULL;
501 request->lock = lock;
502 request->headers = NULL;
503 request->state = NEED_CHECK;
504 request->next = request_queue_head;
505 request_queue_head = request;
506
507 fill_active_slots();
508 step_active_slots();
509 }
510
511 static int fetch_index(unsigned char *sha1)
512 {
513 char *hex = sha1_to_hex(sha1);
514 char *filename;
515 char *url;
516 char tmpfile[PATH_MAX];
517 long prev_posn = 0;
518 char range[RANGE_HEADER_SIZE];
519 struct curl_slist *range_header = NULL;
520
521 FILE *indexfile;
522 struct active_request_slot *slot;
523
524 /* Don't use the index if the pack isn't there */
525 url = xmalloc(strlen(remote->url) + 65);
526 sprintf(url, "%s/objects/pack/pack-%s.pack", remote->url, hex);
527 slot = get_active_slot();
528 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
529 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
530 if (start_active_slot(slot)) {
531 run_active_slot(slot);
532 if (slot->curl_result != CURLE_OK) {
533 free(url);
534 return error("Unable to verify pack %s is available",
535 hex);
536 }
537 } else {
538 return error("Unable to start request");
539 }
540
541 if (has_pack_index(sha1))
542 return 0;
543
544 if (push_verbosely)
545 fprintf(stderr, "Getting index for pack %s\n", hex);
546
547 sprintf(url, "%s/objects/pack/pack-%s.idx", remote->url, hex);
548
549 filename = sha1_pack_index_name(sha1);
550 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
551 indexfile = fopen(tmpfile, "a");
552 if (!indexfile)
553 return error("Unable to open local file %s for pack index",
554 filename);
555
556 slot = get_active_slot();
557 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
558 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
559 curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
560 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
561 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
562 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
563 slot->local = indexfile;
564
565 /* If there is data present from a previous transfer attempt,
566 resume where it left off */
567 prev_posn = ftell(indexfile);
568 if (prev_posn>0) {
569 if (push_verbosely)
570 fprintf(stderr,
571 "Resuming fetch of index for pack %s at byte %ld\n",
572 hex, prev_posn);
573 sprintf(range, "Range: bytes=%ld-", prev_posn);
574 range_header = curl_slist_append(range_header, range);
575 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
576 }
577
578 if (start_active_slot(slot)) {
579 run_active_slot(slot);
580 if (slot->curl_result != CURLE_OK) {
581 free(url);
582 fclose(indexfile);
583 return error("Unable to get pack index %s\n%s", url,
584 curl_errorstr);
585 }
586 } else {
587 free(url);
588 fclose(indexfile);
589 return error("Unable to start request");
590 }
591
592 free(url);
593 fclose(indexfile);
594
595 return move_temp_to_file(tmpfile, filename);
596 }
597
598 static int setup_index(unsigned char *sha1)
599 {
600 struct packed_git *new_pack;
601
602 if (fetch_index(sha1))
603 return -1;
604
605 new_pack = parse_pack_index(sha1);
606 new_pack->next = remote->packs;
607 remote->packs = new_pack;
608 return 0;
609 }
610
611 static int fetch_indices(void)
612 {
613 unsigned char sha1[20];
614 char *url;
615 struct buffer buffer;
616 char *data;
617 int i = 0;
618
619 struct active_request_slot *slot;
620
621 data = xmalloc(4096);
622 memset(data, 0, 4096);
623 buffer.size = 4096;
624 buffer.posn = 0;
625 buffer.buffer = data;
626
627 if (push_verbosely)
628 fprintf(stderr, "Getting pack list\n");
629
630 url = xmalloc(strlen(remote->url) + 21);
631 sprintf(url, "%s/objects/info/packs", remote->url);
632
633 slot = get_active_slot();
634 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
635 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
636 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
637 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
638 if (start_active_slot(slot)) {
639 run_active_slot(slot);
640 if (slot->curl_result != CURLE_OK) {
641 free(buffer.buffer);
642 free(url);
643 if (slot->http_code == 404)
644 return 0;
645 else
646 return error("%s", curl_errorstr);
647 }
648 } else {
649 free(buffer.buffer);
650 free(url);
651 return error("Unable to start request");
652 }
653 free(url);
654
655 data = buffer.buffer;
656 while (i < buffer.posn) {
657 switch (data[i]) {
658 case 'P':
659 i++;
660 if (i + 52 < buffer.posn &&
661 !strncmp(data + i, " pack-", 6) &&
662 !strncmp(data + i + 46, ".pack\n", 6)) {
663 get_sha1_hex(data + i + 6, sha1);
664 setup_index(sha1);
665 i += 51;
666 break;
667 }
668 default:
669 while (data[i] != '\n')
670 i++;
671 }
672 i++;
673 }
674
675 free(buffer.buffer);
676 return 0;
677 }
678
679 static inline int needs_quote(int ch)
680 {
681 switch (ch) {
682 case '/': case '-': case '.':
683 case 'A'...'Z': case 'a'...'z': case '0'...'9':
684 return 0;
685 default:
686 return 1;
687 }
688 }
689
690 static inline int hex(int v)
691 {
692 if (v < 10) return '0' + v;
693 else return 'A' + v - 10;
694 }
695
696 static char *quote_ref_url(const char *base, const char *ref)
697 {
698 const char *cp;
699 char *dp, *qref;
700 int len, baselen, ch;
701
702 baselen = strlen(base);
703 len = baselen + 12; /* "refs/heads/" + NUL */
704 for (cp = ref; (ch = *cp) != 0; cp++, len++)
705 if (needs_quote(ch))
706 len += 2; /* extra two hex plus replacement % */
707 qref = xmalloc(len);
708 memcpy(qref, base, baselen);
709 memcpy(qref + baselen, "refs/heads/", 11);
710 for (cp = ref, dp = qref + baselen + 11; (ch = *cp) != 0; cp++) {
711 if (needs_quote(ch)) {
712 *dp++ = '%';
713 *dp++ = hex((ch >> 4) & 0xF);
714 *dp++ = hex(ch & 0xF);
715 }
716 else
717 *dp++ = ch;
718 }
719 *dp = 0;
720
721 return qref;
722 }
723
724 int fetch_ref(char *ref, unsigned char *sha1)
725 {
726 char *url;
727 char hex[42];
728 struct buffer buffer;
729 char *base = remote->url;
730 struct active_request_slot *slot;
731 buffer.size = 41;
732 buffer.posn = 0;
733 buffer.buffer = hex;
734 hex[41] = '\0';
735
736 url = quote_ref_url(base, ref);
737 slot = get_active_slot();
738 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
739 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
740 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
741 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
742 if (start_active_slot(slot)) {
743 run_active_slot(slot);
744 if (slot->curl_result != CURLE_OK)
745 return error("Couldn't get %s for %s\n%s",
746 url, ref, curl_errorstr);
747 } else {
748 return error("Unable to start request");
749 }
750
751 hex[40] = '\0';
752 get_sha1_hex(hex, sha1);
753 return 0;
754 }
755
756 static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)
757 {
758 int *lock_flags = (int *)ctx->userData;
759
760 if (tag_closed) {
761 if (!strcmp(ctx->name, DAV_CTX_LOCKENTRY)) {
762 if ((*lock_flags & DAV_PROP_LOCKEX) &&
763 (*lock_flags & DAV_PROP_LOCKWR)) {
764 *lock_flags |= DAV_LOCK_OK;
765 }
766 *lock_flags &= DAV_LOCK_OK;
767 } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_WRITE)) {
768 *lock_flags |= DAV_PROP_LOCKWR;
769 } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_EXCLUSIVE)) {
770 *lock_flags |= DAV_PROP_LOCKEX;
771 }
772 }
773 }
774
775 static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
776 {
777 struct active_lock *lock = (struct active_lock *)ctx->userData;
778
779 if (tag_closed && ctx->cdata) {
780 if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
781 lock->owner = xmalloc(strlen(ctx->cdata) + 1);
782 strcpy(lock->owner, ctx->cdata);
783 } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
784 if (!strncmp(ctx->cdata, "Second-", 7))
785 lock->timeout =
786 strtol(ctx->cdata + 7, NULL, 10);
787 } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
788 if (!strncmp(ctx->cdata, "opaquelocktoken:", 16)) {
789 lock->token = xmalloc(strlen(ctx->cdata - 15));
790 strcpy(lock->token, ctx->cdata + 16);
791 }
792 }
793 }
794 }
795
796 static void
797 xml_start_tag(void *userData, const char *name, const char **atts)
798 {
799 struct xml_ctx *ctx = (struct xml_ctx *)userData;
800 const char *c = index(name, ':');
801 int new_len;
802
803 if (c == NULL)
804 c = name;
805 else
806 c++;
807
808 new_len = strlen(ctx->name) + strlen(c) + 2;
809
810 if (new_len > ctx->len) {
811 ctx->name = xrealloc(ctx->name, new_len);
812 ctx->len = new_len;
813 }
814 strcat(ctx->name, ".");
815 strcat(ctx->name, c);
816
817 if (ctx->cdata) {
818 free(ctx->cdata);
819 ctx->cdata = NULL;
820 }
821
822 ctx->userFunc(ctx, 0);
823 }
824
825 static void
826 xml_end_tag(void *userData, const char *name)
827 {
828 struct xml_ctx *ctx = (struct xml_ctx *)userData;
829 const char *c = index(name, ':');
830 char *ep;
831
832 ctx->userFunc(ctx, 1);
833
834 if (c == NULL)
835 c = name;
836 else
837 c++;
838
839 ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
840 *ep = 0;
841 }
842
843 static void
844 xml_cdata(void *userData, const XML_Char *s, int len)
845 {
846 struct xml_ctx *ctx = (struct xml_ctx *)userData;
847 if (ctx->cdata)
848 free(ctx->cdata);
849 ctx->cdata = xcalloc(len+1, 1);
850 strncpy(ctx->cdata, s, len);
851 }
852
853 static struct active_lock *lock_remote(char *file, long timeout)
854 {
855 struct active_request_slot *slot;
856 struct buffer out_buffer;
857 struct buffer in_buffer;
858 char *out_data;
859 char *in_data;
860 char *url;
861 char *ep;
862 char timeout_header[25];
863 struct active_lock *new_lock = NULL;
864 XML_Parser parser = XML_ParserCreate(NULL);
865 enum XML_Status result;
866 struct curl_slist *dav_headers = NULL;
867 struct xml_ctx ctx;
868
869 url = xmalloc(strlen(remote->url) + strlen(file) + 1);
870 sprintf(url, "%s%s", remote->url, file);
871
872 /* Make sure leading directories exist for the remote ref */
873 ep = strchr(url + strlen(remote->url) + 11, '/');
874 while (ep) {
875 *ep = 0;
876 slot = get_active_slot();
877 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
878 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
879 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
880 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
881 if (start_active_slot(slot)) {
882 run_active_slot(slot);
883 if (slot->curl_result != CURLE_OK &&
884 slot->http_code != 405) {
885 fprintf(stderr,
886 "Unable to create branch path %s\n",
887 url);
888 free(url);
889 return NULL;
890 }
891 } else {
892 fprintf(stderr, "Unable to start request\n");
893 free(url);
894 return NULL;
895 }
896 *ep = '/';
897 ep = strchr(ep + 1, '/');
898 }
899
900 out_buffer.size = strlen(LOCK_REQUEST) + strlen(git_default_email) - 2;
901 out_data = xmalloc(out_buffer.size + 1);
902 snprintf(out_data, out_buffer.size + 1, LOCK_REQUEST, git_default_email);
903 out_buffer.posn = 0;
904 out_buffer.buffer = out_data;
905
906 in_buffer.size = 4096;
907 in_data = xmalloc(in_buffer.size);
908 in_buffer.posn = 0;
909 in_buffer.buffer = in_data;
910
911 sprintf(timeout_header, "Timeout: Second-%ld", timeout);
912 dav_headers = curl_slist_append(dav_headers, timeout_header);
913 dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
914
915 slot = get_active_slot();
916 curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
917 curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
918 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
919 curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
920 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
921 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
922 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
923 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
924 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
925
926 new_lock = xcalloc(1, sizeof(*new_lock));
927 new_lock->owner = NULL;
928 new_lock->token = NULL;
929 new_lock->timeout = -1;
930 new_lock->refreshing = 0;
931
932 if (start_active_slot(slot)) {
933 run_active_slot(slot);
934 if (slot->curl_result == CURLE_OK) {
935 ctx.name = xcalloc(10, 1);
936 ctx.len = 0;
937 ctx.cdata = NULL;
938 ctx.userFunc = handle_new_lock_ctx;
939 ctx.userData = new_lock;
940 XML_SetUserData(parser, &ctx);
941 XML_SetElementHandler(parser, xml_start_tag,
942 xml_end_tag);
943 XML_SetCharacterDataHandler(parser, xml_cdata);
944 result = XML_Parse(parser, in_buffer.buffer,
945 in_buffer.posn, 1);
946 free(ctx.name);
947 if (result != XML_STATUS_OK) {
948 fprintf(stderr, "XML error: %s\n",
949 XML_ErrorString(
950 XML_GetErrorCode(parser)));
951 new_lock->timeout = -1;
952 }
953 }
954 } else {
955 fprintf(stderr, "Unable to start request\n");
956 }
957
958 curl_slist_free_all(dav_headers);
959 free(out_data);
960 free(in_data);
961
962 if (new_lock->token == NULL || new_lock->timeout <= 0) {
963 if (new_lock->token != NULL)
964 free(new_lock->token);
965 if (new_lock->owner != NULL)
966 free(new_lock->owner);
967 free(url);
968 free(new_lock);
969 new_lock = NULL;
970 } else {
971 new_lock->url = url;
972 new_lock->start_time = time(NULL);
973 }
974
975 return new_lock;
976 }
977
978 static int unlock_remote(struct active_lock *lock)
979 {
980 struct active_request_slot *slot;
981 char *lock_token_header;
982 struct curl_slist *dav_headers = NULL;
983 int rc = 0;
984
985 lock_token_header = xmalloc(strlen(lock->token) + 31);
986 sprintf(lock_token_header, "Lock-Token: <opaquelocktoken:%s>",
987 lock->token);
988 dav_headers = curl_slist_append(dav_headers, lock_token_header);
989
990 slot = get_active_slot();
991 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
992 curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
993 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_UNLOCK);
994 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
995
996 if (start_active_slot(slot)) {
997 run_active_slot(slot);
998 if (slot->curl_result == CURLE_OK)
999 rc = 1;
1000 else
1001 fprintf(stderr, "Got HTTP error %ld\n",
1002 slot->http_code);
1003 } else {
1004 fprintf(stderr, "Unable to start request\n");
1005 }
1006
1007 curl_slist_free_all(dav_headers);
1008 free(lock_token_header);
1009
1010 if (lock->owner != NULL)
1011 free(lock->owner);
1012 free(lock->url);
1013 /* Freeing the token causes a segfault...
1014 free(lock->token);
1015 */
1016 free(lock);
1017
1018 return rc;
1019 }
1020
1021 static int locking_available(void)
1022 {
1023 struct active_request_slot *slot;
1024 struct buffer in_buffer;
1025 struct buffer out_buffer;
1026 char *in_data;
1027 char *out_data;
1028 XML_Parser parser = XML_ParserCreate(NULL);
1029 enum XML_Status result;
1030 struct curl_slist *dav_headers = NULL;
1031 struct xml_ctx ctx;
1032 int lock_flags = 0;
1033
1034 out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2;
1035 out_data = xmalloc(out_buffer.size + 1);
1036 snprintf(out_data, out_buffer.size + 1, PROPFIND_REQUEST, remote->url);
1037 out_buffer.posn = 0;
1038 out_buffer.buffer = out_data;
1039
1040 in_buffer.size = 4096;
1041 in_data = xmalloc(in_buffer.size);
1042 in_buffer.posn = 0;
1043 in_buffer.buffer = in_data;
1044
1045 dav_headers = curl_slist_append(dav_headers, "Depth: 0");
1046 dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
1047
1048 slot = get_active_slot();
1049 curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
1050 curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
1051 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
1052 curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
1053 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
1054 curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url);
1055 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
1056 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
1057 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
1058
1059 if (start_active_slot(slot)) {
1060 run_active_slot(slot);
1061 if (slot->curl_result == CURLE_OK) {
1062 ctx.name = xcalloc(10, 1);
1063 ctx.len = 0;
1064 ctx.cdata = NULL;
1065 ctx.userFunc = handle_lockprop_ctx;
1066 ctx.userData = &lock_flags;
1067 XML_SetUserData(parser, &ctx);
1068 XML_SetElementHandler(parser, xml_start_tag,
1069 xml_end_tag);
1070 result = XML_Parse(parser, in_buffer.buffer,
1071 in_buffer.posn, 1);
1072 free(ctx.name);
1073
1074 if (result != XML_STATUS_OK) {
1075 fprintf(stderr, "XML error: %s\n",
1076 XML_ErrorString(
1077 XML_GetErrorCode(parser)));
1078 lock_flags = 0;
1079 }
1080 }
1081 } else {
1082 fprintf(stderr, "Unable to start request\n");
1083 }
1084
1085 free(out_data);
1086 free(in_buffer.buffer);
1087 curl_slist_free_all(dav_headers);
1088
1089 return lock_flags;
1090 }
1091
1092 static int is_ancestor(unsigned char *sha1, struct commit *commit)
1093 {
1094 struct commit_list *parents;
1095
1096 if (parse_commit(commit))
1097 return 0;
1098 parents = commit->parents;
1099 for (; parents; parents = parents->next) {
1100 if (!memcmp(sha1, parents->item->object.sha1, 20)) {
1101 return 1;
1102 } else if (parents->item->object.type == commit_type) {
1103 if (is_ancestor(
1104 sha1,
1105 (struct commit *)&parents->item->object
1106 ))
1107 return 1;
1108 }
1109 }
1110 return 0;
1111 }
1112
1113 static void get_delta(unsigned char *sha1, struct object *obj,
1114 struct active_lock *lock)
1115 {
1116 struct commit *commit;
1117 struct commit_list *parents;
1118 struct tree *tree;
1119 struct tree_entry_list *entry;
1120
1121 if (sha1 && !memcmp(sha1, obj->sha1, 20))
1122 return;
1123
1124 if (aborted)
1125 return;
1126
1127 if (obj->type == commit_type) {
1128 if (push_verbosely)
1129 fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
1130 add_request(obj->sha1, lock);
1131 commit = (struct commit *)obj;
1132 if (parse_commit(commit)) {
1133 fprintf(stderr, "Error parsing commit %s\n",
1134 sha1_to_hex(obj->sha1));
1135 aborted = 1;
1136 return;
1137 }
1138 parents = commit->parents;
1139 for (; parents; parents = parents->next)
1140 if (sha1 == NULL ||
1141 memcmp(sha1, parents->item->object.sha1, 20))
1142 get_delta(sha1, &parents->item->object,
1143 lock);
1144 get_delta(sha1, &commit->tree->object, lock);
1145 } else if (obj->type == tree_type) {
1146 if (push_verbosely)
1147 fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
1148 add_request(obj->sha1, lock);
1149 tree = (struct tree *)obj;
1150 if (parse_tree(tree)) {
1151 fprintf(stderr, "Error parsing tree %s\n",
1152 sha1_to_hex(obj->sha1));
1153 aborted = 1;
1154 return;
1155 }
1156 entry = tree->entries;
1157 tree->entries = NULL;
1158 while (entry) {
1159 struct tree_entry_list *next = entry->next;
1160 get_delta(sha1, entry->item.any, lock);
1161 free(entry->name);
1162 free(entry);
1163 entry = next;
1164 }
1165 } else if (obj->type == blob_type || obj->type == tag_type) {
1166 add_request(obj->sha1, lock);
1167 }
1168 }
1169
1170 static int update_remote(unsigned char *sha1, struct active_lock *lock)
1171 {
1172 struct active_request_slot *slot;
1173 char *out_data;
1174 char *if_header;
1175 struct buffer out_buffer;
1176 struct curl_slist *dav_headers = NULL;
1177 int i;
1178
1179 if_header = xmalloc(strlen(lock->token) + 25);
1180 sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
1181 dav_headers = curl_slist_append(dav_headers, if_header);
1182
1183 out_buffer.size = 41;
1184 out_data = xmalloc(out_buffer.size + 1);
1185 i = snprintf(out_data, out_buffer.size + 1, "%s\n", sha1_to_hex(sha1));
1186 if (i != out_buffer.size) {
1187 fprintf(stderr, "Unable to initialize PUT request body\n");
1188 return 0;
1189 }
1190 out_buffer.posn = 0;
1191 out_buffer.buffer = out_data;
1192
1193 slot = get_active_slot();
1194 curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
1195 curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
1196 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
1197 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
1198 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
1199 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
1200 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
1201 curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
1202 curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
1203
1204 if (start_active_slot(slot)) {
1205 run_active_slot(slot);
1206 free(out_data);
1207 free(if_header);
1208 if (slot->curl_result != CURLE_OK) {
1209 fprintf(stderr,
1210 "PUT error: curl result=%d, HTTP code=%ld\n",
1211 slot->curl_result, slot->http_code);
1212 /* We should attempt recovery? */
1213 return 0;
1214 }
1215 } else {
1216 free(out_data);
1217 free(if_header);
1218 fprintf(stderr, "Unable to start PUT request\n");
1219 return 0;
1220 }
1221
1222 return 1;
1223 }
1224
1225 int main(int argc, char **argv)
1226 {
1227 struct transfer_request *request;
1228 struct transfer_request *next_request;
1229 int nr_refspec = 0;
1230 char **refspec = NULL;
1231 int do_remote_update;
1232 int new_branch;
1233 int force_this;
1234 char *local_ref;
1235 unsigned char local_sha1[20];
1236 struct object *local_object = NULL;
1237 char *remote_ref = NULL;
1238 unsigned char remote_sha1[20];
1239 struct active_lock *remote_lock;
1240 char *remote_path = NULL;
1241 int rc = 0;
1242 int i;
1243
1244 setup_ident();
1245
1246 remote = xmalloc(sizeof(*remote));
1247 remote->url = NULL;
1248 remote->packs = NULL;
1249
1250 argv++;
1251 for (i = 1; i < argc; i++, argv++) {
1252 char *arg = *argv;
1253
1254 if (*arg == '-') {
1255 if (!strcmp(arg, "--complete")) {
1256 push_all = 1;
1257 continue;
1258 }
1259 if (!strcmp(arg, "--force")) {
1260 force_all = 1;
1261 continue;
1262 }
1263 if (!strcmp(arg, "--verbose")) {
1264 push_verbosely = 1;
1265 continue;
1266 }
1267 usage(http_push_usage);
1268 }
1269 if (!remote->url) {
1270 remote->url = arg;
1271 continue;
1272 }
1273 refspec = argv;
1274 nr_refspec = argc - i;
1275 break;
1276 }
1277
1278 memset(remote_dir_exists, 0, 256);
1279
1280 http_init();
1281
1282 no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
1283 default_headers = curl_slist_append(default_headers, "Range:");
1284 default_headers = curl_slist_append(default_headers, "Destination:");
1285 default_headers = curl_slist_append(default_headers, "If:");
1286 default_headers = curl_slist_append(default_headers,
1287 "Pragma: no-cache");
1288
1289 /* Verify DAV compliance/lock support */
1290 if (!locking_available()) {
1291 fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url);
1292 rc = 1;
1293 goto cleanup;
1294 }
1295
1296 /* Process each refspec */
1297 for (i = 0; i < nr_refspec; i++) {
1298 char *ep;
1299 force_this = 0;
1300 do_remote_update = 0;
1301 new_branch = 0;
1302 local_ref = refspec[i];
1303 if (*local_ref == '+') {
1304 force_this = 1;
1305 local_ref++;
1306 }
1307 ep = strchr(local_ref, ':');
1308 if (ep) {
1309 remote_ref = ep + 1;
1310 *ep = 0;
1311 }
1312 else
1313 remote_ref = local_ref;
1314
1315 /* Lock remote branch ref */
1316 if (remote_path)
1317 free(remote_path);
1318 remote_path = xmalloc(strlen(remote_ref) + 12);
1319 sprintf(remote_path, "refs/heads/%s", remote_ref);
1320 remote_lock = lock_remote(remote_path, LOCK_TIME);
1321 if (remote_lock == NULL) {
1322 fprintf(stderr, "Unable to lock remote branch %s\n",
1323 remote_ref);
1324 rc = 1;
1325 continue;
1326 }
1327
1328 /* Resolve local and remote refs */
1329 if (fetch_ref(remote_ref, remote_sha1) != 0) {
1330 fprintf(stderr,
1331 "Remote branch %s does not exist on %s\n",
1332 remote_ref, remote->url);
1333 new_branch = 1;
1334 }
1335 if (get_sha1(local_ref, local_sha1) != 0) {
1336 fprintf(stderr, "Error resolving local branch %s\n",
1337 local_ref);
1338 rc = 1;
1339 goto unlock;
1340 }
1341
1342 /* Find relationship between local and remote */
1343 local_object = parse_object(local_sha1);
1344 if (!local_object) {
1345 fprintf(stderr, "Unable to parse local object %s\n",
1346 sha1_to_hex(local_sha1));
1347 rc = 1;
1348 goto unlock;
1349 } else if (new_branch) {
1350 do_remote_update = 1;
1351 } else {
1352 if (!memcmp(local_sha1, remote_sha1, 20)) {
1353 fprintf(stderr,
1354 "* %s: same as branch '%s' of %s\n",
1355 local_ref, remote_ref, remote->url);
1356 } else if (is_ancestor(remote_sha1,
1357 (struct commit *)local_object)) {
1358 fprintf(stderr,
1359 "Remote %s will fast-forward to local %s\n",
1360 remote_ref, local_ref);
1361 do_remote_update = 1;
1362 } else if (force_all || force_this) {
1363 fprintf(stderr,
1364 "* %s on %s does not fast forward to local branch '%s', overwriting\n",
1365 remote_ref, remote->url, local_ref);
1366 do_remote_update = 1;
1367 } else {
1368 fprintf(stderr,
1369 "* %s on %s does not fast forward to local branch '%s'\n",
1370 remote_ref, remote->url, local_ref);
1371 rc = 1;
1372 goto unlock;
1373 }
1374 }
1375
1376 /* Generate and check list of required objects */
1377 pushing = 0;
1378 if (do_remote_update || push_all)
1379 fetch_indices();
1380 get_delta(push_all ? NULL : remote_sha1,
1381 local_object, remote_lock);
1382 finish_all_active_slots();
1383
1384 /* Push missing objects to remote, this would be a
1385 convenient time to pack them first if appropriate. */
1386 pushing = 1;
1387 fill_active_slots();
1388 finish_all_active_slots();
1389
1390 /* Update the remote branch if all went well */
1391 if (do_remote_update) {
1392 if (!aborted && update_remote(local_sha1,
1393 remote_lock)) {
1394 fprintf(stderr, "%s remote branch %s\n",
1395 new_branch ? "Created" : "Updated",
1396 remote_ref);
1397 } else {
1398 fprintf(stderr,
1399 "Unable to %s remote branch %s\n",
1400 new_branch ? "create" : "update",
1401 remote_ref);
1402 rc = 1;
1403 goto unlock;
1404 }
1405 }
1406
1407 unlock:
1408 unlock_remote(remote_lock);
1409 free(remote_path);
1410 }
1411
1412 cleanup:
1413 free(remote);
1414
1415 curl_slist_free_all(no_pragma_header);
1416 curl_slist_free_all(default_headers);
1417
1418 http_cleanup();
1419
1420 request = request_queue_head;
1421 while (request != NULL) {
1422 next_request = request->next;
1423 release_request(request);
1424 request = next_request;
1425 }
1426
1427 return rc;
1428 }
1429 #else /* ifdef USE_CURL_MULTI */
1430 int main(int argc, char **argv)
1431 {
1432 fprintf(stderr, "http-push requires curl 7.9.8 or higher.\n");
1433 return 1;
1434 }
1435 #endif