Blame src/transport.c

Packit ae9e2a
/*
Packit ae9e2a
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit ae9e2a
 *
Packit ae9e2a
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit ae9e2a
 * a Linking Exception. For full terms see the included COPYING file.
Packit ae9e2a
 */
Packit ae9e2a
#include "common.h"
Packit ae9e2a
#include "git2/types.h"
Packit ae9e2a
#include "git2/remote.h"
Packit ae9e2a
#include "git2/net.h"
Packit ae9e2a
#include "git2/transport.h"
Packit ae9e2a
#include "git2/sys/transport.h"
Packit ae9e2a
#include "path.h"
Packit ae9e2a
Packit ae9e2a
typedef struct transport_definition {
Packit ae9e2a
	char *prefix;
Packit ae9e2a
	git_transport_cb fn;
Packit ae9e2a
	void *param;
Packit ae9e2a
} transport_definition;
Packit ae9e2a
Packit ae9e2a
static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1, NULL };
Packit ae9e2a
static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0, NULL };
Packit ae9e2a
#ifdef GIT_SSH
Packit ae9e2a
static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0, NULL };
Packit ae9e2a
#endif
Packit ae9e2a
Packit ae9e2a
static transport_definition local_transport_definition = { "file://", git_transport_local, NULL };
Packit ae9e2a
Packit ae9e2a
static transport_definition transports[] = {
Packit ae9e2a
	{ "git://",   git_transport_smart, &git_subtransport_definition },
Packit ae9e2a
	{ "http://",  git_transport_smart, &http_subtransport_definition },
Packit ae9e2a
	{ "https://", git_transport_smart, &http_subtransport_definition },
Packit ae9e2a
	{ "file://",  git_transport_local, NULL },
Packit ae9e2a
#ifdef GIT_SSH
Packit ae9e2a
	{ "ssh://",   git_transport_smart, &ssh_subtransport_definition },
Packit ae9e2a
	{ "ssh+git://",   git_transport_smart, &ssh_subtransport_definition },
Packit ae9e2a
	{ "git+ssh://",   git_transport_smart, &ssh_subtransport_definition },
Packit ae9e2a
#endif
Packit ae9e2a
	{ NULL, 0, 0 }
Packit ae9e2a
};
Packit ae9e2a
Packit ae9e2a
static git_vector custom_transports = GIT_VECTOR_INIT;
Packit ae9e2a
Packit ae9e2a
#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
Packit ae9e2a
Packit ae9e2a
static transport_definition * transport_find_by_url(const char *url)
Packit ae9e2a
{
Packit ae9e2a
	size_t i = 0;
Packit ae9e2a
	transport_definition *d;
Packit ae9e2a
Packit ae9e2a
	/* Find a user transport who wants to deal with this URI */
Packit ae9e2a
	git_vector_foreach(&custom_transports, i, d) {
Packit ae9e2a
		if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
Packit ae9e2a
			return d;
Packit ae9e2a
		}
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	/* Find a system transport for this URI */
Packit ae9e2a
	for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
Packit ae9e2a
		d = &transports[i];
Packit ae9e2a
Packit ae9e2a
		if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
Packit ae9e2a
			return d;
Packit ae9e2a
		}
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	return NULL;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
static int transport_find_fn(
Packit ae9e2a
	git_transport_cb *out,
Packit ae9e2a
	const char *url,
Packit ae9e2a
	void **param)
