]> git.proxmox.com Git - libgit2.git/blob - tests/online/clone.c
Release 1.0.0+dfsg.1-2 to Debian experimental.
[libgit2.git] / tests / online / clone.c
1 #include "clar_libgit2.h"
2
3 #include "git2/clone.h"
4 #include "git2/cred_helpers.h"
5 #include "remote.h"
6 #include "futils.h"
7 #include "refs.h"
8
9 #define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
10 #define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
11 #define BB_REPO_URL "https://libgit3@bitbucket.org/libgit2/testgitrepository.git"
12 #define BB_REPO_URL_WITH_PASS "https://libgit3:libgit3@bitbucket.org/libgit2/testgitrepository.git"
13 #define BB_REPO_URL_WITH_WRONG_PASS "https://libgit3:wrong@bitbucket.org/libgit2/testgitrepository.git"
14
15 #define SSH_REPO_URL "ssh://github.com/libgit2/TestGitRepository"
16
17 static git_repository *g_repo;
18 static git_clone_options g_options;
19
20 static char *_remote_url = NULL;
21 static char *_remote_user = NULL;
22 static char *_remote_pass = NULL;
23 static char *_remote_sslnoverify = NULL;
24 static char *_remote_ssh_pubkey = NULL;
25 static char *_remote_ssh_privkey = NULL;
26 static char *_remote_ssh_passphrase = NULL;
27 static char *_remote_ssh_fingerprint = NULL;
28 static char *_remote_proxy_scheme = NULL;
29 static char *_remote_proxy_host = NULL;
30 static char *_remote_proxy_user = NULL;
31 static char *_remote_proxy_pass = NULL;
32 static char *_remote_proxy_selfsigned = NULL;
33 static char *_remote_expectcontinue = NULL;
34
35 static int _orig_proxies_need_reset = 0;
36 static char *_orig_http_proxy = NULL;
37 static char *_orig_https_proxy = NULL;
38
39 static int ssl_cert(git_cert *cert, int valid, const char *host, void *payload)
40 {
41 GIT_UNUSED(cert);
42 GIT_UNUSED(host);
43 GIT_UNUSED(payload);
44
45 if (_remote_sslnoverify != NULL)
46 valid = 1;
47
48 return valid ? 0 : GIT_ECERTIFICATE;
49 }
50
51 void test_online_clone__initialize(void)
52 {
53 git_checkout_options dummy_opts = GIT_CHECKOUT_OPTIONS_INIT;
54 git_fetch_options dummy_fetch = GIT_FETCH_OPTIONS_INIT;
55
56 g_repo = NULL;
57
58 memset(&g_options, 0, sizeof(git_clone_options));
59 g_options.version = GIT_CLONE_OPTIONS_VERSION;
60 g_options.checkout_opts = dummy_opts;
61 g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
62 g_options.fetch_opts = dummy_fetch;
63 g_options.fetch_opts.callbacks.certificate_check = ssl_cert;
64
65 _remote_url = cl_getenv("GITTEST_REMOTE_URL");
66 _remote_user = cl_getenv("GITTEST_REMOTE_USER");
67 _remote_pass = cl_getenv("GITTEST_REMOTE_PASS");
68 _remote_sslnoverify = cl_getenv("GITTEST_REMOTE_SSL_NOVERIFY");
69 _remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
70 _remote_ssh_privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY");
71 _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
72 _remote_ssh_fingerprint = cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT");
73 _remote_proxy_scheme = cl_getenv("GITTEST_REMOTE_PROXY_SCHEME");
74 _remote_proxy_host = cl_getenv("GITTEST_REMOTE_PROXY_HOST");
75 _remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER");
76 _remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS");
77 _remote_proxy_selfsigned = cl_getenv("GITTEST_REMOTE_PROXY_SELFSIGNED");
78 _remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE");
79
80 if (_remote_expectcontinue)
81 git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1);
82
83 _orig_proxies_need_reset = 0;
84 }
85
86 void test_online_clone__cleanup(void)
87 {
88 if (g_repo) {
89 git_repository_free(g_repo);
90 g_repo = NULL;
91 }
92 cl_fixture_cleanup("./foo");
93
94 git__free(_remote_url);
95 git__free(_remote_user);
96 git__free(_remote_pass);
97 git__free(_remote_sslnoverify);
98 git__free(_remote_ssh_pubkey);
99 git__free(_remote_ssh_privkey);
100 git__free(_remote_ssh_passphrase);
101 git__free(_remote_ssh_fingerprint);
102 git__free(_remote_proxy_scheme);
103 git__free(_remote_proxy_host);
104 git__free(_remote_proxy_user);
105 git__free(_remote_proxy_pass);
106 git__free(_remote_proxy_selfsigned);
107 git__free(_remote_expectcontinue);
108
109 if (_orig_proxies_need_reset) {
110 cl_setenv("HTTP_PROXY", _orig_http_proxy);
111 cl_setenv("HTTPS_PROXY", _orig_https_proxy);
112
113 git__free(_orig_http_proxy);
114 git__free(_orig_https_proxy);
115 }
116 }
117
118 void test_online_clone__network_full(void)
119 {
120 git_remote *origin;
121
122 cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
123 cl_assert(!git_repository_is_bare(g_repo));
124 cl_git_pass(git_remote_lookup(&origin, g_repo, "origin"));
125
126 cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, origin->download_tags);
127
128 git_remote_free(origin);
129 }
130
131 void test_online_clone__network_bare(void)
132 {
133 git_remote *origin;
134
135 g_options.bare = true;
136
137 cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
138 cl_assert(git_repository_is_bare(g_repo));
139 cl_git_pass(git_remote_lookup(&origin, g_repo, "origin"));
140
141 git_remote_free(origin);
142 }
143
144 void test_online_clone__empty_repository(void)
145 {
146 git_reference *head;
147
148 cl_git_pass(git_clone(&g_repo, LIVE_EMPTYREPO_URL, "./foo", &g_options));
149
150 cl_assert_equal_i(true, git_repository_is_empty(g_repo));
151 cl_assert_equal_i(true, git_repository_head_unborn(g_repo));
152
153 cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
154 cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
155 cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
156
157 git_reference_free(head);
158 }
159
160 static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
161 {
162 bool *was_called = (bool*)payload;
163 GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
164 (*was_called) = true;
165 }
166
167 static int fetch_progress(const git_indexer_progress *stats, void *payload)
168 {
169 bool *was_called = (bool*)payload;
170 GIT_UNUSED(stats);
171 (*was_called) = true;
172 return 0;
173 }
174
175 void test_online_clone__can_checkout_a_cloned_repo(void)
176 {
177 git_buf path = GIT_BUF_INIT;
178 git_reference *head;
179 bool checkout_progress_cb_was_called = false,
180 fetch_progress_cb_was_called = false;
181
182 g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
183 g_options.checkout_opts.progress_cb = &checkout_progress;
184 g_options.checkout_opts.progress_payload = &checkout_progress_cb_was_called;
185 g_options.fetch_opts.callbacks.transfer_progress = &fetch_progress;
186 g_options.fetch_opts.callbacks.payload = &fetch_progress_cb_was_called;
187
188 cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
189
190 cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
191 cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
192
193 cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
194 cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
195 cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
196
197 cl_assert_equal_i(true, checkout_progress_cb_was_called);
198 cl_assert_equal_i(true, fetch_progress_cb_was_called);
199
200 git_reference_free(head);
201 git_buf_dispose(&path);
202 }
203
204 static int remote_mirror_cb(git_remote **out, git_repository *repo,
205 const char *name, const char *url, void *payload)
206 {
207 int error;
208 git_remote *remote;
209
210 GIT_UNUSED(payload);
211
212 if ((error = git_remote_create_with_fetchspec(&remote, repo, name, url, "+refs/*:refs/*")) < 0)
213 return error;
214
215 *out = remote;
216 return 0;
217 }
218
219 void test_online_clone__clone_mirror(void)
220 {
221 git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
222 git_reference *head;
223
224 bool fetch_progress_cb_was_called = false;
225
226 opts.fetch_opts.callbacks.transfer_progress = &fetch_progress;
227 opts.fetch_opts.callbacks.payload = &fetch_progress_cb_was_called;
228
229 opts.bare = true;
230 opts.remote_cb = remote_mirror_cb;
231
232 cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo.git", &opts));
233
234 cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
235 cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
236 cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
237
238 cl_assert_equal_i(true, fetch_progress_cb_was_called);
239
240 git_reference_free(head);
241 git_repository_free(g_repo);
242 g_repo = NULL;
243
244 cl_fixture_cleanup("./foo.git");
245 }
246
247 static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
248 {
249 int *callcount = (int*)payload;
250 GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b);
251 *callcount = *callcount + 1;
252 return 0;
253 }
254
255 void test_online_clone__custom_remote_callbacks(void)
256 {
257 int callcount = 0;
258
259 g_options.fetch_opts.callbacks.update_tips = update_tips;
260 g_options.fetch_opts.callbacks.payload = &callcount;
261
262 cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
263 cl_assert(callcount > 0);
264 }
265
266 void test_online_clone__custom_headers(void)
267 {
268 char *empty_header = "";
269 char *unnamed_header = "this is a header about nothing";
270 char *newlines = "X-Custom: almost OK\n";
271 char *conflict = "Accept: defined-by-git";
272 char *ok = "X-Custom: this should be ok";
273
274 g_options.fetch_opts.custom_headers.count = 1;
275
276 g_options.fetch_opts.custom_headers.strings = &empty_header;
277 cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
278
279 g_options.fetch_opts.custom_headers.strings = &unnamed_header;
280 cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
281
282 g_options.fetch_opts.custom_headers.strings = &newlines;
283 cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
284
285 g_options.fetch_opts.custom_headers.strings = &conflict;
286 cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
287
288 /* Finally, we got it right! */
289 g_options.fetch_opts.custom_headers.strings = &ok;
290 cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
291 }
292
293 static int cred_failure_cb(
294 git_credential **cred,
295 const char *url,
296 const char *username_from_url,
297 unsigned int allowed_types,
298 void *data)
299 {
300 GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url);
301 GIT_UNUSED(allowed_types); GIT_UNUSED(data);
302 return -172;
303 }
304
305 void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
306 {
307 git__free(_remote_url);
308 git__free(_remote_user);
309
310 _remote_url = git__strdup("https://github.com/libgit2/non-existent");
311 _remote_user = git__strdup("libgit2test");
312
313 g_options.fetch_opts.callbacks.credentials = cred_failure_cb;
314
315 cl_git_fail_with(-172, git_clone(&g_repo, _remote_url, "./foo", &g_options));
316 }
317
318 static int cred_count_calls_cb(git_credential **cred, const char *url, const char *user,
319 unsigned int allowed_types, void *data)
320 {
321 size_t *counter = (size_t *) data;
322
323 GIT_UNUSED(url); GIT_UNUSED(user); GIT_UNUSED(allowed_types);
324
325 if (allowed_types == GIT_CREDENTIAL_USERNAME)
326 return git_credential_username_new(cred, "foo");
327
328 (*counter)++;
329
330 if (*counter == 3)
331 return GIT_EUSER;
332
333 return git_credential_userpass_plaintext_new(cred, "foo", "bar");
334 }
335
336 void test_online_clone__cred_callback_called_again_on_auth_failure(void)
337 {
338 size_t counter = 0;
339
340 git__free(_remote_url);
341 git__free(_remote_user);
342
343 _remote_url = git__strdup("https://gitlab.com/libgit2/non-existent");
344 _remote_user = git__strdup("libgit2test");
345
346 g_options.fetch_opts.callbacks.credentials = cred_count_calls_cb;
347 g_options.fetch_opts.callbacks.payload = &counter;
348
349 cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, _remote_url, "./foo", &g_options));
350 cl_assert_equal_i(3, counter);
351 }
352
353 int cred_default(
354 git_credential **cred,
355 const char *url,
356 const char *user_from_url,
357 unsigned int allowed_types,
358 void *payload)
359 {
360 GIT_UNUSED(url);
361 GIT_UNUSED(user_from_url);
362 GIT_UNUSED(payload);
363
364 if (!(allowed_types & GIT_CREDENTIAL_DEFAULT))
365 return 0;
366
367 return git_credential_default_new(cred);
368 }
369
370 void test_online_clone__credentials(void)
371 {
372 /* Remote URL environment variable must be set.
373 * User and password are optional.
374 */
375 git_credential_userpass_payload user_pass = {
376 _remote_user,
377 _remote_pass
378 };
379
380 if (!_remote_url)
381 clar__skip();
382
383 if (cl_is_env_set("GITTEST_REMOTE_DEFAULT")) {
384 g_options.fetch_opts.callbacks.credentials = cred_default;
385 } else {
386 g_options.fetch_opts.callbacks.credentials = git_credential_userpass;
387 g_options.fetch_opts.callbacks.payload = &user_pass;
388 }
389
390 cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options));
391 git_repository_free(g_repo); g_repo = NULL;
392 cl_fixture_cleanup("./foo");
393 }
394
395 void test_online_clone__credentials_via_custom_headers(void)
396 {
397 const char *creds = "libgit3:libgit3";
398 git_buf auth = GIT_BUF_INIT;
399
400 cl_git_pass(git_buf_puts(&auth, "Authorization: Basic "));
401 cl_git_pass(git_buf_encode_base64(&auth, creds, strlen(creds)));
402 g_options.fetch_opts.custom_headers.count = 1;
403 g_options.fetch_opts.custom_headers.strings = &auth.ptr;
404
405 cl_git_pass(git_clone(&g_repo, "https://bitbucket.org/libgit2/testgitrepository.git", "./foo", &g_options));
406
407 git_buf_dispose(&auth);
408 }
409
410 void test_online_clone__bitbucket_style(void)
411 {
412 git_credential_userpass_payload user_pass = {
413 "libgit3", "libgit3"
414 };
415
416 g_options.fetch_opts.callbacks.credentials = git_credential_userpass;
417 g_options.fetch_opts.callbacks.payload = &user_pass;
418
419 cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options));
420 git_repository_free(g_repo); g_repo = NULL;
421 cl_fixture_cleanup("./foo");
422 }
423
424 void test_online_clone__bitbucket_uses_creds_in_url(void)
425 {
426 git_credential_userpass_payload user_pass = {
427 "libgit2", "wrong"
428 };
429
430 g_options.fetch_opts.callbacks.credentials = git_credential_userpass;
431 g_options.fetch_opts.callbacks.payload = &user_pass;
432
433 /*
434 * Correct user and pass are in the URL; the (incorrect) creds in
435 * the `git_credential_userpass_payload` should be ignored.
436 */
437 cl_git_pass(git_clone(&g_repo, BB_REPO_URL_WITH_PASS, "./foo", &g_options));
438 git_repository_free(g_repo); g_repo = NULL;
439 cl_fixture_cleanup("./foo");
440 }
441
442 void test_online_clone__bitbucket_falls_back_to_specified_creds(void)
443 {
444 git_credential_userpass_payload user_pass = {
445 "libgit2", "libgit2"
446 };
447
448 g_options.fetch_opts.callbacks.credentials = git_credential_userpass;
449 g_options.fetch_opts.callbacks.payload = &user_pass;
450
451 /*
452 * TODO: as of March 2018, bitbucket sporadically fails with
453 * 403s instead of replying with a 401 - but only sometimes.
454 */
455 cl_skip();
456
457 /*
458 * Incorrect user and pass are in the URL; the (correct) creds in
459 * the `git_credential_userpass_payload` should be used as a fallback.
460 */
461 cl_git_pass(git_clone(&g_repo, BB_REPO_URL_WITH_WRONG_PASS, "./foo", &g_options));
462 git_repository_free(g_repo); g_repo = NULL;
463 cl_fixture_cleanup("./foo");
464 }
465
466 static int cancel_at_half(const git_indexer_progress *stats, void *payload)
467 {
468 GIT_UNUSED(payload);
469
470 if (stats->received_objects > (stats->total_objects/2))
471 return 4321;
472 return 0;
473 }
474
475 void test_online_clone__can_cancel(void)
476 {
477 g_options.fetch_opts.callbacks.transfer_progress = cancel_at_half;
478
479 cl_git_fail_with(4321,
480 git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
481 }
482
483 static int cred_cb(git_credential **cred, const char *url, const char *user_from_url,
484 unsigned int allowed_types, void *payload)
485 {
486 GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload);
487
488 if (allowed_types & GIT_CREDENTIAL_USERNAME)
489 return git_credential_username_new(cred, _remote_user);
490
491 if (allowed_types & GIT_CREDENTIAL_SSH_KEY)
492 return git_credential_ssh_key_new(cred,
493 _remote_user, _remote_ssh_pubkey,
494 _remote_ssh_privkey, _remote_ssh_passphrase);
495
496 git_error_set(GIT_ERROR_NET, "unexpected cred type");
497 return -1;
498 }
499
500 static int check_ssh_auth_methods(git_credential **cred, const char *url, const char *username_from_url,
501 unsigned int allowed_types, void *data)
502 {
503 int *with_user = (int *) data;
504 GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(data);
505
506 if (!*with_user)
507 cl_assert_equal_i(GIT_CREDENTIAL_USERNAME, allowed_types);
508 else
509 cl_assert(!(allowed_types & GIT_CREDENTIAL_USERNAME));
510
511 return GIT_EUSER;
512 }
513
514 void test_online_clone__ssh_auth_methods(void)
515 {
516 int with_user;
517
518 #ifndef GIT_SSH
519 clar__skip();
520 #endif
521 g_options.fetch_opts.callbacks.credentials = check_ssh_auth_methods;
522 g_options.fetch_opts.callbacks.payload = &with_user;
523 g_options.fetch_opts.callbacks.certificate_check = NULL;
524
525 with_user = 0;
526 cl_git_fail_with(GIT_EUSER,
527 git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
528
529 with_user = 1;
530 cl_git_fail_with(GIT_EUSER,
531 git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
532 }
533
534 static int custom_remote_ssh_with_paths(
535 git_remote **out,
536 git_repository *repo,
537 const char *name,
538 const char *url,
539 void *payload)
540 {
541 int error;
542
543 GIT_UNUSED(payload);
544
545 if ((error = git_remote_create(out, repo, name, url)) < 0)
546 return error;
547
548 return 0;
549 }
550
551 void test_online_clone__ssh_with_paths(void)
552 {
553 char *bad_paths[] = {
554 "/bin/yes",
555 "/bin/false",
556 };
557 char *good_paths[] = {
558 "/usr/bin/git-upload-pack",
559 "/usr/bin/git-receive-pack",
560 };
561 git_strarray arr = {
562 bad_paths,
563 2,
564 };
565
566 #ifndef GIT_SSH
567 clar__skip();
568 #endif
569 if (!_remote_url || !_remote_user || strncmp(_remote_url, "ssh://", 5) != 0)
570 clar__skip();
571
572 g_options.remote_cb = custom_remote_ssh_with_paths;
573 g_options.fetch_opts.callbacks.transport = git_transport_ssh_with_paths;
574 g_options.fetch_opts.callbacks.credentials = cred_cb;
575 g_options.fetch_opts.callbacks.payload = &arr;
576 g_options.fetch_opts.callbacks.certificate_check = NULL;
577
578 cl_git_fail(git_clone(&g_repo, _remote_url, "./foo", &g_options));
579
580 arr.strings = good_paths;
581 cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options));
582 }
583
584 static int cred_foo_bar(git_credential **cred, const char *url, const char *username_from_url,
585 unsigned int allowed_types, void *data)
586
587 {
588 GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(allowed_types); GIT_UNUSED(data);
589
590 return git_credential_userpass_plaintext_new(cred, "foo", "bar");
591 }
592
593 void test_online_clone__ssh_cannot_change_username(void)
594 {
595 #ifndef GIT_SSH
596 clar__skip();
597 #endif
598 g_options.fetch_opts.callbacks.credentials = cred_foo_bar;
599
600 cl_git_fail(git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
601 }
602
603 int ssh_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
604 {
605 git_cert_hostkey *key;
606 git_oid expected = {{0}}, actual = {{0}};
607
608 GIT_UNUSED(valid);
609 GIT_UNUSED(payload);
610
611 cl_assert(_remote_ssh_fingerprint);
612
613 cl_git_pass(git_oid_fromstrp(&expected, _remote_ssh_fingerprint));
614 cl_assert_equal_i(GIT_CERT_HOSTKEY_LIBSSH2, cert->cert_type);
615 key = (git_cert_hostkey *) cert;
616
617 /*
618 * We need to figure out how long our input was to check for
619 * the type. Here we abuse the fact that both hashes fit into
620 * our git_oid type.
621 */
622 if (strlen(_remote_ssh_fingerprint) == 32 && key->type & GIT_CERT_SSH_MD5) {
623 memcpy(&actual.id, key->hash_md5, 16);
624 } else if (strlen(_remote_ssh_fingerprint) == 40 && key->type & GIT_CERT_SSH_SHA1) {
625 memcpy(&actual, key->hash_sha1, 20);
626 } else {
627 cl_fail("Cannot find a usable SSH hash");
628 }
629
630 cl_assert(!memcmp(&expected, &actual, 20));
631
632 cl_assert_equal_s("localhost", host);
633
634 return GIT_EUSER;
635 }
636
637 void test_online_clone__ssh_cert(void)
638 {
639 g_options.fetch_opts.callbacks.certificate_check = ssh_certificate_check;
640
641 if (!_remote_ssh_fingerprint)
642 cl_skip();
643
644 cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, _remote_url, "./foo", &g_options));
645 }
646
647 static char *read_key_file(const char *path)
648 {
649 FILE *f;
650 char *buf;
651 long key_length;
652
653 if (!path || !*path)
654 return NULL;
655
656 cl_assert((f = fopen(path, "r")) != NULL);
657 cl_assert(fseek(f, 0, SEEK_END) != -1);
658 cl_assert((key_length = ftell(f)) != -1);
659 cl_assert(fseek(f, 0, SEEK_SET) != -1);
660 cl_assert((buf = malloc(key_length)) != NULL);
661 cl_assert(fread(buf, key_length, 1, f) == 1);
662 fclose(f);
663
664 return buf;
665 }
666
667 static int ssh_memory_cred_cb(git_credential **cred, const char *url, const char *user_from_url,
668 unsigned int allowed_types, void *payload)
669 {
670 GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload);
671
672 if (allowed_types & GIT_CREDENTIAL_USERNAME)
673 return git_credential_username_new(cred, _remote_user);
674
675 if (allowed_types & GIT_CREDENTIAL_SSH_KEY)
676 {
677 char *pubkey = read_key_file(_remote_ssh_pubkey);
678 char *privkey = read_key_file(_remote_ssh_privkey);
679
680 int ret = git_credential_ssh_key_memory_new(cred, _remote_user, pubkey, privkey, _remote_ssh_passphrase);
681
682 if (privkey)
683 free(privkey);
684 if (pubkey)
685 free(pubkey);
686 return ret;
687 }
688
689 git_error_set(GIT_ERROR_NET, "unexpected cred type");
690 return -1;
691 }
692
693 void test_online_clone__ssh_memory_auth(void)
694 {
695 #ifndef GIT_SSH_MEMORY_CREDENTIALS
696 clar__skip();
697 #endif
698 if (!_remote_url || !_remote_user || !_remote_ssh_privkey || strncmp(_remote_url, "ssh://", 5) != 0)
699 clar__skip();
700
701 g_options.fetch_opts.callbacks.credentials = ssh_memory_cred_cb;
702
703 cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options));
704 }
705
706 static int fail_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
707 {
708 GIT_UNUSED(cert);
709 GIT_UNUSED(valid);
710 GIT_UNUSED(host);
711 GIT_UNUSED(payload);
712
713 return GIT_ECERTIFICATE;
714 }
715
716 void test_online_clone__certificate_invalid(void)
717 {
718 g_options.fetch_opts.callbacks.certificate_check = fail_certificate_check;
719
720 cl_git_fail_with(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options),
721 GIT_ECERTIFICATE);
722
723 #ifdef GIT_SSH
724 cl_git_fail_with(git_clone(&g_repo, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options),
725 GIT_ECERTIFICATE);
726 #endif
727 }
728
729 static int succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
730 {
731 GIT_UNUSED(cert);
732 GIT_UNUSED(valid);
733 GIT_UNUSED(payload);
734
735 cl_assert_equal_s("github.com", host);
736
737 return 0;
738 }
739
740 void test_online_clone__certificate_valid(void)
741 {
742 g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
743
744 cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
745 }
746
747 void test_online_clone__start_with_http(void)
748 {
749 g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
750
751 cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
752 }
753
754 static int called_proxy_creds;
755 static int proxy_cred_cb(git_credential **out, const char *url, const char *username, unsigned int allowed, void *payload)
756 {
757 GIT_UNUSED(url);
758 GIT_UNUSED(username);
759 GIT_UNUSED(allowed);
760 GIT_UNUSED(payload);
761
762 called_proxy_creds = 1;
763 return git_credential_userpass_plaintext_new(out, _remote_proxy_user, _remote_proxy_pass);
764 }
765
766 static int proxy_cert_cb(git_cert *cert, int valid, const char *host, void *payload)
767 {
768 char *colon;
769 size_t host_len;
770
771 GIT_UNUSED(cert);
772 GIT_UNUSED(valid);
773 GIT_UNUSED(payload);
774
775 cl_assert(_remote_proxy_host);
776
777 if ((colon = strchr(_remote_proxy_host, ':')) != NULL)
778 host_len = (colon - _remote_proxy_host);
779 else
780 host_len = strlen(_remote_proxy_host);
781
782 if (_remote_proxy_selfsigned != NULL &&
783 strlen(host) == host_len &&
784 strncmp(_remote_proxy_host, host, host_len) == 0)
785 valid = 1;
786
787 return valid ? 0 : GIT_ECERTIFICATE;
788 }
789
790 void test_online_clone__proxy_credentials_request(void)
791 {
792 git_buf url = GIT_BUF_INIT;
793
794 if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
795 cl_skip();
796
797 cl_git_pass(git_buf_printf(&url, "%s://%s/",
798 _remote_proxy_scheme ? _remote_proxy_scheme : "http",
799 _remote_proxy_host));
800
801 g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
802 g_options.fetch_opts.proxy_opts.url = url.ptr;
803 g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
804 g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
805 called_proxy_creds = 0;
806 cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
807 cl_assert(called_proxy_creds);
808
809 git_buf_dispose(&url);
810 }
811
812 void test_online_clone__proxy_credentials_in_url(void)
813 {
814 git_buf url = GIT_BUF_INIT;
815
816 if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
817 cl_skip();
818
819 cl_git_pass(git_buf_printf(&url, "%s://%s:%s@%s/",
820 _remote_proxy_scheme ? _remote_proxy_scheme : "http",
821 _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
822
823 g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
824 g_options.fetch_opts.proxy_opts.url = url.ptr;
825 g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
826 called_proxy_creds = 0;
827 cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
828 cl_assert(called_proxy_creds == 0);
829
830 git_buf_dispose(&url);
831 }
832
833 void test_online_clone__proxy_credentials_in_environment(void)
834 {
835 git_buf url = GIT_BUF_INIT;
836
837 if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
838 cl_skip();
839
840 _orig_http_proxy = cl_getenv("HTTP_PROXY");
841 _orig_https_proxy = cl_getenv("HTTPS_PROXY");
842 _orig_proxies_need_reset = 1;
843
844 g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
845 g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
846
847 cl_git_pass(git_buf_printf(&url, "%s://%s:%s@%s/",
848 _remote_proxy_scheme ? _remote_proxy_scheme : "http",
849 _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
850
851 cl_setenv("HTTP_PROXY", url.ptr);
852 cl_setenv("HTTPS_PROXY", url.ptr);
853
854 cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
855
856 git_buf_dispose(&url);
857 }
858
859 void test_online_clone__proxy_auto_not_detected(void)
860 {
861 g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
862
863 cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
864 }
865
866 void test_online_clone__proxy_cred_callback_after_failed_url_creds(void)
867 {
868 git_buf url = GIT_BUF_INIT;
869
870 if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
871 cl_skip();
872
873 cl_git_pass(git_buf_printf(&url, "%s://invalid_user_name:INVALID_pass_WORD@%s/",
874 _remote_proxy_scheme ? _remote_proxy_scheme : "http",
875 _remote_proxy_host));
876
877 g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
878 g_options.fetch_opts.proxy_opts.url = url.ptr;
879 g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
880 g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
881 called_proxy_creds = 0;
882 cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
883 cl_assert(called_proxy_creds);
884
885 git_buf_dispose(&url);
886 }
887
888 void test_online_clone__azurerepos(void)
889 {
890 cl_git_pass(git_clone(&g_repo, "https://libgit2@dev.azure.com/libgit2/test/_git/test", "./foo", &g_options));
891 cl_assert(git_path_exists("./foo/master.txt"));
892 }
893
894 void test_online_clone__path_whitespace(void)
895 {
896 cl_git_pass(git_clone(&g_repo, "https://libgit2@dev.azure.com/libgit2/test/_git/spaces%20in%20the%20name", "./foo", &g_options));
897 cl_assert(git_path_exists("./foo/master.txt"));
898 }