Commit a3c062db by Russell Belfer

Make SSH APIs present even without SSH support

The SSH APIs will just return an error code and state that the
library was built without SSH support if they are called in
that case.
parent 82cb8e23
......@@ -36,14 +36,15 @@ typedef enum {
} git_credtype_t;
/* The base structure for all credential types */
typedef struct git_cred {
typedef struct git_cred git_cred;
struct git_cred {
git_credtype_t credtype;
void (*free)(
struct git_cred *cred);
} git_cred;
void (*free)(git_cred *cred);
};
/* A plaintext username and password */
typedef struct git_cred_userpass_plaintext {
typedef struct {
git_cred parent;
char *username;
char *password;
......@@ -51,6 +52,9 @@ typedef struct git_cred_userpass_plaintext {
#ifdef GIT_SSH
typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback));
#else
typedef int (*git_cred_sign_callback)(void *, ...);
#endif
/* A ssh key file and passphrase */
typedef struct git_cred_ssh_keyfile_passphrase {
......@@ -68,7 +72,6 @@ typedef struct git_cred_ssh_publickey {
void *sign_callback;
void *sign_data;
} git_cred_ssh_publickey;
#endif
/**
* Creates a new plain-text username and password credential object.
......@@ -84,7 +87,6 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new(
const char *username,
const char *password);
#ifdef GIT_SSH
/**
* Creates a new ssh key file and passphrase credential object.
* The supplied credential parameter will be internally duplicated.
......@@ -116,9 +118,8 @@ GIT_EXTERN(int) git_cred_ssh_publickey_new(
git_cred **out,
const char *publickey,
size_t publickey_len,
git_cred_sign_callback,
git_cred_sign_callback sign_fn,
void *sign_data);
#endif
/**
* Signature of a function which acquires a credential object.
......@@ -152,17 +153,21 @@ typedef enum {
typedef void (*git_transport_message_cb)(const char *str, int len, void *data);
typedef struct git_transport {
typedef struct git_transport git_transport;
struct git_transport {
unsigned int version;
/* Set progress and error callbacks */
int (*set_callbacks)(struct git_transport *transport,
int (*set_callbacks)(
git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
void *payload);
/* Connect the transport to the remote repository, using the given
* direction. */
int (*connect)(struct git_transport *transport,
int (*connect)(
git_transport *transport,
const char *url,
git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
......@@ -172,17 +177,19 @@ typedef struct git_transport {
/* This function may be called after a successful call to connect(). The
* provided callback is invoked for each ref discovered on the remote
* end. */
int (*ls)(struct git_transport *transport,
int (*ls)(
git_transport *transport,
git_headlist_cb list_cb,
void *payload);
/* Executes the push whose context is in the git_push object. */
int (*push)(struct git_transport *transport, git_push *push);
int (*push)(git_transport *transport, git_push *push);
/* This function may be called after a successful call to connect(), when
* the direction is FETCH. The function performs a negotiation to calculate
* the wants list for the fetch. */
int (*negotiate_fetch)(struct git_transport *transport,
int (*negotiate_fetch)(
git_transport *transport,
git_repository *repo,
const git_remote_head * const *refs,
size_t count);
......@@ -190,28 +197,29 @@ typedef struct git_transport {
/* This function may be called after a successful call to negotiate_fetch(),
* when the direction is FETCH. This function retrieves the pack file for
* the fetch from the remote end. */
int (*download_pack)(struct git_transport *transport,
int (*download_pack)(
git_transport *transport,
git_repository *repo,
git_transfer_progress *stats,
git_transfer_progress_callback progress_cb,
void *progress_payload);
/* Checks to see if the transport is connected */
int (*is_connected)(struct git_transport *transport);
int (*is_connected)(git_transport *transport);
/* Reads the flags value previously passed into connect() */
int (*read_flags)(struct git_transport *transport, int *flags);
int (*read_flags)(git_transport *transport, int *flags);
/* Cancels any outstanding transport operation */
void (*cancel)(struct git_transport *transport);
void (*cancel)(git_transport *transport);
/* This function is the reverse of connect() -- it terminates the
* connection to the remote end. */
int (*close)(struct git_transport *transport);
int (*close)(git_transport *transport);
/* Frees/destructs the git_transport object. */
void (*free)(struct git_transport *transport);
} git_transport;
void (*free)(git_transport *transport);
};
#define GIT_TRANSPORT_VERSION 1
#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
......@@ -299,35 +307,36 @@ typedef enum {
GIT_SERVICE_RECEIVEPACK = 4,
} git_smart_service_t;
struct git_smart_subtransport;
typedef struct git_smart_subtransport git_smart_subtransport;
typedef struct git_smart_subtransport_stream git_smart_subtransport_stream;
/* A stream used by the smart transport to read and write data
* from a subtransport */
typedef struct git_smart_subtransport_stream {
struct git_smart_subtransport_stream {
/* The owning subtransport */
struct git_smart_subtransport *subtransport;
git_smart_subtransport *subtransport;
int (*read)(
struct git_smart_subtransport_stream *stream,
char *buffer,
size_t buf_size,
size_t *bytes_read);
git_smart_subtransport_stream *stream,
char *buffer,
size_t buf_size,
size_t *bytes_read);
int (*write)(
struct git_smart_subtransport_stream *stream,
const char *buffer,
size_t len);
git_smart_subtransport_stream *stream,
const char *buffer,
size_t len);
void (*free)(
struct git_smart_subtransport_stream *stream);
} git_smart_subtransport_stream;
git_smart_subtransport_stream *stream);
};
/* An implementation of a subtransport which carries data for the
* smart transport */
typedef struct git_smart_subtransport {
struct git_smart_subtransport {
int (* action)(
git_smart_subtransport_stream **out,
struct git_smart_subtransport *transport,
git_smart_subtransport *transport,
const char *url,
git_smart_service_t action);
......@@ -337,10 +346,10 @@ typedef struct git_smart_subtransport {
*
* 1. UPLOADPACK_LS -> UPLOADPACK
* 2. RECEIVEPACK_LS -> RECEIVEPACK */
int (* close)(struct git_smart_subtransport *transport);
int (*close)(git_smart_subtransport *transport);
void (* free)(struct git_smart_subtransport *transport);
} git_smart_subtransport;
void (*free)(git_smart_subtransport *transport);
};
/* A function which creates a new subtransport for the smart transport */
typedef int (*git_smart_subtransport_cb)(
......
......@@ -73,7 +73,7 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
/* It could be a SSH remote path. Check to see if there's a :
* SSH is an unsupported transport mechanism in this version of libgit2 */
if (!definition && strrchr(url, ':'))
definition = &dummy_transport_definition;
definition = &dummy_transport_definition;
#else
/* For other systems, perform the SSH check first, to avoid going to the
* filesystem if it is not necessary */
......@@ -97,7 +97,7 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
*callback = definition->fn;
*param = definition->param;
return 0;
}
......
......@@ -68,7 +68,7 @@ static void ssh_keyfile_passphrase_free(struct git_cred *cred)
if (c->publickey) {
git__free(c->publickey);
}
git__free(c->privatekey);
if (c->passphrase) {
......@@ -82,12 +82,28 @@ static void ssh_keyfile_passphrase_free(struct git_cred *cred)
git__free(c);
}
static void ssh_publickey_free(struct git_cred *cred)
{
git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
git__free(c->publickey);
c->sign_callback = NULL;
c->sign_data = NULL;
memset(c, 0, sizeof(*c));
git__free(c);
}
#endif
int git_cred_ssh_keyfile_passphrase_new(
git_cred **cred,
const char *publickey,
const char *privatekey,
const char *passphrase)
{
#ifdef GIT_SSH
git_cred_ssh_keyfile_passphrase *c;
assert(cred && privatekey);
......@@ -97,15 +113,15 @@ int git_cred_ssh_keyfile_passphrase_new(
c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE;
c->parent.free = ssh_keyfile_passphrase_free;
c->privatekey = git__strdup(privatekey);
GITERR_CHECK_ALLOC(c->privatekey);
if (publickey) {
c->publickey = git__strdup(publickey);
GITERR_CHECK_ALLOC(c->publickey);
}
if (passphrase) {
c->passphrase = git__strdup(passphrase);
GITERR_CHECK_ALLOC(c->passphrase);
......@@ -113,29 +129,27 @@ int git_cred_ssh_keyfile_passphrase_new(
*cred = &c->parent;
return 0;
}
static void ssh_publickey_free(struct git_cred *cred)
{
git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
#else
GIT_UNUSED(publickey);
GIT_UNUSED(privatekey);
GIT_UNUSED(passphrase);
git__free(c->publickey);
c->sign_callback = NULL;
c->sign_data = NULL;
memset(c, 0, sizeof(*c));
assert(cred);
*cred = NULL;
git__free(c);
giterr_set(GITERR_INVALID, "Cannot create SSH credential. Library was built without SSH support");
return -1;
#endif
}
int git_cred_ssh_publickey_new(
git_cred **cred,
const char *publickey,
size_t publickey_len,
LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)),
git_cred_sign_callback sign_callback,
void *sign_data)
{
#ifdef GIT_SSH
git_cred_ssh_publickey *c;
if (!cred)
......@@ -146,17 +160,28 @@ int git_cred_ssh_publickey_new(
c->parent.credtype = GIT_CREDTYPE_SSH_PUBLICKEY;
c->parent.free = ssh_publickey_free;
c->publickey = git__malloc(publickey_len);
GITERR_CHECK_ALLOC(c->publickey);
memcpy(c->publickey, publickey, publickey_len);
c->publickey_len = publickey_len;
c->sign_callback = sign_callback;
c->sign_data = sign_data;
*cred = &c->parent;
return 0;
}
#else
GIT_UNUSED(publickey);
GIT_UNUSED(publickey_len);
GIT_UNUSED(sign_callback);
GIT_UNUSED(sign_data);
assert(cred);
*cred = NULL;
giterr_set(GITERR_INVALID, "Cannot create SSH credential. Library was built without SSH support");
return -1;
#endif
}
......@@ -5,13 +5,13 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifdef GIT_SSH
#include "git2.h"
#include "buffer.h"
#include "netops.h"
#include "smart.h"
#ifdef GIT_SSH
#include <libssh2.h>
#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
......@@ -46,27 +46,27 @@ typedef struct {
static int gen_proto(git_buf *request, const char *cmd, const char *url)
{
char *repo;
if (!git__prefixcmp(url, prefix_ssh)) {
url = url + strlen(prefix_ssh);
repo = strchr(url, '/');
} else {
repo = strchr(url, ':');
}
if (!repo) {
return -1;
}
int len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
git_buf_grow(request, len);
git_buf_printf(request, "%s '%s'", cmd, repo);
git_buf_putc(request, '\0');
if (git_buf_oom(request))
return -1;
return 0;
}
......@@ -74,11 +74,11 @@ static int send_command(ssh_stream *s)
{
int error;
git_buf request = GIT_BUF_INIT;
error = gen_proto(&request, s->cmd, s->url);
if (error < 0)
goto cleanup;
error = libssh2_channel_exec(
s->channel,
request.ptr
......@@ -86,9 +86,9 @@ static int send_command(ssh_stream *s)
if (0 != error)
goto cleanup;
s->sent_command = 1;
cleanup:
git_buf_free(&request);
return error;
......@@ -101,18 +101,18 @@ static int ssh_stream_read(
size_t *bytes_read)
{
ssh_stream *s = (ssh_stream *)stream;
*bytes_read = 0;
if (!s->sent_command && send_command(s) < 0)
return -1;
int rc = libssh2_channel_read(s->channel, buffer, buf_size);
if (rc < 0)
return -1;
*bytes_read = rc;
return 0;
}
......@@ -122,15 +122,15 @@ static int ssh_stream_write(
size_t len)
{
ssh_stream *s = (ssh_stream *)stream;
if (!s->sent_command && send_command(s) < 0)
return -1;
int rc = libssh2_channel_write(s->channel, buffer, len);
if (rc < 0) {
return -1;
}
return rc;
}
......@@ -139,26 +139,26 @@ static void ssh_stream_free(git_smart_subtransport_stream *stream)
ssh_stream *s = (ssh_stream *)stream;
ssh_subtransport *t = OWNING_SUBTRANSPORT(s);
int ret;
GIT_UNUSED(ret);
t->current_stream = NULL;
if (s->channel) {
libssh2_channel_close(s->channel);
libssh2_channel_free(s->channel);
s->channel = NULL;
}
if (s->session) {
libssh2_session_free(s->session), s->session = NULL;
}
if (s->socket.socket) {
ret = gitno_close(&s->socket);
assert(!ret);
}
git__free(s->url);
git__free(s);
}
......@@ -170,26 +170,26 @@ static int ssh_stream_alloc(
git_smart_subtransport_stream **stream)
{
ssh_stream *s;
if (!stream)
return -1;
s = git__calloc(sizeof(ssh_stream), 1);
GITERR_CHECK_ALLOC(s);
s->parent.subtransport = &t->parent;
s->parent.read = ssh_stream_read;
s->parent.write = ssh_stream_write;
s->parent.free = ssh_stream_free;
s->cmd = cmd;
s->url = git__strdup(url);
if (!s->url) {
git__free(s);
return -1;
}
*stream = &s->parent;
return 0;
}
......@@ -201,14 +201,14 @@ static int git_ssh_extract_url_parts(
{
char *colon, *at;
const char *start;
colon = strchr(url, ':');
if (colon == NULL) {
giterr_set(GITERR_NET, "Malformed URL: missing :");
return -1;
}
at = strchr(url, '@');
if (at) {
start = at+1;
......@@ -217,9 +217,9 @@ static int git_ssh_extract_url_parts(
start = url;
*username = git__strdup(default_user);
}
*host = git__substrdup(start, colon - start);
return 0;
}
......@@ -235,7 +235,7 @@ static int _git_ssh_authenticate_session(
case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
rc = libssh2_userauth_password(
session,
session,
c->username,
c->password
);
......@@ -244,7 +244,7 @@ static int _git_ssh_authenticate_session(
case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
rc = libssh2_userauth_publickey_fromfile(
session,
session,
user,
c->publickey,
c->privatekey,
......@@ -268,7 +268,7 @@ static int _git_ssh_authenticate_session(
rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
}
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
return rc;
}
......@@ -281,31 +281,31 @@ static int _git_ssh_session_create
if (!session) {
return -1;
}
LIBSSH2_SESSION* s = libssh2_session_init();
if (!s)
return -1;
int rc = 0;
do {
rc = libssh2_session_startup(s, socket.socket);
} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
if (0 != rc) {
goto on_error;
}
libssh2_session_set_blocking(s, 1);
*session = s;
return 0;
on_error:
if (s) {
libssh2_session_free(s), s = NULL;
}
return -1;
}
......@@ -321,13 +321,13 @@ static int _git_ssh_setup_conn(
ssh_stream *s;
LIBSSH2_SESSION* session=NULL;
LIBSSH2_CHANNEL* channel=NULL;
*stream = NULL;
if (ssh_stream_alloc(t, url, cmd, stream) < 0)
return -1;
s = (ssh_stream *)*stream;
if (!git__prefixcmp(url, prefix_ssh)) {
url = url + strlen(prefix_ssh);
if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0)
......@@ -338,10 +338,10 @@ static int _git_ssh_setup_conn(
port = git__strdup(default_port);
GITERR_CHECK_ALLOC(port);
}
if (gitno_connect(&s->socket, host, port, 0) < 0)
goto on_error;
if (user && pass) {
if (git_cred_userpass_plaintext_new(&t->cred, user, pass) < 0)
goto on_error;
......@@ -354,26 +354,26 @@ static int _git_ssh_setup_conn(
return -1;
}
assert(t->cred);
if (!user) {
user = git__strdup(default_user);
}
if (_git_ssh_session_create(&session, s->socket) < 0)
goto on_error;
if (_git_ssh_authenticate_session(session, user, t->cred) < 0)
goto on_error;
channel = libssh2_channel_open_session(session);
if (!channel)
goto on_error;
libssh2_channel_set_blocking(channel, 1);
s->session = session;
s->channel = channel;
t->current_stream = s;
git__free(host);
git__free(port);
......@@ -381,11 +381,11 @@ static int _git_ssh_setup_conn(
git__free(pass);
return 0;
on_error:
if (*stream)
ssh_stream_free(*stream);
git__free(host);
git__free(port);
git__free(user);
......@@ -404,7 +404,7 @@ static int ssh_uploadpack_ls(
{
if (_git_ssh_setup_conn(t, url, cmd_uploadpack, stream) < 0)
return -1;
return 0;
}
......@@ -414,12 +414,12 @@ static int ssh_uploadpack(
git_smart_subtransport_stream **stream)
{
GIT_UNUSED(url);
if (t->current_stream) {
*stream = &t->current_stream->parent;
return 0;
}
giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
return -1;
}
......@@ -431,7 +431,7 @@ static int ssh_receivepack_ls(
{
if (_git_ssh_setup_conn(t, url, cmd_receivepack, stream) < 0)
return -1;
return 0;
}
......@@ -441,12 +441,12 @@ static int ssh_receivepack(
git_smart_subtransport_stream **stream)
{
GIT_UNUSED(url);
if (t->current_stream) {
*stream = &t->current_stream->parent;
return 0;
}
giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK");
return -1;
}
......@@ -458,21 +458,21 @@ static int _ssh_action(
git_smart_service_t action)
{
ssh_subtransport *t = (ssh_subtransport *) subtransport;
switch (action) {
case GIT_SERVICE_UPLOADPACK_LS:
return ssh_uploadpack_ls(t, url, stream);
case GIT_SERVICE_UPLOADPACK:
return ssh_uploadpack(t, url, stream);
case GIT_SERVICE_RECEIVEPACK_LS:
return ssh_receivepack_ls(t, url, stream);
case GIT_SERVICE_RECEIVEPACK:
return ssh_receivepack(t, url, stream);
}
*stream = NULL;
return -1;
}
......@@ -480,40 +480,49 @@ static int _ssh_action(
static int _ssh_close(git_smart_subtransport *subtransport)
{
ssh_subtransport *t = (ssh_subtransport *) subtransport;
assert(!t->current_stream);
GIT_UNUSED(t);
return 0;
}
static void _ssh_free(git_smart_subtransport *subtransport)
{
ssh_subtransport *t = (ssh_subtransport *) subtransport;
assert(!t->current_stream);
git__free(t);
}
#endif
int git_smart_subtransport_ssh(git_smart_subtransport **out, git_transport *owner)
int git_smart_subtransport_ssh(
git_smart_subtransport **out, git_transport *owner)
{
#ifdef GIT_SSH
ssh_subtransport *t;
if (!out)
return -1;
assert(out);
t = git__calloc(sizeof(ssh_subtransport), 1);
GITERR_CHECK_ALLOC(t);
t->owner = (transport_smart *)owner;
t->parent.action = _ssh_action;
t->parent.close = _ssh_close;
t->parent.free = _ssh_free;
*out = (git_smart_subtransport *) t;
return 0;
}
#else
GIT_UNUSED(owner);
assert(out);
*out = NULL;
giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support");
return -1;
#endif
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment