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