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