transport.c 5.53 KB
Newer Older
Vicent Marti committed
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
Vicent Marti committed
3 4 5 6
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */
7

8
#include "common.h"
9

10
#include "git2/types.h"
11
#include "git2/remote.h"
12
#include "git2/net.h"
13
#include "git2/transport.h"
14
#include "git2/sys/transport.h"
15
#include "fs_path.h"
16

17
typedef struct transport_definition {
18 19
	char *prefix;
	git_transport_cb fn;
20 21 22
	void *param;
} transport_definition;

23 24
static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1, NULL };
static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0, NULL };
25
#ifdef GIT_SSH
26
static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0, NULL };
27
#endif
28

29
static transport_definition local_transport_definition = { "file://", git_transport_local, NULL };
30

31
static transport_definition transports[] = {
32 33 34 35
	{ "git://",   git_transport_smart, &git_subtransport_definition },
	{ "http://",  git_transport_smart, &http_subtransport_definition },
	{ "https://", git_transport_smart, &http_subtransport_definition },
	{ "file://",  git_transport_local, NULL },
36
#ifdef GIT_SSH
37
	{ "ssh://",   git_transport_smart, &ssh_subtransport_definition },
Chris Bargren committed
38 39
	{ "ssh+git://",   git_transport_smart, &ssh_subtransport_definition },
	{ "git+ssh://",   git_transport_smart, &ssh_subtransport_definition },
40
#endif
41
	{ NULL, 0, 0 }
42 43
};

44
static git_vector custom_transports = GIT_VECTOR_INIT;
45

46
#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
47

48
static transport_definition * transport_find_by_url(const char *url)
49
{
50
	size_t i = 0;
51
	transport_definition *d;
52

53 54 55
	/* Find a user transport who wants to deal with this URI */
	git_vector_foreach(&custom_transports, i, d) {
		if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
56
			return d;
57
		}
58 59
	}

60
	/* Find a system transport for this URI */
61 62 63 64 65
	for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
		d = &transports[i];

		if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
			return d;
66
		}
67 68
	}

69 70 71 72 73 74 75 76 77 78
	return NULL;
}

static int transport_find_fn(
	git_transport_cb *out,
	const char *url,
	void **param)
{
	transport_definition *definition = transport_find_by_url(url);

79 80 81 82 83 84
#ifdef GIT_WIN32
	/* On Windows, it might not be possible to discern between absolute local
	 * and ssh paths - first check if this is a valid local path that points
	 * to a directory and if so assume local path, else assume SSH */

	/* Check to see if the path points to a file on the local file system */
85
	if (!definition && git_fs_path_exists(url) && git_fs_path_isdir(url))
86
		definition = &local_transport_definition;
87
#endif
88 89 90 91

	/* For other systems, perform the SSH check first, to avoid going to the
	 * filesystem if it is not necessary */

Richard Ipsum committed
92
	/* It could be a SSH remote path. Check to see if there's a : */
93
	if (!definition && strrchr(url, ':')) {
94 95
		/* re-search transports again with ssh:// as url
		 * so that we can find a third party ssh transport */
96 97
		definition = transport_find_by_url("ssh://");
	}
98

99
#ifndef GIT_WIN32
100
	/* Check to see if the path points to a file on the local file system */
101
	if (!definition && git_fs_path_exists(url) && git_fs_path_isdir(url))
102 103
		definition = &local_transport_definition;
#endif
104

105
	if (!definition)
106
		return GIT_ENOTFOUND;
107

108
	*out = definition->fn;
109
	*param = definition->param;
110

111
	return 0;
112 113 114 115 116 117
}

/**************
 * Public API *
 **************/

118
int git_transport_new(git_transport **out, git_remote *owner, const char *url)
119 120 121
{
	git_transport_cb fn;
	git_transport *transport;
122
	void *param;
123 124
	int error;

125
	if ((error = transport_find_fn(&fn, url, &param)) == GIT_ENOTFOUND) {
126
		git_error_set(GIT_ERROR_NET, "unsupported URL protocol");
127
		return -1;
128 129
	} else if (error < 0)
		return error;
130

131
	if ((error = fn(&transport, owner, param)) < 0)
132
		return error;
133

134
	GIT_ERROR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
135

136 137
	*out = transport;

138
	return 0;
139
}
140

141
int git_transport_register(
142
	const char *scheme,
143 144 145
	git_transport_cb cb,
	void *param)
{
146
	git_str prefix = GIT_STR_INIT;
147 148 149
	transport_definition *d, *definition = NULL;
	size_t i;
	int error = 0;
150

151 152
	GIT_ASSERT_ARG(scheme);
	GIT_ASSERT_ARG(cb);
153

154
	if ((error = git_str_printf(&prefix, "%s://", scheme)) < 0)
155 156
		goto on_error;

157 158 159 160 161 162 163 164
	git_vector_foreach(&custom_transports, i, d) {
		if (strcasecmp(d->prefix, prefix.ptr) == 0) {
			error = GIT_EEXISTS;
			goto on_error;
		}
	}

	definition = git__calloc(1, sizeof(transport_definition));
165
	GIT_ERROR_CHECK_ALLOC(definition);
166

167
	definition->prefix = git_str_detach(&prefix);
168 169
	definition->fn = cb;
	definition->param = param;
170

171
	if (git_vector_insert(&custom_transports, definition) < 0)
172 173 174 175 176
		goto on_error;

	return 0;

on_error:
177
	git_str_dispose(&prefix);
178 179
	git__free(definition);
	return error;
180 181
}

182
int git_transport_unregister(const char *scheme)
183
{
184
	git_str prefix = GIT_STR_INIT;
185
	transport_definition *d;
186 187 188
	size_t i;
	int error = 0;

189
	GIT_ASSERT_ARG(scheme);
190

191
	if ((error = git_str_printf(&prefix, "%s://", scheme)) < 0)
192 193 194 195 196 197
		goto done;

	git_vector_foreach(&custom_transports, i, d) {
		if (strcasecmp(d->prefix, prefix.ptr) == 0) {
			if ((error = git_vector_remove(&custom_transports, i)) < 0)
				goto done;
198 199 200 201

			git__free(d->prefix);
			git__free(d);

202 203
			if (!custom_transports.length)
				git_vector_free(&custom_transports);
204

205 206
			error = 0;
			goto done;
207 208 209
		}
	}

210 211 212
	error = GIT_ENOTFOUND;

done:
213
	git_str_dispose(&prefix);
214
	return error;
215 216
}

217
int git_transport_init(git_transport *opts, unsigned int version)
218
{
219 220 221
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
		opts, version, git_transport, GIT_TRANSPORT_INIT);
	return 0;
222
}