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