http-push memory/fd cleanup
[git/dscho.git] / http-push.c
blobf3c92c971e65e9df72fafdab52b4935866a0a794
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"
9 #ifdef USE_CURL_MULTI
11 #include <expat.h>
13 static const char http_push_usage[] =
14 "git-http-push [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\n";
16 #ifndef XML_STATUS_OK
17 enum XML_Status {
18 XML_STATUS_OK = 1,
19 XML_STATUS_ERROR = 0
21 #define XML_STATUS_OK 1
22 #define XML_STATUS_ERROR 0
23 #endif
25 #define RANGE_HEADER_SIZE 30
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"
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)
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"
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>"
52 #define LOCK_TIME 600
53 #define LOCK_REFRESH 30
55 static int pushing = 0;
56 static int aborted = 0;
57 static char remote_dir_exists[256];
59 static struct curl_slist *no_pragma_header;
60 static struct curl_slist *default_headers;
62 static int push_verbosely = 0;
63 static int push_all = 0;
64 static int force_all = 0;
66 struct repo
68 char *url;
69 struct packed_git *packs;
72 static struct repo *remote = NULL;
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,
85 struct transfer_request
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;
108 static struct transfer_request *request_queue_head = NULL;
110 struct xml_ctx
112 char *name;
113 int len;
114 char *cdata;
115 void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
116 void *userData;
119 struct active_lock
121 char *url;
122 char *owner;
123 char *token;
124 time_t start_time;
125 long timeout;
126 int refreshing;
129 static void finish_request(struct transfer_request *request);
131 static void process_response(void *callback_data)
133 struct transfer_request *request =
134 (struct transfer_request *)callback_data;
136 finish_request(request);
139 static void start_check(struct transfer_request *request)
141 char *hex = sha1_to_hex(request->sha1);
142 struct active_request_slot *slot;
143 char *posn;
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);
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);
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;
172 static void start_mkcol(struct transfer_request *request)
174 char *hex = sha1_to_hex(request->sha1);
175 struct active_request_slot *slot;
176 char *posn;
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, "/");
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);
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;
206 static void start_put(struct transfer_request *request)
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;
219 unpacked = read_sha1_file(request->sha1, type, &len);
220 hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
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);
228 /* Compress it */
229 stream.next_out = request->buffer.buffer;
230 stream.avail_out = size;
232 /* First header.. */
233 stream.next_in = (void *)hdr;
234 stream.avail_in = hdrlen;
235 while (deflate(&stream, 0) == Z_OK)
236 /* nothing */;
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);
246 request->buffer.size = stream.total_out;
247 request->buffer.posn = 0;
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);
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);
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;
288 static void start_move(struct transfer_request *request)
290 struct active_request_slot *slot;
291 struct curl_slist *dav_headers = NULL;
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);
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;
314 static int refresh_lock(struct active_lock *lock)
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;
322 lock->refreshing = 1;
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);
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);
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;
347 lock->refreshing = 0;
348 curl_slist_free_all(dav_headers);
349 free(if_header);
351 return rc;
354 static void finish_request(struct transfer_request *request)
356 time_t current_time = time(NULL);
357 int time_remaining;
359 request->curl_result = request->slot->curl_result;
360 request->http_code = request->slot->http_code;
361 request->slot = NULL;
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;
373 if (request->headers != NULL)
374 curl_slist_free_all(request->headers);
376 /* URL is reused for MOVE after PUT */
377 if (request->state != RUN_PUT) {
378 free(request->url);
379 request->url = NULL;
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;
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;
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;
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;
434 static void release_request(struct transfer_request *request)
436 struct transfer_request *entry = request_queue_head;
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;
447 if (request->url != NULL)
448 free(request->url);
449 free(request);
452 void fill_active_slots(void)
454 struct transfer_request *request = request_queue_head;
455 struct active_request_slot *slot = active_queue_head;
456 int num_transfers;
458 if (aborted)
459 return;
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);
472 request = request->next;
475 while (slot != NULL) {
476 if (!slot->in_use && slot->curl != NULL) {
477 curl_easy_cleanup(slot->curl);
478 slot->curl = NULL;
480 slot = slot->next;
484 static void add_request(unsigned char *sha1, struct active_lock *lock)
486 struct transfer_request *request = request_queue_head;
487 struct packed_git *target;
489 while (request != NULL && memcmp(request->sha1, sha1, 20))
490 request = request->next;
491 if (request != NULL)
492 return;
494 target = find_sha1_pack(sha1, remote->packs);
495 if (target)
496 return;
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;
507 fill_active_slots();
508 step_active_slots();
511 static int fetch_index(unsigned char *sha1)
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;
521 FILE *indexfile;
522 struct active_request_slot *slot;
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);
537 } else {
538 return error("Unable to start request");
541 if (has_pack_index(sha1))
542 return 0;
544 if (push_verbosely)
545 fprintf(stderr, "Getting index for pack %s\n", hex);
547 sprintf(url, "%s/objects/pack/pack-%s.idx", remote->url, hex);
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);
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;
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);
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);
586 } else {
587 free(url);
588 fclose(indexfile);
589 return error("Unable to start request");
592 free(url);
593 fclose(indexfile);
595 return move_temp_to_file(tmpfile, filename);
598 static int setup_index(unsigned char *sha1)
600 struct packed_git *new_pack;
602 if (fetch_index(sha1))
603 return -1;
605 new_pack = parse_pack_index(sha1);
606 new_pack->next = remote->packs;
607 remote->packs = new_pack;
608 return 0;
611 static int fetch_indices(void)
613 unsigned char sha1[20];
614 char *url;
615 struct buffer buffer;
616 char *data;
617 int i = 0;
619 struct active_request_slot *slot;
621 data = xmalloc(4096);
622 memset(data, 0, 4096);
623 buffer.size = 4096;
624 buffer.posn = 0;
625 buffer.buffer = data;
627 if (push_verbosely)
628 fprintf(stderr, "Getting pack list\n");
630 url = xmalloc(strlen(remote->url) + 21);
631 sprintf(url, "%s/objects/info/packs", remote->url);
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);
648 } else {
649 free(buffer.buffer);
650 free(url);
651 return error("Unable to start request");
653 free(url);
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;
668 default:
669 while (data[i] != '\n')
670 i++;
672 i++;
675 free(buffer.buffer);
676 return 0;
679 static inline int needs_quote(int ch)
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;
690 static inline int hex(int v)
692 if (v < 10) return '0' + v;
693 else return 'A' + v - 10;
696 static char *quote_ref_url(const char *base, const char *ref)
698 const char *cp;
699 char *dp, *qref;
700 int len, baselen, ch;
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);
716 else
717 *dp++ = ch;
719 *dp = 0;
721 return qref;
724 int fetch_ref(char *ref, unsigned char *sha1)
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';
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");
751 hex[40] = '\0';
752 get_sha1_hex(hex, sha1);
753 return 0;
756 static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)
758 int *lock_flags = (int *)ctx->userData;
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;
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;
775 static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
777 struct active_lock *lock = (struct active_lock *)ctx->userData;
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);
796 static void
797 xml_start_tag(void *userData, const char *name, const char **atts)
799 struct xml_ctx *ctx = (struct xml_ctx *)userData;
800 const char *c = index(name, ':');
801 int new_len;
803 if (c == NULL)
804 c = name;
805 else
806 c++;
808 new_len = strlen(ctx->name) + strlen(c) + 2;
810 if (new_len > ctx->len) {
811 ctx->name = xrealloc(ctx->name, new_len);
812 ctx->len = new_len;
814 strcat(ctx->name, ".");
815 strcat(ctx->name, c);
817 if (ctx->cdata) {
818 free(ctx->cdata);
819 ctx->cdata = NULL;
822 ctx->userFunc(ctx, 0);
825 static void
826 xml_end_tag(void *userData, const char *name)
828 struct xml_ctx *ctx = (struct xml_ctx *)userData;
829 const char *c = index(name, ':');
830 char *ep;
832 ctx->userFunc(ctx, 1);
834 if (c == NULL)
835 c = name;
836 else
837 c++;
839 ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
840 *ep = 0;
843 static void
844 xml_cdata(void *userData, const XML_Char *s, int len)
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);
853 static struct active_lock *lock_remote(char *file, long timeout)
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;
869 url = xmalloc(strlen(remote->url) + strlen(file) + 1);
870 sprintf(url, "%s%s", remote->url, file);
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;
891 } else {
892 fprintf(stderr, "Unable to start request\n");
893 free(url);
894 return NULL;
896 *ep = '/';
897 ep = strchr(ep + 1, '/');
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;
906 in_buffer.size = 4096;
907 in_data = xmalloc(in_buffer.size);
908 in_buffer.posn = 0;
909 in_buffer.buffer = in_data;
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");
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);
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;
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;
954 } else {
955 fprintf(stderr, "Unable to start request\n");
958 curl_slist_free_all(dav_headers);
959 free(out_data);
960 free(in_data);
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);
975 return new_lock;
978 static int unlock_remote(struct active_lock *lock)
980 struct active_request_slot *slot;
981 char *lock_token_header;
982 struct curl_slist *dav_headers = NULL;
983 int rc = 0;
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);
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);
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");
1007 curl_slist_free_all(dav_headers);
1008 free(lock_token_header);
1010 if (lock->owner != NULL)
1011 free(lock->owner);
1012 free(lock->url);
1013 /* Freeing the token causes a segfault...
1014 free(lock->token);
1016 free(lock);
1018 return rc;
1021 static int locking_available(void)
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;
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;
1040 in_buffer.size = 4096;
1041 in_data = xmalloc(in_buffer.size);
1042 in_buffer.posn = 0;
1043 in_buffer.buffer = in_data;
1045 dav_headers = curl_slist_append(dav_headers, "Depth: 0");
1046 dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
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);
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);
1074 if (result != XML_STATUS_OK) {
1075 fprintf(stderr, "XML error: %s\n",
1076 XML_ErrorString(
1077 XML_GetErrorCode(parser)));
1078 lock_flags = 0;
1081 } else {
1082 fprintf(stderr, "Unable to start request\n");
1085 free(out_data);
1086 free(in_buffer.buffer);
1087 curl_slist_free_all(dav_headers);
1089 return lock_flags;
1092 static int is_ancestor(unsigned char *sha1, struct commit *commit)
1094 struct commit_list *parents;
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
1107 return 1;
1110 return 0;
1113 static void get_delta(unsigned char *sha1, struct object *obj,
1114 struct active_lock *lock)
1116 struct commit *commit;
1117 struct commit_list *parents;
1118 struct tree *tree;
1119 struct tree_entry_list *entry;
1121 if (sha1 && !memcmp(sha1, obj->sha1, 20))
1122 return;
1124 if (aborted)
1125 return;
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;
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;
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;
1165 } else if (obj->type == blob_type || obj->type == tag_type) {
1166 add_request(obj->sha1, lock);
1170 static int update_remote(unsigned char *sha1, struct active_lock *lock)
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;
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);
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;
1190 out_buffer.posn = 0;
1191 out_buffer.buffer = out_data;
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);
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;
1215 } else {
1216 free(out_data);
1217 free(if_header);
1218 fprintf(stderr, "Unable to start PUT request\n");
1219 return 0;
1222 return 1;
1225 int main(int argc, char **argv)
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;
1244 setup_ident();
1246 remote = xmalloc(sizeof(*remote));
1247 remote->url = NULL;
1248 remote->packs = NULL;
1250 argv++;
1251 for (i = 1; i < argc; i++, argv++) {
1252 char *arg = *argv;
1254 if (*arg == '-') {
1255 if (!strcmp(arg, "--complete")) {
1256 push_all = 1;
1257 continue;
1259 if (!strcmp(arg, "--force")) {
1260 force_all = 1;
1261 continue;
1263 if (!strcmp(arg, "--verbose")) {
1264 push_verbosely = 1;
1265 continue;
1267 usage(http_push_usage);
1269 if (!remote->url) {
1270 remote->url = arg;
1271 continue;
1273 refspec = argv;
1274 nr_refspec = argc - i;
1275 break;
1278 memset(remote_dir_exists, 0, 256);
1280 http_init();
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");
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;
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++;
1307 ep = strchr(local_ref, ':');
1308 if (ep) {
1309 remote_ref = ep + 1;
1310 *ep = 0;
1312 else
1313 remote_ref = local_ref;
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;
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;
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;
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;
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();
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();
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;
1407 unlock:
1408 unlock_remote(remote_lock);
1409 free(remote_path);
1412 cleanup:
1413 free(remote);
1415 curl_slist_free_all(no_pragma_header);
1416 curl_slist_free_all(default_headers);
1418 http_cleanup();
1420 request = request_queue_head;
1421 while (request != NULL) {
1422 next_request = request->next;
1423 release_request(request);
1424 request = next_request;
1427 return rc;
1429 #else /* ifdef USE_CURL_MULTI */
1430 int main(int argc, char **argv)
1432 fprintf(stderr, "http-push requires curl 7.9.8 or higher.\n");
1433 return 1;
1435 #endif