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