]> git.proxmox.com Git - libgit2.git/blob - src/transport.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / transport.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
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 char *prefix;
19 git_transport_cb fn;
20 void *param;
21 } transport_definition;
22
23 static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1, NULL };
24 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 { "git://", git_transport_smart, &git_subtransport_definition },
33 { "http://", git_transport_smart, &http_subtransport_definition },
34 { "https://", git_transport_smart, &http_subtransport_definition },
35 { "file://", git_transport_local, NULL },
36 #ifdef GIT_SSH
37 { "ssh://", git_transport_smart, &ssh_subtransport_definition },
38 { "ssh+git://", git_transport_smart, &ssh_subtransport_definition },
39 { "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 /* Find a user transport who wants to deal with this URI */
54 git_vector_foreach(&custom_transports, i, d) {
55 if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
56 return d;
57 }
58 }
59
60 /* Find a system transport for this URI */
61 for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
62 d = &transports[i];
63
64 if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
65 return d;
66 }
67 }
68
69 return NULL;
70 }
71
72 static int transport_find_fn(
73 git_transport_cb *out,
74 const char *url,
75 void **param)
76 {
77 transport_definition *definition = transport_find_by_url(url);
78
79 #ifdef GIT_WIN32
80 /* On Windows, it might not be possible to discern between absolute local
81 * and ssh paths - first check if this is a valid local path that points
82 * to a directory and if so assume local path, else assume SSH */
83
84 /* 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 /* For other systems, perform the SSH check first, to avoid going to the
90 * filesystem if it is not necessary */
91
92 /* It could be a SSH remote path. Check to see if there's a : */
93 if (!definition && strrchr(url, ':')) {
94 /* re-search transports again with ssh:// as url
95 * so that we can find a third party ssh transport */
96 definition = transport_find_by_url("ssh://");
97 }
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 definition = &local_transport_definition;
103 #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 * Public API *
116 **************/
117
118 int git_transport_new(git_transport **out, git_remote *owner, const char *url)
119 {
120 git_transport_cb fn;
121 git_transport *transport;
122 void *param;
123 int error;
124
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 } else if (error < 0)
129 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 *out = transport;
137
138 return 0;
139 }
140
141 int git_transport_register(
142 const char *scheme,
143 git_transport_cb cb,
144 void *param)
145 {
146 git_str prefix = GIT_STR_INIT;
147 transport_definition *d, *definition = NULL;
148 size_t i;
149 int error = 0;
150
151 GIT_ASSERT_ARG(scheme);
152 GIT_ASSERT_ARG(cb);
153
154 if ((error = git_str_printf(&prefix, "%s://", scheme)) < 0)
155 goto on_error;
156
157 git_vector_foreach(&custom_transports, i, d) {
158 if (strcasecmp(d->prefix, prefix.ptr) == 0) {
159 error = GIT_EEXISTS;
160 goto on_error;
161 }
162 }
163
164 definition = git__calloc(1, sizeof(transport_definition));
165 GIT_ERROR_CHECK_ALLOC(definition);
166
167 definition->prefix = git_str_detach(&prefix);
168 definition->fn = cb;
169 definition->param = param;
170
171 if (git_vector_insert(&custom_transports, definition) < 0)
172 goto on_error;
173
174 return 0;
175
176 on_error:
177 git_str_dispose(&prefix);
178 git__free(definition);
179 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 size_t i;
187 int error = 0;
188
189 GIT_ASSERT_ARG(scheme);
190
191 if ((error = git_str_printf(&prefix, "%s://", scheme)) < 0)
192 goto done;
193
194 git_vector_foreach(&custom_transports, i, d) {
195 if (strcasecmp(d->prefix, prefix.ptr) == 0) {
196 if ((error = git_vector_remove(&custom_transports, i)) < 0)
197 goto done;
198
199 git__free(d->prefix);
200 git__free(d);
201
202 if (!custom_transports.length)
203 git_vector_free(&custom_transports);
204
205 error = 0;
206 goto done;
207 }
208 }
209
210 error = GIT_ENOTFOUND;
211
212 done:
213 git_str_dispose(&prefix);
214 return error;
215 }
216
217 int git_transport_init(git_transport *opts, unsigned int version)
218 {
219 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
220 opts, version, git_transport, GIT_TRANSPORT_INIT);
221 return 0;
222 }