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