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