13 static const char http_push_usage[] =
14 "git-http-push [--complete] [--force] [--verbose] <url> <ref> [<ref>...]\n";
21 #define XML_STATUS_OK 1
22 #define XML_STATUS_ERROR 0
25 #define RANGE_HEADER_SIZE 30
28 #define DAV_LOCK "LOCK"
29 #define DAV_MKCOL "MKCOL"
30 #define DAV_MOVE "MOVE"
31 #define DAV_PROPFIND "PROPFIND"
33 #define DAV_UNLOCK "UNLOCK"
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>"
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;
69 struct packed_git *packs;
72 static struct repo *remote = NULL;
85 struct transfer_request
87 unsigned char sha1[20];
90 struct active_lock *lock;
91 struct curl_slist *headers;
93 char filename[PATH_MAX];
94 char tmpfile[PATH_MAX];
95 enum transfer_state state;
97 char errorstr[CURL_ERROR_SIZE];
99 unsigned char real_sha1[20];
104 struct active_request_slot *slot;
105 struct transfer_request *next;
108 static struct transfer_request *request_queue_head = NULL;
115 void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
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;
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/");
150 memcpy(posn, hex, 2);
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;
166 request->state = ABORTED;
171 static void start_mkcol(struct transfer_request *request)
173 char *hex = sha1_to_hex(request->sha1);
174 struct active_request_slot *slot;
177 request->url = xmalloc(strlen(remote->url) + 13);
178 strcpy(request->url, remote->url);
179 posn = request->url + strlen(remote->url);
180 strcpy(posn, "objects/");
182 memcpy(posn, hex, 2);
186 slot = get_active_slot();
187 slot->callback_func = process_response;
188 slot->callback_data = request;
189 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
190 curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
191 curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr);
192 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
193 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
195 if (start_active_slot(slot)) {
196 request->slot = slot;
197 request->state = RUN_MKCOL;
199 request->state = ABORTED;
204 static void start_put(struct transfer_request *request)
206 char *hex = sha1_to_hex(request->sha1);
207 struct active_request_slot *slot;
217 unpacked = read_sha1_file(request->sha1, type, &len);
218 hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
221 memset(&stream, 0, sizeof(stream));
222 deflateInit(&stream, Z_BEST_COMPRESSION);
223 size = deflateBound(&stream, len + hdrlen);
224 request->buffer.buffer = xmalloc(size);
227 stream.next_out = request->buffer.buffer;
228 stream.avail_out = size;
231 stream.next_in = (void *)hdr;
232 stream.avail_in = hdrlen;
233 while (deflate(&stream, 0) == Z_OK)
236 /* Then the data itself.. */
237 stream.next_in = unpacked;
238 stream.avail_in = len;
239 while (deflate(&stream, Z_FINISH) == Z_OK)
244 request->buffer.size = stream.total_out;
245 request->buffer.posn = 0;
247 if (request->url != NULL)
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/");
255 memcpy(posn, hex, 2);
258 strcpy(posn, hex + 2);
259 request->dest = xmalloc(strlen(request->url) + 14);
260 sprintf(request->dest, "Destination: %s", request->url);
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;
282 request->state = ABORTED;
287 static void start_move(struct transfer_request *request)
289 struct active_request_slot *slot;
290 struct curl_slist *dav_headers = NULL;
292 slot = get_active_slot();
293 slot->callback_func = process_response;
294 slot->callback_data = request;
295 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */
296 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE);
297 dav_headers = curl_slist_append(dav_headers, request->dest);
298 dav_headers = curl_slist_append(dav_headers, "Overwrite: T");
299 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
300 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
301 curl_easy_setopt(slot->curl, CURLOPT_URL, request->url);
303 if (start_active_slot(slot)) {
304 request->slot = slot;
305 request->state = RUN_MOVE;
307 request->state = ABORTED;
312 static int refresh_lock(struct active_lock *lock)
314 struct active_request_slot *slot;
316 char timeout_header[25];
317 struct curl_slist *dav_headers = NULL;
320 lock->refreshing = 1;
322 if_header = xmalloc(strlen(lock->token) + 25);
323 sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
324 sprintf(timeout_header, "Timeout: Second-%ld", lock->timeout);
325 dav_headers = curl_slist_append(dav_headers, if_header);
326 dav_headers = curl_slist_append(dav_headers, timeout_header);
328 slot = get_active_slot();
329 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
330 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
331 curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
332 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
333 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
335 if (start_active_slot(slot)) {
336 run_active_slot(slot);
337 if (slot->curl_result != CURLE_OK) {
338 fprintf(stderr, "Got HTTP error %ld\n", slot->http_code);
340 lock->start_time = time(NULL);
345 lock->refreshing = 0;
346 curl_slist_free_all(dav_headers);
352 static void finish_request(struct transfer_request *request)
354 time_t current_time = time(NULL);
357 request->curl_result = request->slot->curl_result;
358 request->http_code = request->slot->http_code;
359 request->slot = NULL;
361 /* Refresh the lock if it is close to timing out */
362 time_remaining = request->lock->start_time + request->lock->timeout
364 if (time_remaining < LOCK_REFRESH && !request->lock->refreshing) {
365 if (!refresh_lock(request->lock)) {
366 fprintf(stderr, "Unable to refresh remote lock\n");
371 if (request->headers != NULL)
372 curl_slist_free_all(request->headers);
373 if (request->state == RUN_HEAD) {
374 if (request->http_code == 404) {
375 request->state = NEED_PUSH;
376 } else if (request->curl_result == CURLE_OK) {
377 remote_dir_exists[request->sha1[0]] = 1;
378 request->state = COMPLETE;
380 fprintf(stderr, "HEAD %s failed, aborting (%d/%ld)\n",
381 sha1_to_hex(request->sha1),
382 request->curl_result, request->http_code);
383 request->state = ABORTED;
386 } else if (request->state == RUN_MKCOL) {
387 if (request->curl_result == CURLE_OK ||
388 request->http_code == 405) {
389 remote_dir_exists[request->sha1[0]] = 1;
392 fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n",
393 sha1_to_hex(request->sha1),
394 request->curl_result, request->http_code);
395 request->state = ABORTED;
398 } else if (request->state == RUN_PUT) {
399 if (request->curl_result == CURLE_OK) {
402 fprintf(stderr, "PUT %s failed, aborting (%d/%ld)\n",
403 sha1_to_hex(request->sha1),
404 request->curl_result, request->http_code);
405 request->state = ABORTED;
408 } else if (request->state == RUN_MOVE) {
409 if (request->curl_result == CURLE_OK) {
413 sha1_to_hex(request->sha1));
414 request->state = COMPLETE;
416 fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n",
417 sha1_to_hex(request->sha1),
418 request->curl_result, request->http_code);
419 request->state = ABORTED;
425 static void release_request(struct transfer_request *request)
427 struct transfer_request *entry = request_queue_head;
429 if (request == request_queue_head) {
430 request_queue_head = request->next;
432 while (entry->next != NULL && entry->next != request)
434 if (entry->next == request)
435 entry->next = entry->next->next;
442 void fill_active_slots(void)
444 struct transfer_request *request = request_queue_head;
445 struct active_request_slot *slot = active_queue_head;
451 while (active_requests < max_requests && request != NULL) {
452 if (!pushing && request->state == NEED_CHECK) {
453 start_check(request);
454 curl_multi_perform(curlm, &num_transfers);
455 } else if (pushing && request->state == NEED_PUSH) {
456 if (remote_dir_exists[request->sha1[0]])
459 start_mkcol(request);
460 curl_multi_perform(curlm, &num_transfers);
462 request = request->next;
465 while (slot != NULL) {
466 if (!slot->in_use && slot->curl != NULL) {
467 curl_easy_cleanup(slot->curl);
474 static void add_request(unsigned char *sha1, struct active_lock *lock)
476 struct transfer_request *request = request_queue_head;
477 struct packed_git *target;
479 while (request != NULL && memcmp(request->sha1, sha1, 20))
480 request = request->next;
484 target = find_sha1_pack(sha1, remote->packs);
488 request = xmalloc(sizeof(*request));
489 memcpy(request->sha1, sha1, 20);
491 request->lock = lock;
492 request->headers = NULL;
493 request->state = NEED_CHECK;
494 request->next = request_queue_head;
495 request_queue_head = request;
501 static int fetch_index(unsigned char *sha1)
503 char *hex = sha1_to_hex(sha1);
506 char tmpfile[PATH_MAX];
508 char range[RANGE_HEADER_SIZE];
509 struct curl_slist *range_header = NULL;
512 struct active_request_slot *slot;
514 /* Don't use the index if the pack isn't there */
515 url = xmalloc(strlen(remote->url) + 65);
516 sprintf(url, "%s/objects/pack/pack-%s.pack", remote->url, hex);
517 slot = get_active_slot();
518 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
519 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
520 if (start_active_slot(slot)) {
521 run_active_slot(slot);
522 if (slot->curl_result != CURLE_OK) {
524 return error("Unable to verify pack %s is available",
528 return error("Unable to start request");
531 if (has_pack_index(sha1))
535 fprintf(stderr, "Getting index for pack %s\n", hex);
537 sprintf(url, "%s/objects/pack/pack-%s.idx", remote->url, hex);
539 filename = sha1_pack_index_name(sha1);
540 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
541 indexfile = fopen(tmpfile, "a");
543 return error("Unable to open local file %s for pack index",
546 slot = get_active_slot();
547 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
548 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
549 curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
550 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
551 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
552 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
553 slot->local = indexfile;
555 /* If there is data present from a previous transfer attempt,
556 resume where it left off */
557 prev_posn = ftell(indexfile);
561 "Resuming fetch of index for pack %s at byte %ld\n",
563 sprintf(range, "Range: bytes=%ld-", prev_posn);
564 range_header = curl_slist_append(range_header, range);
565 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
568 if (start_active_slot(slot)) {
569 run_active_slot(slot);
570 if (slot->curl_result != CURLE_OK) {
573 return error("Unable to get pack index %s\n%s", url,
578 return error("Unable to start request");
584 return move_temp_to_file(tmpfile, filename);
587 static int setup_index(unsigned char *sha1)
589 struct packed_git *new_pack;
591 if (fetch_index(sha1))
594 new_pack = parse_pack_index(sha1);
595 new_pack->next = remote->packs;
596 remote->packs = new_pack;
600 static int fetch_indices(void)
602 unsigned char sha1[20];
604 struct buffer buffer;
608 struct active_request_slot *slot;
610 data = xmalloc(4096);
611 memset(data, 0, 4096);
614 buffer.buffer = data;
617 fprintf(stderr, "Getting pack list\n");
619 url = xmalloc(strlen(remote->url) + 21);
620 sprintf(url, "%s/objects/info/packs", remote->url);
622 slot = get_active_slot();
623 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
624 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
625 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
626 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
627 if (start_active_slot(slot)) {
628 run_active_slot(slot);
629 if (slot->curl_result != CURLE_OK) {
632 if (slot->http_code == 404)
635 return error("%s", curl_errorstr);
640 return error("Unable to start request");
644 data = buffer.buffer;
645 while (i < buffer.posn) {
649 if (i + 52 < buffer.posn &&
650 !strncmp(data + i, " pack-", 6) &&
651 !strncmp(data + i + 46, ".pack\n", 6)) {
652 get_sha1_hex(data + i + 6, sha1);
658 while (data[i] != '\n')
668 static inline int needs_quote(int ch)
671 case '/': case '-': case '.':
672 case 'A'...'Z': case 'a'...'z': case '0'...'9':
679 static inline int hex(int v)
681 if (v < 10) return '0' + v;
682 else return 'A' + v - 10;
685 static char *quote_ref_url(const char *base, const char *ref)
689 int len, baselen, ch;
691 baselen = strlen(base);
692 len = baselen + 12; /* "refs/heads/" + NUL */
693 for (cp = ref; (ch = *cp) != 0; cp++, len++)
695 len += 2; /* extra two hex plus replacement % */
697 memcpy(qref, base, baselen);
698 memcpy(qref + baselen, "refs/heads/", 11);
699 for (cp = ref, dp = qref + baselen + 11; (ch = *cp) != 0; cp++) {
700 if (needs_quote(ch)) {
702 *dp++ = hex((ch >> 4) & 0xF);
703 *dp++ = hex(ch & 0xF);
713 int fetch_ref(char *ref, unsigned char *sha1)
717 struct buffer buffer;
718 char *base = remote->url;
719 struct active_request_slot *slot;
725 url = quote_ref_url(base, ref);
726 slot = get_active_slot();
727 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
728 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
729 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
730 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
731 if (start_active_slot(slot)) {
732 run_active_slot(slot);
733 if (slot->curl_result != CURLE_OK)
734 return error("Couldn't get %s for %s\n%s",
735 url, ref, curl_errorstr);
737 return error("Unable to start request");
741 get_sha1_hex(hex, sha1);
745 static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)
747 int *lock_flags = (int *)ctx->userData;
750 if (!strcmp(ctx->name, DAV_CTX_LOCKENTRY)) {
751 if ((*lock_flags & DAV_PROP_LOCKEX) &&
752 (*lock_flags & DAV_PROP_LOCKWR)) {
753 *lock_flags |= DAV_LOCK_OK;
755 *lock_flags &= DAV_LOCK_OK;
756 } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_WRITE)) {
757 *lock_flags |= DAV_PROP_LOCKWR;
758 } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_EXCLUSIVE)) {
759 *lock_flags |= DAV_PROP_LOCKEX;
764 static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
766 struct active_lock *lock = (struct active_lock *)ctx->userData;
768 if (tag_closed && ctx->cdata) {
769 if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
770 lock->owner = xmalloc(strlen(ctx->cdata) + 1);
771 strcpy(lock->owner, ctx->cdata);
772 } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
773 if (!strncmp(ctx->cdata, "Second-", 7))
775 strtol(ctx->cdata + 7, NULL, 10);
776 } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
777 if (!strncmp(ctx->cdata, "opaquelocktoken:", 16)) {
778 lock->token = xmalloc(strlen(ctx->cdata - 15));
779 strcpy(lock->token, ctx->cdata + 16);
786 xml_start_tag(void *userData, const char *name, const char **atts)
788 struct xml_ctx *ctx = (struct xml_ctx *)userData;
789 const char *c = index(name, ':');
797 new_len = strlen(ctx->name) + strlen(c) + 2;
799 if (new_len > ctx->len) {
800 ctx->name = xrealloc(ctx->name, new_len);
803 strcat(ctx->name, ".");
804 strcat(ctx->name, c);
811 ctx->userFunc(ctx, 0);
815 xml_end_tag(void *userData, const char *name)
817 struct xml_ctx *ctx = (struct xml_ctx *)userData;
818 const char *c = index(name, ':');
821 ctx->userFunc(ctx, 1);
828 ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
833 xml_cdata(void *userData, const XML_Char *s, int len)
835 struct xml_ctx *ctx = (struct xml_ctx *)userData;
838 ctx->cdata = xcalloc(len+1, 1);
839 strncpy(ctx->cdata, s, len);
842 static struct active_lock *lock_remote(char *file, long timeout)
844 struct active_request_slot *slot;
845 struct buffer out_buffer;
846 struct buffer in_buffer;
851 char timeout_header[25];
852 struct active_lock *new_lock = NULL;
853 XML_Parser parser = XML_ParserCreate(NULL);
854 enum XML_Status result;
855 struct curl_slist *dav_headers = NULL;
858 url = xmalloc(strlen(remote->url) + strlen(file) + 1);
859 sprintf(url, "%s%s", remote->url, file);
861 /* Make sure leading directories exist for the remote ref */
862 ep = strchr(url + strlen(remote->url) + 11, '/');
865 slot = get_active_slot();
866 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
867 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
868 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MKCOL);
869 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
870 if (start_active_slot(slot)) {
871 run_active_slot(slot);
872 if (slot->curl_result != CURLE_OK &&
873 slot->http_code != 405) {
875 "Unable to create branch path %s\n",
881 fprintf(stderr, "Unable to start request\n");
886 ep = strchr(ep + 1, '/');
889 out_buffer.size = strlen(LOCK_REQUEST) + strlen(git_default_email) - 2;
890 out_data = xmalloc(out_buffer.size + 1);
891 snprintf(out_data, out_buffer.size + 1, LOCK_REQUEST, git_default_email);
893 out_buffer.buffer = out_data;
895 in_buffer.size = 4096;
896 in_data = xmalloc(in_buffer.size);
898 in_buffer.buffer = in_data;
900 sprintf(timeout_header, "Timeout: Second-%ld", timeout);
901 dav_headers = curl_slist_append(dav_headers, timeout_header);
902 dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
904 slot = get_active_slot();
905 curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
906 curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
907 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
908 curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
909 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
910 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
911 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
912 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
913 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
915 new_lock = xcalloc(1, sizeof(*new_lock));
916 new_lock->owner = NULL;
917 new_lock->token = NULL;
918 new_lock->timeout = -1;
919 new_lock->refreshing = 0;
921 if (start_active_slot(slot)) {
922 run_active_slot(slot);
923 if (slot->curl_result == CURLE_OK) {
924 ctx.name = xcalloc(10, 1);
927 ctx.userFunc = handle_new_lock_ctx;
928 ctx.userData = new_lock;
929 XML_SetUserData(parser, &ctx);
930 XML_SetElementHandler(parser, xml_start_tag,
932 XML_SetCharacterDataHandler(parser, xml_cdata);
933 result = XML_Parse(parser, in_buffer.buffer,
936 if (result != XML_STATUS_OK) {
937 fprintf(stderr, "XML error: %s\n",
939 XML_GetErrorCode(parser)));
940 new_lock->timeout = -1;
944 fprintf(stderr, "Unable to start request\n");
947 curl_slist_free_all(dav_headers);
951 if (new_lock->token == NULL || new_lock->timeout <= 0) {
952 if (new_lock->token != NULL)
953 free(new_lock->token);
954 if (new_lock->owner != NULL)
955 free(new_lock->owner);
961 new_lock->start_time = time(NULL);
967 static int unlock_remote(struct active_lock *lock)
969 struct active_request_slot *slot;
970 char *lock_token_header;
971 struct curl_slist *dav_headers = NULL;
974 lock_token_header = xmalloc(strlen(lock->token) + 31);
975 sprintf(lock_token_header, "Lock-Token: <opaquelocktoken:%s>",
977 dav_headers = curl_slist_append(dav_headers, lock_token_header);
979 slot = get_active_slot();
980 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
981 curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
982 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_UNLOCK);
983 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
985 if (start_active_slot(slot)) {
986 run_active_slot(slot);
987 if (slot->curl_result == CURLE_OK)
990 fprintf(stderr, "Got HTTP error %ld\n",
993 fprintf(stderr, "Unable to start request\n");
996 curl_slist_free_all(dav_headers);
997 free(lock_token_header);
999 if (lock->owner != NULL)
1002 /* Freeing the token causes a segfault...
1010 static int locking_available(void)
1012 struct active_request_slot *slot;
1013 struct buffer in_buffer;
1014 struct buffer out_buffer;
1017 XML_Parser parser = XML_ParserCreate(NULL);
1018 enum XML_Status result;
1019 struct curl_slist *dav_headers = NULL;
1023 out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2;
1024 out_data = xmalloc(out_buffer.size + 1);
1025 snprintf(out_data, out_buffer.size + 1, PROPFIND_REQUEST, remote->url);
1026 out_buffer.posn = 0;
1027 out_buffer.buffer = out_data;
1029 in_buffer.size = 4096;
1030 in_data = xmalloc(in_buffer.size);
1032 in_buffer.buffer = in_data;
1034 dav_headers = curl_slist_append(dav_headers, "Depth: 0");
1035 dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
1037 slot = get_active_slot();
1038 curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
1039 curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
1040 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
1041 curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
1042 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
1043 curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url);
1044 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
1045 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
1046 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
1048 if (start_active_slot(slot)) {
1049 run_active_slot(slot);
1050 if (slot->curl_result == CURLE_OK) {
1051 ctx.name = xcalloc(10, 1);
1054 ctx.userFunc = handle_lockprop_ctx;
1055 ctx.userData = &lock_flags;
1056 XML_SetUserData(parser, &ctx);
1057 XML_SetElementHandler(parser, xml_start_tag,
1059 result = XML_Parse(parser, in_buffer.buffer,
1063 if (result != XML_STATUS_OK) {
1064 fprintf(stderr, "XML error: %s\n",
1066 XML_GetErrorCode(parser)));
1071 fprintf(stderr, "Unable to start request\n");
1075 free(in_buffer.buffer);
1076 curl_slist_free_all(dav_headers);
1081 static int is_ancestor(unsigned char *sha1, struct commit *commit)
1083 struct commit_list *parents;
1085 if (parse_commit(commit))
1087 parents = commit->parents;
1088 for (; parents; parents = parents->next) {
1089 if (!memcmp(sha1, parents->item->object.sha1, 20)) {
1091 } else if (parents->item->object.type == commit_type) {
1094 (struct commit *)&parents->item->object
1102 static void get_delta(unsigned char *sha1, struct object *obj,
1103 struct active_lock *lock)
1105 struct commit *commit;
1106 struct commit_list *parents;
1108 struct tree_entry_list *entry;
1110 if (sha1 && !memcmp(sha1, obj->sha1, 20))
1116 if (obj->type == commit_type) {
1118 fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
1119 add_request(obj->sha1, lock);
1120 commit = (struct commit *)obj;
1121 if (parse_commit(commit)) {
1122 fprintf(stderr, "Error parsing commit %s\n",
1123 sha1_to_hex(obj->sha1));
1127 parents = commit->parents;
1128 for (; parents; parents = parents->next)
1130 memcmp(sha1, parents->item->object.sha1, 20))
1131 get_delta(sha1, &parents->item->object,
1133 get_delta(sha1, &commit->tree->object, lock);
1134 } else if (obj->type == tree_type) {
1136 fprintf(stderr, "walk %s\n", sha1_to_hex(obj->sha1));
1137 add_request(obj->sha1, lock);
1138 tree = (struct tree *)obj;
1139 if (parse_tree(tree)) {
1140 fprintf(stderr, "Error parsing tree %s\n",
1141 sha1_to_hex(obj->sha1));
1145 entry = tree->entries;
1146 tree->entries = NULL;
1148 struct tree_entry_list *next = entry->next;
1149 get_delta(sha1, entry->item.any, lock);
1154 } else if (obj->type == blob_type || obj->type == tag_type) {
1155 add_request(obj->sha1, lock);
1159 static int update_remote(unsigned char *sha1, struct active_lock *lock)
1161 struct active_request_slot *slot;
1164 struct buffer out_buffer;
1165 struct curl_slist *dav_headers = NULL;
1168 if_header = xmalloc(strlen(lock->token) + 25);
1169 sprintf(if_header, "If: (<opaquelocktoken:%s>)", lock->token);
1170 dav_headers = curl_slist_append(dav_headers, if_header);
1172 out_buffer.size = 41;
1173 out_data = xmalloc(out_buffer.size + 1);
1174 i = snprintf(out_data, out_buffer.size + 1, "%s\n", sha1_to_hex(sha1));
1175 if (i != out_buffer.size) {
1176 fprintf(stderr, "Unable to initialize PUT request body\n");
1179 out_buffer.posn = 0;
1180 out_buffer.buffer = out_data;
1182 slot = get_active_slot();
1183 curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
1184 curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
1185 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
1186 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_null);
1187 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PUT);
1188 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
1189 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
1190 curl_easy_setopt(slot->curl, CURLOPT_PUT, 1);
1191 curl_easy_setopt(slot->curl, CURLOPT_URL, lock->url);
1193 if (start_active_slot(slot)) {
1194 run_active_slot(slot);
1197 if (slot->curl_result != CURLE_OK) {
1199 "PUT error: curl result=%d, HTTP code=%ld\n",
1200 slot->curl_result, slot->http_code);
1201 /* We should attempt recovery? */
1207 fprintf(stderr, "Unable to start PUT request\n");
1214 int main(int argc, char **argv)
1216 struct transfer_request *request;
1217 struct transfer_request *next_request;
1219 char **refspec = NULL;
1220 int do_remote_update;
1224 unsigned char local_sha1[20];
1225 struct object *local_object = NULL;
1226 char *remote_ref = NULL;
1227 unsigned char remote_sha1[20];
1228 struct active_lock *remote_lock;
1229 char *remote_path = NULL;
1235 remote = xmalloc(sizeof(*remote));
1237 remote->packs = NULL;
1240 for (i = 1; i < argc; i++, argv++) {
1244 if (!strcmp(arg, "--complete")) {
1248 if (!strcmp(arg, "--force")) {
1252 if (!strcmp(arg, "--verbose")) {
1256 usage(http_push_usage);
1263 nr_refspec = argc - i;
1267 memset(remote_dir_exists, 0, 256);
1271 no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
1272 default_headers = curl_slist_append(default_headers, "Range:");
1273 default_headers = curl_slist_append(default_headers, "Destination:");
1274 default_headers = curl_slist_append(default_headers, "If:");
1275 default_headers = curl_slist_append(default_headers,
1276 "Pragma: no-cache");
1278 /* Verify DAV compliance/lock support */
1279 if (!locking_available()) {
1280 fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url);
1285 /* Process each refspec */
1286 for (i = 0; i < nr_refspec; i++) {
1289 do_remote_update = 0;
1291 local_ref = refspec[i];
1292 if (*local_ref == '+') {
1296 ep = strchr(local_ref, ':');
1298 remote_ref = ep + 1;
1302 remote_ref = local_ref;
1304 /* Lock remote branch ref */
1307 remote_path = xmalloc(strlen(remote_ref) + 12);
1308 sprintf(remote_path, "refs/heads/%s", remote_ref);
1309 remote_lock = lock_remote(remote_path, LOCK_TIME);
1310 if (remote_lock == NULL) {
1311 fprintf(stderr, "Unable to lock remote branch %s\n",
1317 /* Resolve local and remote refs */
1318 if (fetch_ref(remote_ref, remote_sha1) != 0) {
1320 "Remote branch %s does not exist on %s\n",
1321 remote_ref, remote->url);
1324 if (get_sha1(local_ref, local_sha1) != 0) {
1325 fprintf(stderr, "Error resolving local branch %s\n",
1331 /* Find relationship between local and remote */
1332 local_object = parse_object(local_sha1);
1333 if (!local_object) {
1334 fprintf(stderr, "Unable to parse local object %s\n",
1335 sha1_to_hex(local_sha1));
1338 } else if (new_branch) {
1339 do_remote_update = 1;
1341 if (!memcmp(local_sha1, remote_sha1, 20)) {
1343 "* %s: same as branch '%s' of %s\n",
1344 local_ref, remote_ref, remote->url);
1345 } else if (is_ancestor(remote_sha1,
1346 (struct commit *)local_object)) {
1348 "Remote %s will fast-forward to local %s\n",
1349 remote_ref, local_ref);
1350 do_remote_update = 1;
1351 } else if (force_all || force_this) {
1353 "* %s on %s does not fast forward to local branch '%s', overwriting\n",
1354 remote_ref, remote->url, local_ref);
1355 do_remote_update = 1;
1358 "* %s on %s does not fast forward to local branch '%s'\n",
1359 remote_ref, remote->url, local_ref);
1365 /* Generate and check list of required objects */
1367 if (do_remote_update || push_all)
1369 get_delta(push_all ? NULL : remote_sha1,
1370 local_object, remote_lock);
1371 finish_all_active_slots();
1373 /* Push missing objects to remote, this would be a
1374 convenient time to pack them first if appropriate. */
1376 fill_active_slots();
1377 finish_all_active_slots();
1379 /* Update the remote branch if all went well */
1380 if (do_remote_update) {
1381 if (!aborted && update_remote(local_sha1,
1383 fprintf(stderr, "%s remote branch %s\n",
1384 new_branch ? "Created" : "Updated",
1388 "Unable to %s remote branch %s\n",
1389 new_branch ? "create" : "update",
1397 unlock_remote(remote_lock);
1404 curl_slist_free_all(no_pragma_header);
1405 curl_slist_free_all(default_headers);
1409 request = request_queue_head;
1410 while (request != NULL) {
1411 next_request = request->next;
1412 release_request(request);
1413 request = next_request;
1418 #else /* ifdef USE_CURL_MULTI */
1419 int main(int argc, char **argv)
1421 fprintf(stderr, "http-push requires curl 7.9.8 or higher.\n");