Teach git-local-fetch the --stdin switch
[git/git.git] / http-fetch.c
CommitLineData
6eb7ed54
DB
1#include "cache.h"
2#include "commit.h"
271421cd 3#include "pack.h"
215a7ad1 4#include "fetch.h"
29508e1e 5#include "http.h"
7baa3e86 6
8d9fbe57
NH
7#ifndef NO_EXPAT
8#include <expat.h>
9
10/* Definitions for DAV requests */
11#define DAV_PROPFIND "PROPFIND"
12#define DAV_PROPFIND_RESP ".multistatus.response"
13#define DAV_PROPFIND_NAME ".multistatus.response.href"
14#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection"
15#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>"
16
17/* Definitions for processing XML DAV responses */
18#ifndef XML_STATUS_OK
19enum XML_Status {
20 XML_STATUS_OK = 1,
21 XML_STATUS_ERROR = 0
22};
23#define XML_STATUS_OK 1
24#define XML_STATUS_ERROR 0
25#endif
26
27/* Flags that control remote_ls processing */
28#define PROCESS_FILES (1u << 0)
29#define PROCESS_DIRS (1u << 1)
30#define RECURSIVE (1u << 2)
31
32/* Flags that remote_ls passes to callback functions */
33#define IS_DIR (1u << 0)
34#endif
35
49a0f240
NH
36#define PREV_BUF_SIZE 4096
37#define RANGE_HEADER_SIZE 30
38
acc075a8 39static int got_alternates = -1;
bb528079 40static int corrupt_object_found = 0;
1d389ab6 41
1db69b57 42static struct curl_slist *no_pragma_header;
6eb7ed54 43
b3661567
DB
44struct alt_base
45{
46 char *base;
8d9fbe57 47 int path_len;
b3661567
DB
48 int got_indices;
49 struct packed_git *packs;
50 struct alt_base *next;
51};
52
a7928f8e 53static struct alt_base *alt = NULL;
6eb7ed54 54
e388ab74 55enum object_request_state {
1d389ab6
NH
56 WAITING,
57 ABORTED,
58 ACTIVE,
59 COMPLETE,
60};
6eb7ed54 61
e388ab74 62struct object_request
1d389ab6
NH
63{
64 unsigned char sha1[20];
65 struct alt_base *repo;
66 char *url;
67 char filename[PATH_MAX];
68 char tmpfile[PATH_MAX];
69 int local;
e388ab74 70 enum object_request_state state;
1d389ab6
NH
71 CURLcode curl_result;
72 char errorstr[CURL_ERROR_SIZE];
73 long http_code;
74 unsigned char real_sha1[20];
75 SHA_CTX c;
76 z_stream stream;
77 int zret;
78 int rename;
79 struct active_request_slot *slot;
e388ab74 80 struct object_request *next;
1d389ab6
NH
81};
82
e388ab74 83struct alternates_request {
acc075a8
NH
84 char *base;
85 char *url;
86 struct buffer *buffer;
87 struct active_request_slot *slot;
88 int http_specific;
89};
90
8d9fbe57
NH
91#ifndef NO_EXPAT
92struct xml_ctx
93{
94 char *name;
95 int len;
96 char *cdata;
97 void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
98 void *userData;
99};
100
101struct remote_ls_ctx
102{
103 struct alt_base *repo;
104 char *path;
105 void (*userFunc)(struct remote_ls_ctx *ls);
106 void *userData;
107 int flags;
108 char *dentry_name;
109 int dentry_flags;
110 int rc;
111 struct remote_ls_ctx *parent;
112};
113#endif
114
e388ab74 115static struct object_request *object_queue_head = NULL;
bc8f2652 116
182005b9
DB
117static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
118 void *data)
119{
bf0f910d 120 unsigned char expn[4096];
6eb7ed54
DB
121 size_t size = eltsize * nmemb;
122 int posn = 0;
e388ab74 123 struct object_request *obj_req = (struct object_request *)data;
6eb7ed54 124 do {
e388ab74 125 ssize_t retval = write(obj_req->local,
1d7f171c 126 (char *) ptr + posn, size - posn);
6eb7ed54
DB
127 if (retval < 0)
128 return posn;
129 posn += retval;
130 } while (posn < size);
131
e388ab74
NH
132 obj_req->stream.avail_in = size;
133 obj_req->stream.next_in = ptr;
6eb7ed54 134 do {
e388ab74
NH
135 obj_req->stream.next_out = expn;
136 obj_req->stream.avail_out = sizeof(expn);
137 obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
138 SHA1_Update(&obj_req->c, expn,
139 sizeof(expn) - obj_req->stream.avail_out);
140 } while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
1d389ab6 141 data_received++;
6eb7ed54
DB
142 return size;
143}
144
acc075a8 145static void fetch_alternates(char *base);
1d389ab6 146
29508e1e 147static void process_object_response(void *callback_data);
1d389ab6 148
e388ab74 149static void start_object_request(struct object_request *obj_req)
1d389ab6 150{
e388ab74 151 char *hex = sha1_to_hex(obj_req->sha1);
1d389ab6
NH
152 char prevfile[PATH_MAX];
153 char *url;
154 char *posn;
155 int prevlocal;
156 unsigned char prev_buf[PREV_BUF_SIZE];
157 ssize_t prev_read = 0;
158 long prev_posn = 0;
159 char range[RANGE_HEADER_SIZE];
160 struct curl_slist *range_header = NULL;
161 struct active_request_slot *slot;
162
e388ab74 163 snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
1d389ab6 164 unlink(prevfile);
e388ab74
NH
165 rename(obj_req->tmpfile, prevfile);
166 unlink(obj_req->tmpfile);
1d389ab6 167
e388ab74
NH
168 if (obj_req->local != -1)
169 error("fd leakage in start: %d", obj_req->local);
170 obj_req->local = open(obj_req->tmpfile,
1d389ab6 171 O_WRONLY | O_CREAT | O_EXCL, 0666);
b721e01f
JH
172 /* This could have failed due to the "lazy directory creation";
173 * try to mkdir the last path component.
174 */
e388ab74
NH
175 if (obj_req->local < 0 && errno == ENOENT) {
176 char *dir = strrchr(obj_req->tmpfile, '/');
b721e01f
JH
177 if (dir) {
178 *dir = 0;
e388ab74 179 mkdir(obj_req->tmpfile, 0777);
b721e01f
JH
180 *dir = '/';
181 }
e388ab74 182 obj_req->local = open(obj_req->tmpfile,
b721e01f
JH
183 O_WRONLY | O_CREAT | O_EXCL, 0666);
184 }
185
e388ab74
NH
186 if (obj_req->local < 0) {
187 obj_req->state = ABORTED;
bd2afde8 188 error("Couldn't create temporary file %s for %s: %s",
e388ab74 189 obj_req->tmpfile, obj_req->filename, strerror(errno));
1d389ab6
NH
190 return;
191 }
192
e388ab74 193 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
1d389ab6 194
e388ab74 195 inflateInit(&obj_req->stream);
1d389ab6 196
e388ab74 197 SHA1_Init(&obj_req->c);
1d389ab6 198
e388ab74
NH
199 url = xmalloc(strlen(obj_req->repo->base) + 50);
200 obj_req->url = xmalloc(strlen(obj_req->repo->base) + 50);
201 strcpy(url, obj_req->repo->base);
202 posn = url + strlen(obj_req->repo->base);
1d389ab6
NH
203 strcpy(posn, "objects/");
204 posn += 8;
205 memcpy(posn, hex, 2);
206 posn += 2;
207 *(posn++) = '/';
208 strcpy(posn, hex + 2);
e388ab74 209 strcpy(obj_req->url, url);
1d389ab6
NH
210
211 /* If a previous temp file is present, process what was already
212 fetched. */
213 prevlocal = open(prevfile, O_RDONLY);
214 if (prevlocal != -1) {
215 do {
216 prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
217 if (prev_read>0) {
218 if (fwrite_sha1_file(prev_buf,
219 1,
220 prev_read,
e388ab74 221 obj_req) == prev_read) {
1d389ab6
NH
222 prev_posn += prev_read;
223 } else {
224 prev_read = -1;
225 }
226 }
227 } while (prev_read > 0);
228 close(prevlocal);
229 }
230 unlink(prevfile);
231
232 /* Reset inflate/SHA1 if there was an error reading the previous temp
233 file; also rewind to the beginning of the local file. */
234 if (prev_read == -1) {
e388ab74
NH
235 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
236 inflateInit(&obj_req->stream);
237 SHA1_Init(&obj_req->c);
1d389ab6
NH
238 if (prev_posn>0) {
239 prev_posn = 0;
e388ab74
NH
240 lseek(obj_req->local, SEEK_SET, 0);
241 ftruncate(obj_req->local, 0);
1d389ab6
NH
242 }
243 }
244
245 slot = get_active_slot();
29508e1e 246 slot->callback_func = process_object_response;
e388ab74
NH
247 slot->callback_data = obj_req;
248 obj_req->slot = slot;
29508e1e 249
e388ab74 250 curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req);
1d389ab6 251 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
e388ab74 252 curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr);
1d389ab6 253 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
03126006 254 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1d389ab6
NH
255
256 /* If we have successfully processed data from a previous fetch
257 attempt, only fetch the data we don't already have. */
258 if (prev_posn>0) {
259 if (get_verbosely)
260 fprintf(stderr,
261 "Resuming fetch of object %s at byte %ld\n",
262 hex, prev_posn);
263 sprintf(range, "Range: bytes=%ld-", prev_posn);
264 range_header = curl_slist_append(range_header, range);
265 curl_easy_setopt(slot->curl,
266 CURLOPT_HTTPHEADER, range_header);
267 }
268
a7a8d378 269 /* Try to get the request started, abort the request on error */
e388ab74 270 obj_req->state = ACTIVE;
1d389ab6 271 if (!start_active_slot(slot)) {
e388ab74
NH
272 obj_req->state = ABORTED;
273 obj_req->slot = NULL;
274 close(obj_req->local); obj_req->local = -1;
275 free(obj_req->url);
276 return;
1d389ab6 277 }
1d389ab6
NH
278}
279
e388ab74 280static void finish_object_request(struct object_request *obj_req)
1d389ab6 281{
50496b21
NH
282 struct stat st;
283
e388ab74
NH
284 fchmod(obj_req->local, 0444);
285 close(obj_req->local); obj_req->local = -1;
1d389ab6 286
e388ab74 287 if (obj_req->http_code == 416) {
1d389ab6 288 fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
e388ab74
NH
289 } else if (obj_req->curl_result != CURLE_OK) {
290 if (stat(obj_req->tmpfile, &st) == 0)
50496b21 291 if (st.st_size == 0)
e388ab74 292 unlink(obj_req->tmpfile);
1d389ab6
NH
293 return;
294 }
295
e388ab74
NH
296 inflateEnd(&obj_req->stream);
297 SHA1_Final(obj_req->real_sha1, &obj_req->c);
298 if (obj_req->zret != Z_STREAM_END) {
299 unlink(obj_req->tmpfile);
1d389ab6
NH
300 return;
301 }
e388ab74
NH
302 if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
303 unlink(obj_req->tmpfile);
1d389ab6
NH
304 return;
305 }
e388ab74
NH
306 obj_req->rename =
307 move_temp_to_file(obj_req->tmpfile, obj_req->filename);
1d389ab6 308
e388ab74
NH
309 if (obj_req->rename == 0)
310 pull_say("got %s\n", sha1_to_hex(obj_req->sha1));
1d389ab6
NH
311}
312
29508e1e
NH
313static void process_object_response(void *callback_data)
314{
e388ab74
NH
315 struct object_request *obj_req =
316 (struct object_request *)callback_data;
29508e1e 317
e388ab74
NH
318 obj_req->curl_result = obj_req->slot->curl_result;
319 obj_req->http_code = obj_req->slot->http_code;
320 obj_req->slot = NULL;
321 obj_req->state = COMPLETE;
29508e1e
NH
322
323 /* Use alternates if necessary */
a14c2256
NH
324 if (obj_req->http_code == 404 ||
325 obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE) {
29508e1e 326 fetch_alternates(alt->base);
e388ab74
NH
327 if (obj_req->repo->next != NULL) {
328 obj_req->repo =
329 obj_req->repo->next;
330 close(obj_req->local);
331 obj_req->local = -1;
332 start_object_request(obj_req);
29508e1e
NH
333 return;
334 }
335 }
336
e388ab74 337 finish_object_request(obj_req);
29508e1e
NH
338}
339
e388ab74 340static void release_object_request(struct object_request *obj_req)
1d389ab6 341{
e388ab74 342 struct object_request *entry = object_queue_head;
1d389ab6 343
e388ab74
NH
344 if (obj_req->local != -1)
345 error("fd leakage in release: %d", obj_req->local);
346 if (obj_req == object_queue_head) {
347 object_queue_head = obj_req->next;
1d389ab6 348 } else {
e388ab74 349 while (entry->next != NULL && entry->next != obj_req)
1d389ab6 350 entry = entry->next;
e388ab74 351 if (entry->next == obj_req)
1d389ab6
NH
352 entry->next = entry->next->next;
353 }
354
e388ab74
NH
355 free(obj_req->url);
356 free(obj_req);
1d389ab6
NH
357}
358
a7a8d378 359#ifdef USE_CURL_MULTI
29508e1e 360void fill_active_slots(void)
1d389ab6 361{
e388ab74 362 struct object_request *obj_req = object_queue_head;
f1a906a3 363 struct active_request_slot *slot = active_queue_head;
1d389ab6
NH
364 int num_transfers;
365
e388ab74
NH
366 while (active_requests < max_requests && obj_req != NULL) {
367 if (obj_req->state == WAITING) {
368 if (has_sha1_file(obj_req->sha1))
09db444f 369 obj_req->state = COMPLETE;
11f0dafe 370 else
e388ab74 371 start_object_request(obj_req);
1d389ab6
NH
372 curl_multi_perform(curlm, &num_transfers);
373 }
e388ab74 374 obj_req = obj_req->next;
1d389ab6 375 }
f1a906a3
NH
376
377 while (slot != NULL) {
378 if (!slot->in_use && slot->curl != NULL) {
379 curl_easy_cleanup(slot->curl);
380 slot->curl = NULL;
381 }
382 slot = slot->next;
8fcf7f9a 383 }
1d389ab6 384}
a7a8d378 385#endif
1d389ab6
NH
386
387void prefetch(unsigned char *sha1)
388{
e388ab74
NH
389 struct object_request *newreq;
390 struct object_request *tail;
1d389ab6
NH
391 char *filename = sha1_file_name(sha1);
392
393 newreq = xmalloc(sizeof(*newreq));
394 memcpy(newreq->sha1, sha1, 20);
395 newreq->repo = alt;
396 newreq->url = NULL;
397 newreq->local = -1;
398 newreq->state = WAITING;
399 snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
400 snprintf(newreq->tmpfile, sizeof(newreq->tmpfile),
401 "%s.temp", filename);
e8dff6ba 402 newreq->slot = NULL;
1d389ab6
NH
403 newreq->next = NULL;
404
e388ab74
NH
405 if (object_queue_head == NULL) {
406 object_queue_head = newreq;
1d389ab6 407 } else {
e388ab74 408 tail = object_queue_head;
1d389ab6
NH
409 while (tail->next != NULL) {
410 tail = tail->next;
411 }
412 tail->next = newreq;
413 }
29508e1e 414
a7a8d378 415#ifdef USE_CURL_MULTI
29508e1e
NH
416 fill_active_slots();
417 step_active_slots();
a7a8d378 418#endif
1d389ab6
NH
419}
420
b3661567 421static int fetch_index(struct alt_base *repo, unsigned char *sha1)
182005b9 422{
1d389ab6 423 char *hex = sha1_to_hex(sha1);
182005b9
DB
424 char *filename;
425 char *url;
49a0f240 426 char tmpfile[PATH_MAX];
49a0f240
NH
427 long prev_posn = 0;
428 char range[RANGE_HEADER_SIZE];
429 struct curl_slist *range_header = NULL;
182005b9
DB
430
431 FILE *indexfile;
1d389ab6 432 struct active_request_slot *slot;
cb754fdf 433 struct slot_results results;
182005b9
DB
434
435 if (has_pack_index(sha1))
436 return 0;
437
438 if (get_verbosely)
1d389ab6 439 fprintf(stderr, "Getting index for pack %s\n", hex);
8fcf7f9a 440
b3661567 441 url = xmalloc(strlen(repo->base) + 64);
1d389ab6 442 sprintf(url, "%s/objects/pack/pack-%s.idx", repo->base, hex);
8fcf7f9a 443
182005b9 444 filename = sha1_pack_index_name(sha1);
49a0f240
NH
445 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
446 indexfile = fopen(tmpfile, "a");
182005b9
DB
447 if (!indexfile)
448 return error("Unable to open local file %s for pack index",
449 filename);
450
1d389ab6 451 slot = get_active_slot();
c8568e13 452 slot->results = &results;
1d389ab6
NH
453 curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
454 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
455 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
03126006 456 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1d389ab6
NH
457 slot->local = indexfile;
458
49a0f240
NH
459 /* If there is data present from a previous transfer attempt,
460 resume where it left off */
461 prev_posn = ftell(indexfile);
462 if (prev_posn>0) {
463 if (get_verbosely)
464 fprintf(stderr,
465 "Resuming fetch of index for pack %s at byte %ld\n",
1d389ab6 466 hex, prev_posn);
49a0f240
NH
467 sprintf(range, "Range: bytes=%ld-", prev_posn);
468 range_header = curl_slist_append(range_header, range);
1d389ab6 469 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
49a0f240
NH
470 }
471
1d389ab6
NH
472 if (start_active_slot(slot)) {
473 run_active_slot(slot);
c8568e13 474 if (results.curl_result != CURLE_OK) {
1d389ab6
NH
475 fclose(indexfile);
476 return error("Unable to get pack index %s\n%s", url,
477 curl_errorstr);
478 }
479 } else {
313c4714 480 fclose(indexfile);
1d389ab6 481 return error("Unable to start request");
182005b9
DB
482 }
483
484 fclose(indexfile);
49a0f240 485
b721e01f 486 return move_temp_to_file(tmpfile, filename);
182005b9
DB
487}
488
b3661567 489static int setup_index(struct alt_base *repo, unsigned char *sha1)
182005b9
DB
490{
491 struct packed_git *new_pack;
492 if (has_pack_file(sha1))
a9486b02 493 return 0; /* don't list this as something we can get */
182005b9 494
b3661567 495 if (fetch_index(repo, sha1))
182005b9
DB
496 return -1;
497
498 new_pack = parse_pack_index(sha1);
b3661567
DB
499 new_pack->next = repo->packs;
500 repo->packs = new_pack;
182005b9
DB
501 return 0;
502}
503
e388ab74 504static void process_alternates_response(void *callback_data)
b3661567 505{
e388ab74
NH
506 struct alternates_request *alt_req =
507 (struct alternates_request *)callback_data;
acc075a8 508 struct active_request_slot *slot = alt_req->slot;
1d389ab6 509 struct alt_base *tail = alt;
acc075a8 510 char *base = alt_req->base;
bc8f2652 511 static const char null_byte = '\0';
acc075a8
NH
512 char *data;
513 int i = 0;
1d389ab6 514
acc075a8
NH
515 if (alt_req->http_specific) {
516 if (slot->curl_result != CURLE_OK ||
517 !alt_req->buffer->posn) {
518
519 /* Try reusing the slot to get non-http alternates */
520 alt_req->http_specific = 0;
521 sprintf(alt_req->url, "%s/objects/info/alternates",
522 base);
523 curl_easy_setopt(slot->curl, CURLOPT_URL,
524 alt_req->url);
525 active_requests++;
526 slot->in_use = 1;
c9826473
NH
527 if (slot->finished != NULL)
528 (*slot->finished) = 0;
a3f583cb 529 if (!start_active_slot(slot)) {
acc075a8 530 got_alternates = -1;
29508e1e 531 slot->in_use = 0;
c9826473
NH
532 if (slot->finished != NULL)
533 (*slot->finished) = 1;
1d389ab6 534 }
a3f583cb 535 return;
b3661567 536 }
acc075a8 537 } else if (slot->curl_result != CURLE_OK) {
a14c2256
NH
538 if (slot->http_code != 404 &&
539 slot->curl_result != CURLE_FILE_COULDNT_READ_FILE) {
acc075a8
NH
540 got_alternates = -1;
541 return;
542 }
b3661567
DB
543 }
544
29508e1e 545 fwrite_buffer(&null_byte, 1, 1, alt_req->buffer);
acc075a8
NH
546 alt_req->buffer->posn--;
547 data = alt_req->buffer->buffer;
1b0c1e67 548
acc075a8 549 while (i < alt_req->buffer->posn) {
b3661567 550 int posn = i;
acc075a8 551 while (posn < alt_req->buffer->posn && data[posn] != '\n')
b3661567
DB
552 posn++;
553 if (data[posn] == '\n') {
1b0c1e67
DB
554 int okay = 0;
555 int serverlen = 0;
556 struct alt_base *newalt;
557 char *target = NULL;
8d9fbe57 558 char *path;
b3661567 559 if (data[i] == '/') {
1b0c1e67
DB
560 serverlen = strchr(base + 8, '/') - base;
561 okay = 1;
562 } else if (!memcmp(data + i, "../", 3)) {
563 i += 3;
564 serverlen = strlen(base);
8fcf7f9a 565 while (i + 2 < posn &&
1b0c1e67
DB
566 !memcmp(data + i, "../", 3)) {
567 do {
568 serverlen--;
569 } while (serverlen &&
570 base[serverlen - 1] != '/');
571 i += 3;
572 }
a9486b02 573 /* If the server got removed, give up. */
8fcf7f9a 574 okay = strchr(base, ':') - base + 3 <
1b0c1e67 575 serverlen;
acc075a8 576 } else if (alt_req->http_specific) {
1b0c1e67
DB
577 char *colon = strchr(data + i, ':');
578 char *slash = strchr(data + i, '/');
579 if (colon && slash && colon < data + posn &&
580 slash < data + posn && colon < slash) {
581 okay = 1;
582 }
583 }
a9486b02 584 /* skip 'objects' at end */
1b0c1e67
DB
585 if (okay) {
586 target = xmalloc(serverlen + posn - i - 6);
817151e6
PE
587 strlcpy(target, base, serverlen);
588 strlcpy(target + serverlen, data + i, posn - i - 6);
b3661567 589 if (get_verbosely)
8fcf7f9a 590 fprintf(stderr,
b3661567
DB
591 "Also look at %s\n", target);
592 newalt = xmalloc(sizeof(*newalt));
1d389ab6 593 newalt->next = NULL;
b3661567
DB
594 newalt->base = target;
595 newalt->got_indices = 0;
596 newalt->packs = NULL;
8d9fbe57
NH
597 path = strstr(target, "//");
598 if (path) {
ef9e58c8 599 path = strchr(path+2, '/');
8d9fbe57
NH
600 if (path)
601 newalt->path_len = strlen(path);
602 }
603
1d389ab6
NH
604 while (tail->next != NULL)
605 tail = tail->next;
606 tail->next = newalt;
b3661567
DB
607 }
608 }
609 i = posn + 1;
610 }
bc8f2652 611
f7eb290f 612 got_alternates = 1;
acc075a8
NH
613}
614
615static void fetch_alternates(char *base)
616{
617 struct buffer buffer;
618 char *url;
619 char *data;
620 struct active_request_slot *slot;
cb754fdf 621 struct alternates_request alt_req;
acc075a8
NH
622
623 /* If another request has already started fetching alternates,
624 wait for them to arrive and return to processing this request's
625 curl message */
29508e1e 626#ifdef USE_CURL_MULTI
acc075a8 627 while (got_alternates == 0) {
29508e1e 628 step_active_slots();
acc075a8 629 }
29508e1e 630#endif
acc075a8
NH
631
632 /* Nothing to do if they've already been fetched */
633 if (got_alternates == 1)
634 return;
635
636 /* Start the fetch */
637 got_alternates = 0;
638
639 data = xmalloc(4096);
640 buffer.size = 4096;
641 buffer.posn = 0;
642 buffer.buffer = data;
643
644 if (get_verbosely)
645 fprintf(stderr, "Getting alternates list for %s\n", base);
8fcf7f9a 646
acc075a8
NH
647 url = xmalloc(strlen(base) + 31);
648 sprintf(url, "%s/objects/info/http-alternates", base);
649
650 /* Use a callback to process the result, since another request
651 may fail and need to have alternates loaded before continuing */
652 slot = get_active_slot();
e388ab74 653 slot->callback_func = process_alternates_response;
acc075a8
NH
654 slot->callback_data = &alt_req;
655
656 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
29508e1e 657 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
acc075a8
NH
658 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
659
660 alt_req.base = base;
661 alt_req.url = url;
662 alt_req.buffer = &buffer;
663 alt_req.http_specific = 1;
664 alt_req.slot = slot;
665
666 if (start_active_slot(slot))
667 run_active_slot(slot);
668 else
669 got_alternates = -1;
670
671 free(data);
672 free(url);
b3661567
DB
673}
674
8d9fbe57
NH
675#ifndef NO_EXPAT
676static void
677xml_start_tag(void *userData, const char *name, const char **atts)
678{
679 struct xml_ctx *ctx = (struct xml_ctx *)userData;
ef9e58c8 680 const char *c = strchr(name, ':');
8d9fbe57
NH
681 int new_len;
682
683 if (c == NULL)
684 c = name;
685 else
686 c++;
687
688 new_len = strlen(ctx->name) + strlen(c) + 2;
689
690 if (new_len > ctx->len) {
691 ctx->name = xrealloc(ctx->name, new_len);
692 ctx->len = new_len;
693 }
694 strcat(ctx->name, ".");
695 strcat(ctx->name, c);
696
697 if (ctx->cdata) {
698 free(ctx->cdata);
699 ctx->cdata = NULL;
700 }
701
702 ctx->userFunc(ctx, 0);
703}
704
705static void
706xml_end_tag(void *userData, const char *name)
707{
708 struct xml_ctx *ctx = (struct xml_ctx *)userData;
ef9e58c8 709 const char *c = strchr(name, ':');
8d9fbe57
NH
710 char *ep;
711
712 ctx->userFunc(ctx, 1);
713
714 if (c == NULL)
715 c = name;
716 else
717 c++;
718
719 ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
720 *ep = 0;
721}
722
723static void
724xml_cdata(void *userData, const XML_Char *s, int len)
725{
726 struct xml_ctx *ctx = (struct xml_ctx *)userData;
727 if (ctx->cdata)
728 free(ctx->cdata);
bfbd0bb6 729 ctx->cdata = xmalloc(len + 1);
817151e6 730 strlcpy(ctx->cdata, s, len + 1);
8d9fbe57
NH
731}
732
733static int remote_ls(struct alt_base *repo, const char *path, int flags,
734 void (*userFunc)(struct remote_ls_ctx *ls),
735 void *userData);
736
737static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
738{
739 struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;
740
741 if (tag_closed) {
742 if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
743 if (ls->dentry_flags & IS_DIR) {
744 if (ls->flags & PROCESS_DIRS) {
745 ls->userFunc(ls);
746 }
747 if (strcmp(ls->dentry_name, ls->path) &&
748 ls->flags & RECURSIVE) {
749 ls->rc = remote_ls(ls->repo,
750 ls->dentry_name,
751 ls->flags,
752 ls->userFunc,
753 ls->userData);
754 }
755 } else if (ls->flags & PROCESS_FILES) {
756 ls->userFunc(ls);
757 }
758 } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
759 ls->dentry_name = xmalloc(strlen(ctx->cdata) -
760 ls->repo->path_len + 1);
761 strcpy(ls->dentry_name, ctx->cdata + ls->repo->path_len);
762 } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
763 ls->dentry_flags |= IS_DIR;
764 }
765 } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
766 if (ls->dentry_name) {
767 free(ls->dentry_name);
768 }
769 ls->dentry_name = NULL;
770 ls->dentry_flags = 0;
771 }
772}
773
774static int remote_ls(struct alt_base *repo, const char *path, int flags,
775 void (*userFunc)(struct remote_ls_ctx *ls),
776 void *userData)
777{
778 char *url = xmalloc(strlen(repo->base) + strlen(path) + 1);
779 struct active_request_slot *slot;
780 struct slot_results results;
781 struct buffer in_buffer;
782 struct buffer out_buffer;
783 char *in_data;
784 char *out_data;
785 XML_Parser parser = XML_ParserCreate(NULL);
786 enum XML_Status result;
787 struct curl_slist *dav_headers = NULL;
788 struct xml_ctx ctx;
789 struct remote_ls_ctx ls;
790
791 ls.flags = flags;
792 ls.repo = repo;
793 ls.path = strdup(path);
794 ls.dentry_name = NULL;
795 ls.dentry_flags = 0;
796 ls.userData = userData;
797 ls.userFunc = userFunc;
798 ls.rc = 0;
799
800 sprintf(url, "%s%s", repo->base, path);
801
802 out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
803 out_data = xmalloc(out_buffer.size + 1);
804 snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
805 out_buffer.posn = 0;
806 out_buffer.buffer = out_data;
807
808 in_buffer.size = 4096;
809 in_data = xmalloc(in_buffer.size);
810 in_buffer.posn = 0;
811 in_buffer.buffer = in_data;
812
813 dav_headers = curl_slist_append(dav_headers, "Depth: 1");
814 dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
815
816 slot = get_active_slot();
817 slot->results = &results;
818 curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
819 curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
820 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
821 curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
822 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
823 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
824 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
825 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
826 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
827
828 if (start_active_slot(slot)) {
829 run_active_slot(slot);
830 if (results.curl_result == CURLE_OK) {
831 ctx.name = xcalloc(10, 1);
832 ctx.len = 0;
833 ctx.cdata = NULL;
834 ctx.userFunc = handle_remote_ls_ctx;
835 ctx.userData = &ls;
836 XML_SetUserData(parser, &ctx);
837 XML_SetElementHandler(parser, xml_start_tag,
838 xml_end_tag);
839 XML_SetCharacterDataHandler(parser, xml_cdata);
840 result = XML_Parse(parser, in_buffer.buffer,
841 in_buffer.posn, 1);
842 free(ctx.name);
843
844 if (result != XML_STATUS_OK) {
845 ls.rc = error("XML error: %s",
846 XML_ErrorString(
847 XML_GetErrorCode(parser)));
848 }
849 } else {
850 ls.rc = -1;
851 }
852 } else {
853 ls.rc = error("Unable to start PROPFIND request");
854 }
855
856 free(ls.path);
857 free(url);
858 free(out_data);
859 free(in_buffer.buffer);
860 curl_slist_free_all(dav_headers);
861
862 return ls.rc;
863}
864
865static void process_ls_pack(struct remote_ls_ctx *ls)
866{
867 unsigned char sha1[20];
868
869 if (strlen(ls->dentry_name) == 63 &&
870 !strncmp(ls->dentry_name, "objects/pack/pack-", 18) &&
871 !strncmp(ls->dentry_name+58, ".pack", 5)) {
872 get_sha1_hex(ls->dentry_name + 18, sha1);
873 setup_index(ls->repo, sha1);
874 }
875}
876#endif
877
b3661567 878static int fetch_indices(struct alt_base *repo)
182005b9
DB
879{
880 unsigned char sha1[20];
881 char *url;
882 struct buffer buffer;
883 char *data;
884 int i = 0;
885
1d389ab6 886 struct active_request_slot *slot;
cb754fdf 887 struct slot_results results;
1d389ab6 888
b3661567 889 if (repo->got_indices)
182005b9
DB
890 return 0;
891
892 data = xmalloc(4096);
893 buffer.size = 4096;
894 buffer.posn = 0;
895 buffer.buffer = data;
896
897 if (get_verbosely)
6fd72e39 898 fprintf(stderr, "Getting pack list for %s\n", repo->base);
8fcf7f9a 899
8d9fbe57
NH
900#ifndef NO_EXPAT
901 if (remote_ls(repo, "objects/pack/", PROCESS_FILES,
902 process_ls_pack, NULL) == 0)
903 return 0;
904#endif
905
b3661567
DB
906 url = xmalloc(strlen(repo->base) + 21);
907 sprintf(url, "%s/objects/info/packs", repo->base);
182005b9 908
1d389ab6 909 slot = get_active_slot();
c8568e13 910 slot->results = &results;
1d389ab6 911 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
29508e1e 912 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
1d389ab6
NH
913 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
914 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
915 if (start_active_slot(slot)) {
916 run_active_slot(slot);
c8568e13
NH
917 if (results.curl_result != CURLE_OK) {
918 if (results.http_code == 404 ||
919 results.curl_result == CURLE_FILE_COULDNT_READ_FILE) {
5e3a7691
NH
920 repo->got_indices = 1;
921 free(buffer.buffer);
922 return 0;
923 } else {
924 repo->got_indices = 0;
925 free(buffer.buffer);
926 return error("%s", curl_errorstr);
927 }
bc8f2652 928 }
1d389ab6 929 } else {
5e3a7691 930 repo->got_indices = 0;
bc8f2652 931 free(buffer.buffer);
1d389ab6
NH
932 return error("Unable to start request");
933 }
182005b9 934
bc8f2652 935 data = buffer.buffer;
b3661567 936 while (i < buffer.posn) {
182005b9
DB
937 switch (data[i]) {
938 case 'P':
939 i++;
455c161c 940 if (i + 52 <= buffer.posn &&
182005b9
DB
941 !strncmp(data + i, " pack-", 6) &&
942 !strncmp(data + i + 46, ".pack\n", 6)) {
943 get_sha1_hex(data + i + 6, sha1);
b3661567 944 setup_index(repo, sha1);
182005b9
DB
945 i += 51;
946 break;
947 }
948 default:
455c161c 949 while (i < buffer.posn && data[i] != '\n')
182005b9
DB
950 i++;
951 }
952 i++;
b3661567 953 }
182005b9 954
bc8f2652 955 free(buffer.buffer);
b3661567 956 repo->got_indices = 1;
182005b9
DB
957 return 0;
958}
959
b3661567 960static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
182005b9
DB
961{
962 char *url;
963 struct packed_git *target;
964 struct packed_git **lst;
965 FILE *packfile;
966 char *filename;
49a0f240
NH
967 char tmpfile[PATH_MAX];
968 int ret;
969 long prev_posn = 0;
970 char range[RANGE_HEADER_SIZE];
971 struct curl_slist *range_header = NULL;
1d389ab6
NH
972
973 struct active_request_slot *slot;
cb754fdf 974 struct slot_results results;
182005b9 975
b3661567 976 if (fetch_indices(repo))
182005b9 977 return -1;
b3661567 978 target = find_sha1_pack(sha1, repo->packs);
182005b9 979 if (!target)
b3661567 980 return -1;
182005b9
DB
981
982 if (get_verbosely) {
983 fprintf(stderr, "Getting pack %s\n",
984 sha1_to_hex(target->sha1));
985 fprintf(stderr, " which contains %s\n",
986 sha1_to_hex(sha1));
987 }
988
b3661567 989 url = xmalloc(strlen(repo->base) + 65);
182005b9 990 sprintf(url, "%s/objects/pack/pack-%s.pack",
b3661567 991 repo->base, sha1_to_hex(target->sha1));
182005b9
DB
992
993 filename = sha1_pack_name(target->sha1);
49a0f240
NH
994 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
995 packfile = fopen(tmpfile, "a");
182005b9
DB
996 if (!packfile)
997 return error("Unable to open local file %s for pack",
998 filename);
999
1d389ab6 1000 slot = get_active_slot();
c8568e13 1001 slot->results = &results;
1d389ab6
NH
1002 curl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);
1003 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
1004 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
03126006 1005 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1d389ab6 1006 slot->local = packfile;
1ddea77e 1007
49a0f240
NH
1008 /* If there is data present from a previous transfer attempt,
1009 resume where it left off */
1010 prev_posn = ftell(packfile);
1011 if (prev_posn>0) {
1012 if (get_verbosely)
1013 fprintf(stderr,
1014 "Resuming fetch of pack %s at byte %ld\n",
1015 sha1_to_hex(target->sha1), prev_posn);
1016 sprintf(range, "Range: bytes=%ld-", prev_posn);
1017 range_header = curl_slist_append(range_header, range);
1d389ab6 1018 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
49a0f240
NH
1019 }
1020
1d389ab6
NH
1021 if (start_active_slot(slot)) {
1022 run_active_slot(slot);
c8568e13 1023 if (results.curl_result != CURLE_OK) {
1d389ab6
NH
1024 fclose(packfile);
1025 return error("Unable to get pack file %s\n%s", url,
1026 curl_errorstr);
1027 }
1028 } else {
313c4714 1029 fclose(packfile);
1d389ab6 1030 return error("Unable to start request");
182005b9
DB
1031 }
1032
1033 fclose(packfile);
1034
b721e01f 1035 ret = move_temp_to_file(tmpfile, filename);
49a0f240 1036 if (ret)
b721e01f 1037 return ret;
49a0f240 1038
b3661567 1039 lst = &repo->packs;
182005b9
DB
1040 while (*lst != target)
1041 lst = &((*lst)->next);
1042 *lst = (*lst)->next;
1043
271421cd
JH
1044 if (verify_pack(target, 0))
1045 return -1;
182005b9
DB
1046 install_packed_git(target);
1047
1048 return 0;
1049}
1050
53f31389
MW
1051static void abort_object_request(struct object_request *obj_req)
1052{
1053 if (obj_req->local >= 0) {
1054 close(obj_req->local);
1055 obj_req->local = -1;
1056 }
1057 unlink(obj_req->tmpfile);
1058 if (obj_req->slot) {
1059 release_active_slot(obj_req->slot);
1060 obj_req->slot = NULL;
1061 }
1062 release_object_request(obj_req);
1063}
1064
a7928f8e 1065static int fetch_object(struct alt_base *repo, unsigned char *sha1)
6eb7ed54
DB
1066{
1067 char *hex = sha1_to_hex(sha1);
29508e1e 1068 int ret = 0;
e388ab74 1069 struct object_request *obj_req = object_queue_head;
1d389ab6 1070
e388ab74
NH
1071 while (obj_req != NULL && memcmp(obj_req->sha1, sha1, 20))
1072 obj_req = obj_req->next;
1073 if (obj_req == NULL)
1d389ab6
NH
1074 return error("Couldn't find request for %s in the queue", hex);
1075
e388ab74 1076 if (has_sha1_file(obj_req->sha1)) {
53f31389 1077 abort_object_request(obj_req);
11f0dafe
NH
1078 return 0;
1079 }
1080
a7a8d378 1081#ifdef USE_CURL_MULTI
e388ab74 1082 while (obj_req->state == WAITING) {
29508e1e 1083 step_active_slots();
1d389ab6 1084 }
a7a8d378 1085#else
e388ab74 1086 start_object_request(obj_req);
a7a8d378 1087#endif
6eb7ed54 1088
e388ab74
NH
1089 while (obj_req->state == ACTIVE) {
1090 run_active_slot(obj_req->slot);
a7a8d378 1091 }
e388ab74
NH
1092 if (obj_req->local != -1) {
1093 close(obj_req->local); obj_req->local = -1;
313c4714 1094 }
6eb7ed54 1095
e388ab74 1096 if (obj_req->state == ABORTED) {
29508e1e 1097 ret = error("Request for %s aborted", hex);
e388ab74
NH
1098 } else if (obj_req->curl_result != CURLE_OK &&
1099 obj_req->http_code != 416) {
a14c2256
NH
1100 if (obj_req->http_code == 404 ||
1101 obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE)
e2029eb9
PB
1102 ret = -1; /* Be silent, it is probably in a pack. */
1103 else
1104 ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
e388ab74
NH
1105 obj_req->errorstr, obj_req->curl_result,
1106 obj_req->http_code, hex);
1107 } else if (obj_req->zret != Z_STREAM_END) {
bb528079 1108 corrupt_object_found++;
bd2afde8 1109 ret = error("File %s (%s) corrupt", hex, obj_req->url);
e388ab74 1110 } else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
bd2afde8 1111 ret = error("File %s has bad hash", hex);
e388ab74 1112 } else if (obj_req->rename < 0) {
7b934ec0
MW
1113 ret = error("unable to write sha1 filename %s",
1114 obj_req->filename);
6eb7ed54 1115 }
49a0f240 1116
e388ab74 1117 release_object_request(obj_req);
29508e1e 1118 return ret;
6eb7ed54
DB
1119}
1120
b3661567
DB
1121int fetch(unsigned char *sha1)
1122{
1123 struct alt_base *altbase = alt;
1d389ab6
NH
1124
1125 if (!fetch_object(altbase, sha1))
1126 return 0;
b3661567 1127 while (altbase) {
b3661567
DB
1128 if (!fetch_pack(altbase, sha1))
1129 return 0;
f7eb290f 1130 fetch_alternates(alt->base);
b3661567
DB
1131 altbase = altbase->next;
1132 }
bd2afde8 1133 return error("Unable to find %s under %s", sha1_to_hex(sha1),
1d389ab6 1134 alt->base);
b3661567
DB
1135}
1136
94fa447a
JH
1137static inline int needs_quote(int ch)
1138{
cfd432e6
FF
1139 if (((ch >= 'A') && (ch <= 'Z'))
1140 || ((ch >= 'a') && (ch <= 'z'))
1141 || ((ch >= '0') && (ch <= '9'))
1142 || (ch == '/')
1143 || (ch == '-')
1144 || (ch == '.'))
94fa447a 1145 return 0;
cfd432e6 1146 return 1;
94fa447a
JH
1147}
1148
1149static inline int hex(int v)
1150{
1151 if (v < 10) return '0' + v;
1152 else return 'A' + v - 10;
1153}
1154
1155static char *quote_ref_url(const char *base, const char *ref)
1156{
1157 const char *cp;
1158 char *dp, *qref;
1159 int len, baselen, ch;
1160
1161 baselen = strlen(base);
1162 len = baselen + 6; /* "refs/" + NUL */
1163 for (cp = ref; (ch = *cp) != 0; cp++, len++)
1164 if (needs_quote(ch))
1165 len += 2; /* extra two hex plus replacement % */
1166 qref = xmalloc(len);
1167 memcpy(qref, base, baselen);
1168 memcpy(qref + baselen, "refs/", 5);
1169 for (cp = ref, dp = qref + baselen + 5; (ch = *cp) != 0; cp++) {
1170 if (needs_quote(ch)) {
1171 *dp++ = '%';
1172 *dp++ = hex((ch >> 4) & 0xF);
1173 *dp++ = hex(ch & 0xF);
1174 }
1175 else
1176 *dp++ = ch;
1177 }
1178 *dp = 0;
1179
1180 return qref;
1181}
1182
cd541a68
DB
1183int fetch_ref(char *ref, unsigned char *sha1)
1184{
94fa447a 1185 char *url;
fa3e0655
DB
1186 char hex[42];
1187 struct buffer buffer;
1d389ab6
NH
1188 char *base = alt->base;
1189 struct active_request_slot *slot;
cb754fdf 1190 struct slot_results results;
fa3e0655
DB
1191 buffer.size = 41;
1192 buffer.posn = 0;
1193 buffer.buffer = hex;
1194 hex[41] = '\0';
8fcf7f9a 1195
94fa447a 1196 url = quote_ref_url(base, ref);
1d389ab6 1197 slot = get_active_slot();
c8568e13 1198 slot->results = &results;
1d389ab6
NH
1199 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
1200 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
1201 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
1202 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
1203 if (start_active_slot(slot)) {
1204 run_active_slot(slot);
c8568e13 1205 if (results.curl_result != CURLE_OK)
1d389ab6
NH
1206 return error("Couldn't get %s for %s\n%s",
1207 url, ref, curl_errorstr);
1208 } else {
1209 return error("Unable to start request");
1210 }
fa3e0655
DB
1211
1212 hex[40] = '\0';
1213 get_sha1_hex(hex, sha1);
1214 return 0;
cd541a68
DB
1215}
1216
6eb7ed54
DB
1217int main(int argc, char **argv)
1218{
c6b69bdb 1219 const char *write_ref = NULL;
6eb7ed54
DB
1220 char *commit_id;
1221 char *url;
8d9fbe57 1222 char *path;
6eb7ed54 1223 int arg = 1;
7b9ae53e 1224 int rc = 0;
6eb7ed54 1225
5a327713 1226 setup_git_directory();
d0740d92 1227 git_config(git_default_config);
5a327713 1228
6eb7ed54
DB
1229 while (arg < argc && argv[arg][0] == '-') {
1230 if (argv[arg][1] == 't') {
4250a5e5 1231 get_tree = 1;
6eb7ed54 1232 } else if (argv[arg][1] == 'c') {
4250a5e5 1233 get_history = 1;
6eb7ed54 1234 } else if (argv[arg][1] == 'a') {
4250a5e5
DB
1235 get_all = 1;
1236 get_tree = 1;
1237 get_history = 1;
e78d9772
JH
1238 } else if (argv[arg][1] == 'v') {
1239 get_verbosely = 1;
fa3e0655
DB
1240 } else if (argv[arg][1] == 'w') {
1241 write_ref = argv[arg + 1];
1242 arg++;
820eca68
DB
1243 } else if (!strcmp(argv[arg], "--recover")) {
1244 get_recover = 1;
6eb7ed54
DB
1245 }
1246 arg++;
1247 }
1248 if (argc < arg + 2) {
cc41cd2e 1249 usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] commit-id url");
6eb7ed54
DB
1250 return 1;
1251 }
1252 commit_id = argv[arg];
1253 url = argv[arg + 1];
1254
29508e1e 1255 http_init();
d402d556 1256
1db69b57 1257 no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
3dcb90f5 1258
b3661567
DB
1259 alt = xmalloc(sizeof(*alt));
1260 alt->base = url;
1261 alt->got_indices = 0;
1262 alt->packs = NULL;
1263 alt->next = NULL;
8d9fbe57
NH
1264 path = strstr(url, "//");
1265 if (path) {
ef9e58c8 1266 path = strchr(path+2, '/');
8d9fbe57
NH
1267 if (path)
1268 alt->path_len = strlen(path);
1269 }
6eb7ed54 1270
4211e4d1 1271 if (pull(1, &commit_id, &write_ref, url))
7b9ae53e 1272 rc = 1;
6eb7ed54 1273
29508e1e
NH
1274 http_cleanup();
1275
07001f95
SE
1276 curl_slist_free_all(no_pragma_header);
1277
bb528079
JH
1278 if (corrupt_object_found) {
1279 fprintf(stderr,
1280"Some loose object were found to be corrupt, but they might be just\n"
1281"a false '404 Not Found' error message sent with incorrect HTTP\n"
1282"status code. Suggest running git fsck-objects.\n");
1283 }
7b9ae53e 1284 return rc;
6eb7ed54 1285}