transport.c 5.46 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"
#include "git2/types.h"
9
#include "git2/remote.h"
10
#include "git2/net.h"
11
#include "git2/transport.h"
12
#include "path.h"
13

14
typedef struct transport_definition {
15
	char *prefix;
16
	unsigned priority;
17
	git_transport_cb fn;
18 19 20 21 22
	void *param;
} transport_definition;

static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1 };
static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0 };
23
#ifdef GIT_SSH
Brad Morgan committed
24
static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0 };
25
#endif
26

27
static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL };
28 29 30
#ifdef GIT_SSH
static transport_definition ssh_transport_definition = { "ssh://", 1, git_transport_smart, &ssh_subtransport_definition };
#else
31 32 33
static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL };
#endif

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

45 46
static git_vector additional_transports = GIT_VECTOR_INIT;

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

49
static int transport_find_fn(const char *url, git_transport_cb *callback, void **param)
50
{
51
	size_t i = 0;
52 53
	unsigned priority = 0;
	transport_definition *definition = NULL, *definition_iter;
54

55
	// First, check to see if it's an obvious URL, which a URL scheme
56
	for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
57 58 59 60 61 62 63
		definition_iter = &transports[i];

		if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
			continue;

		if (definition_iter->priority > priority)
			definition = definition_iter;
64 65
	}

66 67 68 69 70 71 72 73
	git_vector_foreach(&additional_transports, i, definition_iter) {
		if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
			continue;

		if (definition_iter->priority > priority)
			definition = definition_iter;
	}

74 75 76 77 78 79 80 81
#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 */
	if (!definition && git_path_exists(url) && git_path_isdir(url))
		definition = &local_transport_definition;
82
#endif
83 84 85 86

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

87 88
	/* 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 */
89
	if (!definition && strrchr(url, ':'))
90
#ifdef GIT_SSH
91
		definition = &ssh_transport_definition;
92 93 94
#else
        definition = &dummy_transport_definition;
#endif
95

96
#ifndef GIT_WIN32
97 98 99 100
	/* Check to see if the path points to a file on the local file system */
	if (!definition && git_path_exists(url) && git_path_isdir(url))
		definition = &local_transport_definition;
#endif
101

102 103
	if (!definition)
		return -1;
104

105 106
	*callback = definition->fn;
	*param = definition->param;
107

108
	return 0;
109 110 111 112 113 114
}

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

115
int git_transport_dummy(git_transport **transport, git_remote *owner, void *param)
116
{
117
	GIT_UNUSED(transport);
118
	GIT_UNUSED(owner);
119
	GIT_UNUSED(param);
120 121
	giterr_set(GITERR_NET, "This transport isn't implemented. Sorry");
	return -1;
122 123
}

124
int git_transport_new(git_transport **out, git_remote *owner, const char *url)
125 126 127
{
	git_transport_cb fn;
	git_transport *transport;
128
	void *param;
129 130
	int error;

131
	if (transport_find_fn(url, &fn, &param) < 0) {
132 133 134
		giterr_set(GITERR_NET, "Unsupported URL protocol");
		return -1;
	}
135

136
	error = fn(&transport, owner, param);
137
	if (error < 0)
138
		return error;
139

140 141
	*out = transport;

142
	return 0;
143
}
144

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
int git_transport_register(
	const char *prefix,
	unsigned priority,
	git_transport_cb cb,
	void *param)
{
	transport_definition *d;

	d = git__calloc(sizeof(transport_definition), 1);
	GITERR_CHECK_ALLOC(d);

	d->prefix = git__strdup(prefix);

	if (!d->prefix)
		goto on_error;

	d->priority = priority;
	d->fn = cb;
	d->param = param;

	if (git_vector_insert(&additional_transports, d) < 0)
		goto on_error;

	return 0;

on_error:
	git__free(d->prefix);
	git__free(d);
	return -1;
}

int git_transport_unregister(
	const char *prefix,
	unsigned priority)
{
	transport_definition *d;
	unsigned i;

	git_vector_foreach(&additional_transports, i, d) {
		if (d->priority == priority && !strcasecmp(d->prefix, prefix)) {
			if (git_vector_remove(&additional_transports, i) < 0)
				return -1;

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

			if (!additional_transports.length)
				git_vector_free(&additional_transports);

			return 0;
		}
	}

	return GIT_ENOTFOUND;
}

201 202 203
/* from remote.h */
int git_remote_valid_url(const char *url)
{
204 205 206 207
	git_transport_cb fn;
	void *param;

	return !transport_find_fn(url, &fn, &param);
208 209
}

210 211
int git_remote_supported_url(const char* url)
{
212 213 214 215 216
	git_transport_cb fn;
	void *param;

	if (transport_find_fn(url, &fn, &param) < 0)
		return 0;
217

218
	return fn != &git_transport_dummy;
219
}