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