mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
cli: add ssh key handling to clone
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
#include "error.h"
|
||||
#include "sighandler.h"
|
||||
#include "progress.h"
|
||||
#include "console.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "fs_path.h"
|
||||
#include "futils.h"
|
||||
@@ -21,7 +23,6 @@
|
||||
static char *branch, *remote_path, *local_path, *depth;
|
||||
static int quiet, checkout = 1, bare;
|
||||
static bool local_path_exists;
|
||||
static cli_progress progress = CLI_PROGRESS_INIT;
|
||||
|
||||
static const cli_opt_spec opts[] = {
|
||||
CLI_COMMON_OPT,
|
||||
@@ -44,6 +45,16 @@ static const cli_opt_spec opts[] = {
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
#define CREDENTIAL_RETRY_MAX 3
|
||||
|
||||
struct clone_callback_data {
|
||||
cli_progress progress;
|
||||
size_t credential_retries;
|
||||
git_str password;
|
||||
};
|
||||
|
||||
static struct clone_callback_data callback_data = { CLI_PROGRESS_INIT };
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts, 0);
|
||||
@@ -105,7 +116,7 @@ static void cleanup(void)
|
||||
{
|
||||
int rmdir_flags = GIT_RMDIR_REMOVE_FILES;
|
||||
|
||||
cli_progress_abort(&progress);
|
||||
cli_progress_abort(&callback_data.progress);
|
||||
|
||||
if (local_path_exists)
|
||||
rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
|
||||
@@ -122,6 +133,119 @@ static void interrupt_cleanup(void)
|
||||
exit(130);
|
||||
}
|
||||
|
||||
static int find_keys(git_str *pub, git_str *priv)
|
||||
{
|
||||
git_str path = GIT_STR_INIT;
|
||||
static const char *key_paths[6] = {
|
||||
"id_dsa", "id_ecdsa", "id_ecdsa_sk",
|
||||
"id_ed25519", "id_ed25519_sk", "id_rsa"
|
||||
};
|
||||
size_t i, path_len;
|
||||
int error = -1;
|
||||
|
||||
if (git_system_homedir(&path) < 0)
|
||||
goto done;
|
||||
|
||||
path_len = git_str_len(&path);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(key_paths); i++) {
|
||||
git_str_truncate(&path, path_len);
|
||||
|
||||
if (git_str_puts(&path, "/.ssh/") < 0 ||
|
||||
git_str_puts(&path, key_paths[i]) < 0)
|
||||
goto done;
|
||||
|
||||
if (git_fs_path_exists(path.ptr)) {
|
||||
if (git_str_puts(priv, path.ptr) < 0 ||
|
||||
git_str_puts(pub, path.ptr) < 0 ||
|
||||
git_str_puts(pub, ".pub") < 0)
|
||||
goto done;
|
||||
|
||||
error = 0;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
error = GIT_ENOTFOUND;
|
||||
|
||||
done:
|
||||
git_str_dispose(&path);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int clone_credentials(
|
||||
git_credential **out,
|
||||
const char *url,
|
||||
const char *username_from_url,
|
||||
unsigned int allowed_types,
|
||||
void *payload)
|
||||
{
|
||||
struct clone_callback_data *data = (struct clone_callback_data *)payload;
|
||||
git_str pubkey = GIT_STR_INIT, privkey = GIT_STR_INIT,
|
||||
prompt = GIT_STR_INIT;
|
||||
int error = GIT_PASSTHROUGH;
|
||||
|
||||
GIT_UNUSED(url);
|
||||
|
||||
if (++data->credential_retries > CREDENTIAL_RETRY_MAX) {
|
||||
cli_error("authentication failed");
|
||||
error = GIT_EUSER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((allowed_types & GIT_CREDENTIAL_SSH_KEY)) {
|
||||
if ((error = find_keys(&pubkey, &privkey)) < 0) {
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
cli_error("could not find ssh keys for authentication");
|
||||
error = GIT_EUSER;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((error = git_str_printf(&prompt, "Enter passphrase for key '%s': ", pubkey.ptr)) < 0 ||
|
||||
(error = cli_console_getpass(&data->password, prompt.ptr)) < 0)
|
||||
goto done;
|
||||
|
||||
error = git_credential_ssh_key_new(out,
|
||||
username_from_url,
|
||||
pubkey.ptr,
|
||||
privkey.ptr,
|
||||
data->password.ptr);
|
||||
}
|
||||
|
||||
done:
|
||||
git_str_zero(&data->password);
|
||||
git_str_dispose(&prompt);
|
||||
git_str_dispose(&pubkey);
|
||||
git_str_dispose(&privkey);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int clone_progress_sideband(const char *str, int len, void *payload)
|
||||
{
|
||||
struct clone_callback_data *data = (struct clone_callback_data *)payload;
|
||||
return cli_progress_fetch_sideband(str, len, &data->progress);
|
||||
}
|
||||
|
||||
static int clone_progress_transfer(
|
||||
const git_indexer_progress *stats,
|
||||
void *payload)
|
||||
{
|
||||
struct clone_callback_data *data = (struct clone_callback_data *)payload;
|
||||
return cli_progress_fetch_transfer(stats, &data->progress);
|
||||
}
|
||||
|
||||
static void clone_progress_checkout(
|
||||
const char *path,
|
||||
size_t completed_steps,
|
||||
size_t total_steps,
|
||||
void *payload)
|
||||
{
|
||||
struct clone_callback_data *data = (struct clone_callback_data *)payload;
|
||||
return cli_progress_checkout(path, completed_steps, total_steps, &data->progress);
|
||||
}
|
||||
|
||||
int cmd_clone(int argc, char **argv)
|
||||
{
|
||||
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
|
||||
@@ -164,26 +288,31 @@ int cmd_clone(int argc, char **argv)
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
clone_opts.fetch_opts.callbacks.sideband_progress = cli_progress_fetch_sideband;
|
||||
clone_opts.fetch_opts.callbacks.transfer_progress = cli_progress_fetch_transfer;
|
||||
clone_opts.fetch_opts.callbacks.payload = &progress;
|
||||
clone_opts.fetch_opts.callbacks.credentials = clone_credentials;
|
||||
clone_opts.fetch_opts.callbacks.sideband_progress = clone_progress_sideband;
|
||||
clone_opts.fetch_opts.callbacks.transfer_progress = clone_progress_transfer;
|
||||
clone_opts.fetch_opts.callbacks.payload = &callback_data;
|
||||
|
||||
clone_opts.checkout_opts.progress_cb = cli_progress_checkout;
|
||||
clone_opts.checkout_opts.progress_payload = &progress;
|
||||
clone_opts.checkout_opts.progress_cb = clone_progress_checkout;
|
||||
clone_opts.checkout_opts.progress_payload = &callback_data;
|
||||
|
||||
printf("Cloning into '%s'...\n", local_path);
|
||||
}
|
||||
|
||||
if (git_clone(&repo, remote_path, local_path, &clone_opts) < 0) {
|
||||
if ((ret = git_clone(&repo, remote_path, local_path, &clone_opts)) < 0) {
|
||||
cleanup();
|
||||
ret = cli_error_git();
|
||||
|
||||
if (ret != GIT_EUSER)
|
||||
ret = cli_error_git();
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
cli_progress_finish(&progress);
|
||||
cli_progress_finish(&callback_data.progress);
|
||||
|
||||
done:
|
||||
cli_progress_dispose(&progress);
|
||||
cli_progress_dispose(&callback_data.progress);
|
||||
git_str_zero(&callback_data.password);
|
||||
git__free(computed_path);
|
||||
git_repository_free(repo);
|
||||
return ret;
|
||||
|
||||
Reference in New Issue
Block a user