Packit ae9e2a
{
Packit ae9e2a
	transport_definition *definition = transport_find_by_url(url);
Packit ae9e2a
Packit ae9e2a
#ifdef GIT_WIN32
Packit ae9e2a
	/* On Windows, it might not be possible to discern between absolute local
Packit ae9e2a
	 * and ssh paths - first check if this is a valid local path that points
Packit ae9e2a
	 * to a directory and if so assume local path, else assume SSH */
Packit ae9e2a
Packit ae9e2a
	/* Check to see if the path points to a file on the local file system */
Packit ae9e2a
	if (!definition && git_path_exists(url) && git_path_isdir(url))
Packit ae9e2a
		definition = &local_transport_definition;
Packit ae9e2a
#endif
Packit ae9e2a
Packit ae9e2a
	/* For other systems, perform the SSH check first, to avoid going to the
Packit ae9e2a
	 * filesystem if it is not necessary */
Packit ae9e2a
Packit ae9e2a
	/* It could be a SSH remote path. Check to see if there's a : */
Packit ae9e2a
	if (!definition && strrchr(url, ':')) {
Packit ae9e2a
		/* re-search transports again with ssh:// as url
Packit ae9e2a
		 * so that we can find a third party ssh transport */
Packit ae9e2a
		definition = transport_find_by_url("ssh://");
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
#ifndef GIT_WIN32
Packit ae9e2a
	/* Check to see if the path points to a file on the local file system */
Packit ae9e2a
	if (!definition && git_path_exists(url) && git_path_isdir(url))
Packit ae9e2a
		definition = &local_transport_definition;
Packit ae9e2a
#endif
Packit ae9e2a
Packit ae9e2a
	if (!definition)
Packit ae9e2a
		return GIT_ENOTFOUND;
Packit ae9e2a
Packit ae9e2a
	*out = definition->fn;
Packit ae9e2a
	*param = definition->param;
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
/**************
Packit ae9e2a
 * Public API *
Packit ae9e2a
 **************/
Packit ae9e2a
Packit ae9e2a
int git_transport_new(git_transport **out, git_remote *owner, const char *url)
Packit ae9e2a
{
Packit ae9e2a
	git_transport_cb fn;
Packit ae9e2a
	git_transport *transport;
Packit ae9e2a
	void *param;
Packit ae9e2a
	int error;
Packit ae9e2a
Packit ae9e2a
	if ((error = transport_find_fn(&fn, url, &param)) == GIT_ENOTFOUND) {
Packit ae9e2a
		giterr_set(GITERR_NET, "unsupported URL protocol");
Packit ae9e2a
		return -1;
Packit ae9e2a
	} else if (error < 0)
Packit ae9e2a
		return error;
Packit ae9e2a
Packit ae9e2a
	if ((error = fn(&transport, owner, param)) < 0)
Packit ae9e2a
		return error;
Packit ae9e2a
Packit ae9e2a
	GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
Packit ae9e2a
Packit ae9e2a
	*out = transport;
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_transport_register(
Packit ae9e2a
	const char *scheme,
Packit ae9e2a
	git_transport_cb cb,
Packit ae9e2a
	void *param)
Packit ae9e2a
{
Packit ae9e2a
	git_buf prefix = GIT_BUF_INIT;
Packit ae9e2a
	transport_definition *d, *definition = NULL;
Packit ae9e2a
	size_t i;
Packit ae9e2a
	int error = 0;
Packit ae9e2a
Packit ae9e2a
	assert(scheme);
Packit ae9e2a
	assert(cb);
Packit ae9e2a
Packit ae9e2a
	if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
Packit ae9e2a
		goto on_error;
Packit ae9e2a
Packit ae9e2a
	git_vector_foreach(&custom_transports, i, d) {
Packit ae9e2a
		if (strcasecmp(d->prefix, prefix.ptr) == 0) {
Packit ae9e2a
			error = GIT_EEXISTS;
Packit ae9e2a
			goto on_error;
Packit ae9e2a
		}
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	definition = git__calloc(1, sizeof(transport_definition));
Packit ae9e2a
	GITERR_CHECK_ALLOC(definition);
Packit ae9e2a
Packit ae9e2a
	definition->prefix = git_buf_detach(&prefix);
Packit ae9e2a
	definition->fn = cb;
Packit ae9e2a
	definition->param = param;
Packit ae9e2a
Packit ae9e2a
	if (git_vector_insert(&custom_transports, definition) < 0)
Packit ae9e2a
		goto on_error;
Packit ae9e2a
Packit ae9e2a
	return 0;
Packit ae9e2a
Packit ae9e2a
on_error:
Packit ae9e2a
	git_buf_free(&prefix);
Packit ae9e2a
	git__free(definition);
Packit ae9e2a
	return error;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_transport_unregister(const char *scheme)
Packit ae9e2a
{
Packit ae9e2a
	git_buf prefix = GIT_BUF_INIT;
Packit ae9e2a
	transport_definition *d;
Packit ae9e2a
	size_t i;
Packit ae9e2a
	int error = 0;
Packit ae9e2a
Packit ae9e2a
	assert(scheme);
Packit ae9e2a
Packit ae9e2a
	if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0)
Packit ae9e2a
		goto done;
Packit ae9e2a
Packit ae9e2a
	git_vector_foreach(&custom_transports, i, d) {
Packit ae9e2a
		if (strcasecmp(d->prefix, prefix.ptr) == 0) {
Packit ae9e2a
			if ((error = git_vector_remove(&custom_transports, i)) < 0)
Packit ae9e2a
				goto done;
Packit ae9e2a
Packit ae9e2a
			git__free(d->prefix);
Packit ae9e2a
			git__free(d);
Packit ae9e2a
Packit ae9e2a
			if (!custom_transports.length)
Packit ae9e2a
				git_vector_free(&custom_transports);
Packit ae9e2a
Packit ae9e2a
			error = 0;
Packit ae9e2a
			goto done;
Packit ae9e2a
		}
Packit ae9e2a
	}
Packit ae9e2a
Packit ae9e2a
	error = GIT_ENOTFOUND;
Packit ae9e2a
Packit ae9e2a
done:
Packit ae9e2a
	git_buf_free(&prefix);
Packit ae9e2a
	return error;
Packit ae9e2a
}
Packit ae9e2a
Packit ae9e2a
int git_transport_init(git_transport *opts, unsigned int version)
Packit ae9e2a
{
Packit ae9e2a
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
Packit ae9e2a
		opts, version, git_transport, GIT_TRANSPORT_INIT);
Packit ae9e2a
	return 0;
Packit ae9e2a
}