9 #if LIBCURL_VERSION_NUM < 0x070704
10 #define curl_global_cleanup() do { /* nothing */ } while(0)
12 #if LIBCURL_VERSION_NUM < 0x070800
13 #define curl_global_init(a) do { /* nothing */ } while(0)
17 static struct curl_slist
*no_pragma_header
;
18 static char curl_errorstr
[CURL_ERROR_SIZE
];
20 static char *initial_base
;
26 struct packed_git
*packs
;
27 struct alt_base
*next
;
30 static struct alt_base
*alt
= NULL
;
33 static z_stream stream
;
38 static int curl_ssl_verify
;
39 static char *ssl_cert
;
41 static char *ssl_capath
;
42 static char *ssl_cainfo
;
51 static size_t fwrite_buffer(void *ptr
, size_t eltsize
, size_t nmemb
,
52 struct buffer
*buffer
)
54 size_t size
= eltsize
* nmemb
;
55 if (size
> buffer
->size
- buffer
->posn
)
56 size
= buffer
->size
- buffer
->posn
;
57 memcpy(buffer
->buffer
+ buffer
->posn
, ptr
, size
);
62 static size_t fwrite_sha1_file(void *ptr
, size_t eltsize
, size_t nmemb
,
65 unsigned char expn
[4096];
66 size_t size
= eltsize
* nmemb
;
69 ssize_t retval
= write(local
, ptr
+ posn
, size
- posn
);
73 } while (posn
< size
);
75 stream
.avail_in
= size
;
78 stream
.next_out
= expn
;
79 stream
.avail_out
= sizeof(expn
);
80 zret
= inflate(&stream
, Z_SYNC_FLUSH
);
81 SHA1_Update(&c
, expn
, sizeof(expn
) - stream
.avail_out
);
82 } while (stream
.avail_in
&& zret
== Z_OK
);
86 void prefetch(unsigned char *sha1
)
90 static int got_alternates
= 0;
92 static int fetch_index(struct alt_base
*repo
, unsigned char *sha1
)
99 if (has_pack_index(sha1
))
103 fprintf(stderr
, "Getting index for pack %s\n",
106 url
= xmalloc(strlen(repo
->base
) + 64);
107 sprintf(url
, "%s/objects/pack/pack-%s.idx",
108 repo
->base
, sha1_to_hex(sha1
));
110 filename
= sha1_pack_index_name(sha1
);
111 indexfile
= fopen(filename
, "w");
113 return error("Unable to open local file %s for pack index",
116 curl_easy_setopt(curl
, CURLOPT_FILE
, indexfile
);
117 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite
);
118 curl_easy_setopt(curl
, CURLOPT_URL
, url
);
119 curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, no_pragma_header
);
120 curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, curl_errorstr
);
122 if (curl_easy_perform(curl
)) {
124 return error("Unable to get pack index %s\n%s", url
,
132 static int setup_index(struct alt_base
*repo
, unsigned char *sha1
)
134 struct packed_git
*new_pack
;
135 if (has_pack_file(sha1
))
136 return 0; // don't list this as something we can get
138 if (fetch_index(repo
, sha1
))
141 new_pack
= parse_pack_index(sha1
);
142 new_pack
->next
= repo
->packs
;
143 repo
->packs
= new_pack
;
147 static int fetch_alternates(char *base
)
150 struct buffer buffer
;
154 int http_specific
= 1;
157 data
= xmalloc(4096);
160 buffer
.buffer
= data
;
163 fprintf(stderr
, "Getting alternates list\n");
165 url
= xmalloc(strlen(base
) + 31);
166 sprintf(url
, "%s/objects/info/http-alternates", base
);
168 curl_easy_setopt(curl
, CURLOPT_FILE
, &buffer
);
169 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
170 curl_easy_setopt(curl
, CURLOPT_URL
, url
);
172 if (curl_easy_perform(curl
) || !buffer
.posn
) {
175 sprintf(url
, "%s/objects/info/alternates", base
);
177 curl_easy_setopt(curl
, CURLOPT_FILE
, &buffer
);
178 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
179 curl_easy_setopt(curl
, CURLOPT_URL
, url
);
181 if (curl_easy_perform(curl
)) {
186 data
[buffer
.posn
] = '\0';
188 while (i
< buffer
.posn
) {
190 while (posn
< buffer
.posn
&& data
[posn
] != '\n')
192 if (data
[posn
] == '\n') {
195 struct alt_base
*newalt
;
197 if (data
[i
] == '/') {
198 serverlen
= strchr(base
+ 8, '/') - base
;
200 } else if (!memcmp(data
+ i
, "../", 3)) {
202 serverlen
= strlen(base
);
203 while (i
+ 2 < posn
&&
204 !memcmp(data
+ i
, "../", 3)) {
207 } while (serverlen
&&
208 base
[serverlen
- 1] != '/');
211 // If the server got removed, give up.
212 okay
= strchr(base
, ':') - base
+ 3 <
214 } else if (http_specific
) {
215 char *colon
= strchr(data
+ i
, ':');
216 char *slash
= strchr(data
+ i
, '/');
217 if (colon
&& slash
&& colon
< data
+ posn
&&
218 slash
< data
+ posn
&& colon
< slash
) {
222 // skip 'objects' at end
224 target
= xmalloc(serverlen
+ posn
- i
- 6);
225 strncpy(target
, base
, serverlen
);
226 strncpy(target
+ serverlen
, data
+ i
,
228 target
[serverlen
+ posn
- i
- 7] = '\0';
231 "Also look at %s\n", target
);
232 newalt
= xmalloc(sizeof(*newalt
));
234 newalt
->base
= target
;
235 newalt
->got_indices
= 0;
236 newalt
->packs
= NULL
;
248 static int fetch_indices(struct alt_base
*repo
)
250 unsigned char sha1
[20];
252 struct buffer buffer
;
256 if (repo
->got_indices
)
259 data
= xmalloc(4096);
262 buffer
.buffer
= data
;
265 fprintf(stderr
, "Getting pack list\n");
267 url
= xmalloc(strlen(repo
->base
) + 21);
268 sprintf(url
, "%s/objects/info/packs", repo
->base
);
270 curl_easy_setopt(curl
, CURLOPT_FILE
, &buffer
);
271 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
272 curl_easy_setopt(curl
, CURLOPT_URL
, url
);
273 curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, NULL
);
274 curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, curl_errorstr
);
276 if (curl_easy_perform(curl
))
277 return error("%s", curl_errorstr
);
279 while (i
< buffer
.posn
) {
283 if (i
+ 52 < buffer
.posn
&&
284 !strncmp(data
+ i
, " pack-", 6) &&
285 !strncmp(data
+ i
+ 46, ".pack\n", 6)) {
286 get_sha1_hex(data
+ i
+ 6, sha1
);
287 setup_index(repo
, sha1
);
292 while (data
[i
] != '\n')
298 repo
->got_indices
= 1;
302 static int fetch_pack(struct alt_base
*repo
, unsigned char *sha1
)
305 struct packed_git
*target
;
306 struct packed_git
**lst
;
310 if (fetch_indices(repo
))
312 target
= find_sha1_pack(sha1
, repo
->packs
);
317 fprintf(stderr
, "Getting pack %s\n",
318 sha1_to_hex(target
->sha1
));
319 fprintf(stderr
, " which contains %s\n",
323 url
= xmalloc(strlen(repo
->base
) + 65);
324 sprintf(url
, "%s/objects/pack/pack-%s.pack",
325 repo
->base
, sha1_to_hex(target
->sha1
));
327 filename
= sha1_pack_name(target
->sha1
);
328 packfile
= fopen(filename
, "w");
330 return error("Unable to open local file %s for pack",
333 curl_easy_setopt(curl
, CURLOPT_FILE
, packfile
);
334 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite
);
335 curl_easy_setopt(curl
, CURLOPT_URL
, url
);
336 curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, no_pragma_header
);
337 curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, curl_errorstr
);
339 if (curl_easy_perform(curl
)) {
341 return error("Unable to get pack file %s\n%s", url
,
348 while (*lst
!= target
)
349 lst
= &((*lst
)->next
);
352 install_packed_git(target
);
357 static int fetch_object(struct alt_base
*repo
, unsigned char *sha1
)
359 char *hex
= sha1_to_hex(sha1
);
360 char *filename
= sha1_file_name(sha1
);
361 unsigned char real_sha1
[20];
362 char tmpfile
[PATH_MAX
];
367 snprintf(tmpfile
, sizeof(tmpfile
), "%s/obj_XXXXXX",
368 get_object_directory());
370 local
= mkstemp(tmpfile
);
372 return error("Couldn't create temporary file %s for %s: %s\n",
373 tmpfile
, filename
, strerror(errno
));
375 memset(&stream
, 0, sizeof(stream
));
377 inflateInit(&stream
);
381 curl_easy_setopt(curl
, CURLOPT_FAILONERROR
, 1);
382 curl_easy_setopt(curl
, CURLOPT_FILE
, NULL
);
383 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite_sha1_file
);
384 curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, no_pragma_header
);
385 curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, curl_errorstr
);
387 url
= xmalloc(strlen(repo
->base
) + 50);
388 strcpy(url
, repo
->base
);
389 posn
= url
+ strlen(repo
->base
);
390 strcpy(posn
, "objects/");
392 memcpy(posn
, hex
, 2);
395 strcpy(posn
, hex
+ 2);
397 curl_easy_setopt(curl
, CURLOPT_URL
, url
);
399 if (curl_easy_perform(curl
)) {
401 return error("%s", curl_errorstr
);
407 SHA1_Final(real_sha1
, &c
);
408 if (zret
!= Z_STREAM_END
) {
410 return error("File %s (%s) corrupt\n", hex
, url
);
412 if (memcmp(sha1
, real_sha1
, 20)) {
414 return error("File %s has bad hash\n", hex
);
416 ret
= link(tmpfile
, filename
);
418 /* Same Coda hack as in write_sha1_file(sha1_file.c) */
420 if (ret
== EXDEV
&& !rename(tmpfile
, filename
))
426 return error("unable to write sha1 filename %s: %s",
427 filename
, strerror(ret
));
430 pull_say("got %s\n", hex
);
434 int fetch(unsigned char *sha1
)
436 struct alt_base
*altbase
= alt
;
438 if (!fetch_object(altbase
, sha1
))
440 if (!fetch_pack(altbase
, sha1
))
442 if (fetch_alternates(altbase
->base
) > 0) {
446 altbase
= altbase
->next
;
448 return error("Unable to find %s under %s\n", sha1_to_hex(sha1
),
452 int fetch_ref(char *ref
, unsigned char *sha1
)
456 struct buffer buffer
;
457 char *base
= initial_base
;
463 curl_easy_setopt(curl
, CURLOPT_FILE
, &buffer
);
464 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
465 curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, NULL
);
466 curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, curl_errorstr
);
468 url
= xmalloc(strlen(base
) + 6 + strlen(ref
));
470 posn
= url
+ strlen(base
);
471 strcpy(posn
, "refs/");
475 curl_easy_setopt(curl
, CURLOPT_URL
, url
);
477 if (curl_easy_perform(curl
))
478 return error("Couldn't get %s for %s\n%s",
479 url
, ref
, curl_errorstr
);
482 get_sha1_hex(hex
, sha1
);
486 int main(int argc
, char **argv
)
492 while (arg
< argc
&& argv
[arg
][0] == '-') {
493 if (argv
[arg
][1] == 't') {
495 } else if (argv
[arg
][1] == 'c') {
497 } else if (argv
[arg
][1] == 'a') {
501 } else if (argv
[arg
][1] == 'v') {
503 } else if (argv
[arg
][1] == 'w') {
504 write_ref
= argv
[arg
+ 1];
506 } else if (!strcmp(argv
[arg
], "--recover")) {
511 if (argc
< arg
+ 2) {
512 usage("git-http-fetch [-c] [-t] [-a] [-d] [-v] [--recover] [-w ref] commit-id url");
515 commit_id
= argv
[arg
];
518 curl_global_init(CURL_GLOBAL_ALL
);
520 curl
= curl_easy_init();
521 no_pragma_header
= curl_slist_append(no_pragma_header
, "Pragma:");
523 curl_ssl_verify
= getenv("GIT_SSL_NO_VERIFY") ? 0 : 1;
524 curl_easy_setopt(curl
, CURLOPT_SSL_VERIFYPEER
, curl_ssl_verify
);
525 #if LIBCURL_VERSION_NUM >= 0x070907
526 curl_easy_setopt(curl
, CURLOPT_NETRC
, CURL_NETRC_OPTIONAL
);
529 if ((ssl_cert
= getenv("GIT_SSL_CERT")) != NULL
) {
530 curl_easy_setopt(curl
, CURLOPT_SSLCERT
, ssl_cert
);
532 if ((ssl_key
= getenv("GIT_SSL_KEY")) != NULL
) {
533 curl_easy_setopt(curl
, CURLOPT_SSLKEY
, ssl_key
);
535 #if LIBCURL_VERSION_NUM >= 0x070908
536 if ((ssl_capath
= getenv("GIT_SSL_CAPATH")) != NULL
) {
537 curl_easy_setopt(curl
, CURLOPT_CAPATH
, ssl_capath
);
540 if ((ssl_cainfo
= getenv("GIT_SSL_CAINFO")) != NULL
) {
541 curl_easy_setopt(curl
, CURLOPT_CAINFO
, ssl_cainfo
);
544 alt
= xmalloc(sizeof(*alt
));
546 alt
->got_indices
= 0;
554 curl_slist_free_all(no_pragma_header
);
555 curl_global_cleanup();