]> git.proxmox.com Git - libgit2.git/blob - src/transports/git.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / transports / git.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 "netops.h"
11 #include "stream.h"
12 #include "streams/socket.h"
13 #include "git2/sys/transport.h"
14
15 #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
16
17 static const char prefix_git[] = "git://";
18 static const char cmd_uploadpack[] = "git-upload-pack";
19 static const char cmd_receivepack[] = "git-receive-pack";
20
21 typedef struct {
22 git_smart_subtransport_stream parent;
23 git_stream *io;
24 const char *cmd;
25 char *url;
26 unsigned sent_command : 1;
27 } git_proto_stream;
28
29 typedef struct {
30 git_smart_subtransport parent;
31 git_transport *owner;
32 git_proto_stream *current_stream;
33 } git_subtransport;
34
35 /*
36 * Create a git protocol request.
37 *
38 * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
39 */
40 static int gen_proto(git_str *request, const char *cmd, const char *url)
41 {
42 char *delim, *repo;
43 char host[] = "host=";
44 size_t len;
45
46 delim = strchr(url, '/');
47 if (delim == NULL) {
48 git_error_set(GIT_ERROR_NET, "malformed URL");
49 return -1;
50 }
51
52 repo = delim;
53 if (repo[1] == '~')
54 ++repo;
55
56 delim = strchr(url, ':');
57 if (delim == NULL)
58 delim = strchr(url, '/');
59
60 len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
61
62 git_str_grow(request, len);
63 git_str_printf(request, "%04x%s %s%c%s",
64 (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
65 git_str_put(request, url, delim - url);
66 git_str_putc(request, '\0');
67
68 if (git_str_oom(request))
69 return -1;
70
71 return 0;
72 }
73
74 static int send_command(git_proto_stream *s)
75 {
76 git_str request = GIT_STR_INIT;
77 int error;
78
79 if ((error = gen_proto(&request, s->cmd, s->url)) < 0)
80 goto cleanup;
81
82 if ((error = git_stream__write_full(s->io, request.ptr, request.size, 0)) < 0)
83 goto cleanup;
84
85 s->sent_command = 1;
86
87 cleanup:
88 git_str_dispose(&request);
89 return error;
90 }
91
92 static int git_proto_stream_read(
93 git_smart_subtransport_stream *stream,
94 char *buffer,
95 size_t buf_size,
96 size_t *bytes_read)
97 {
98 int error;
99 git_proto_stream *s = (git_proto_stream *)stream;
100 gitno_buffer buf;
101
102 *bytes_read = 0;
103
104 if (!s->sent_command && (error = send_command(s)) < 0)
105 return error;
106
107 gitno_buffer_setup_fromstream(s->io, &buf, buffer, buf_size);
108
109 if ((error = gitno_recv(&buf)) < 0)
110 return error;
111
112 *bytes_read = buf.offset;
113
114 return 0;
115 }
116
117 static int git_proto_stream_write(
118 git_smart_subtransport_stream *stream,
119 const char *buffer,
120 size_t len)
121 {
122 git_proto_stream *s = (git_proto_stream *)stream;
123 int error;
124
125 if (!s->sent_command && (error = send_command(s)) < 0)
126 return error;
127
128 return git_stream__write_full(s->io, buffer, len, 0);
129 }
130
131 static void git_proto_stream_free(git_smart_subtransport_stream *stream)
132 {
133 git_proto_stream *s;
134 git_subtransport *t;
135
136 if (!stream)
137 return;
138
139 s = (git_proto_stream *)stream;
140 t = OWNING_SUBTRANSPORT(s);
141
142 t->current_stream = NULL;
143
144 git_stream_close(s->io);
145 git_stream_free(s->io);
146 git__free(s->url);
147 git__free(s);
148 }
149
150 static int git_proto_stream_alloc(
151 git_subtransport *t,
152 const char *url,
153 const char *cmd,
154 const char *host,
155 const char *port,
156 git_smart_subtransport_stream **stream)
157 {
158 git_proto_stream *s;
159
160 if (!stream)
161 return -1;
162
163 s = git__calloc(1, sizeof(git_proto_stream));
164 GIT_ERROR_CHECK_ALLOC(s);
165
166 s->parent.subtransport = &t->parent;
167 s->parent.read = git_proto_stream_read;
168 s->parent.write = git_proto_stream_write;
169 s->parent.free = git_proto_stream_free;
170
171 s->cmd = cmd;
172 s->url = git__strdup(url);
173
174 if (!s->url) {
175 git__free(s);
176 return -1;
177 }
178
179 if ((git_socket_stream_new(&s->io, host, port)) < 0)
180 return -1;
181
182 GIT_ERROR_CHECK_VERSION(s->io, GIT_STREAM_VERSION, "git_stream");
183
184 *stream = &s->parent;
185 return 0;
186 }
187
188 static int _git_uploadpack_ls(
189 git_subtransport *t,
190 const char *url,
191 git_smart_subtransport_stream **stream)
192 {
193 git_net_url urldata = GIT_NET_URL_INIT;
194 const char *stream_url = url;
195 const char *host, *port;
196 git_proto_stream *s;
197 int error;
198
199 *stream = NULL;
200
201 if (!git__prefixcmp(url, prefix_git))
202 stream_url += strlen(prefix_git);
203
204 if ((error = git_net_url_parse(&urldata, url)) < 0)
205 return error;
206
207 host = urldata.host;
208 port = urldata.port ? urldata.port : GIT_DEFAULT_PORT;
209
210 error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream);
211
212 git_net_url_dispose(&urldata);
213
214 if (error < 0) {
215 git_proto_stream_free(*stream);
216 return error;
217 }
218
219 s = (git_proto_stream *) *stream;
220 if ((error = git_stream_connect(s->io)) < 0) {
221 git_proto_stream_free(*stream);
222 return error;
223 }
224
225 t->current_stream = s;
226
227 return 0;
228 }
229
230 static int _git_uploadpack(
231 git_subtransport *t,
232 const char *url,
233 git_smart_subtransport_stream **stream)
234 {
235 GIT_UNUSED(url);
236
237 if (t->current_stream) {
238 *stream = &t->current_stream->parent;
239 return 0;
240 }
241
242 git_error_set(GIT_ERROR_NET, "must call UPLOADPACK_LS before UPLOADPACK");
243 return -1;
244 }
245
246 static int _git_receivepack_ls(
247 git_subtransport *t,
248 const char *url,
249 git_smart_subtransport_stream **stream)
250 {
251 git_net_url urldata = GIT_NET_URL_INIT;
252 const char *stream_url = url;
253 git_proto_stream *s;
254 int error;
255
256 *stream = NULL;
257 if (!git__prefixcmp(url, prefix_git))
258 stream_url += strlen(prefix_git);
259
260 if ((error = git_net_url_parse(&urldata, url)) < 0)
261 return error;
262
263 error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, urldata.host, urldata.port, stream);
264
265 git_net_url_dispose(&urldata);
266
267 if (error < 0) {
268 git_proto_stream_free(*stream);
269 return error;
270 }
271
272 s = (git_proto_stream *) *stream;
273
274 if ((error = git_stream_connect(s->io)) < 0)
275 return error;
276
277 t->current_stream = s;
278
279 return 0;
280 }
281
282 static int _git_receivepack(
283 git_subtransport *t,
284 const char *url,
285 git_smart_subtransport_stream **stream)
286 {
287 GIT_UNUSED(url);
288
289 if (t->current_stream) {
290 *stream = &t->current_stream->parent;
291 return 0;
292 }
293
294 git_error_set(GIT_ERROR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK");
295 return -1;
296 }
297
298 static int _git_action(
299 git_smart_subtransport_stream **stream,
300 git_smart_subtransport *subtransport,
301 const char *url,
302 git_smart_service_t action)
303 {
304 git_subtransport *t = (git_subtransport *) subtransport;
305
306 switch (action) {
307 case GIT_SERVICE_UPLOADPACK_LS:
308 return _git_uploadpack_ls(t, url, stream);
309
310 case GIT_SERVICE_UPLOADPACK:
311 return _git_uploadpack(t, url, stream);
312
313 case GIT_SERVICE_RECEIVEPACK_LS:
314 return _git_receivepack_ls(t, url, stream);
315
316 case GIT_SERVICE_RECEIVEPACK:
317 return _git_receivepack(t, url, stream);
318 }
319
320 *stream = NULL;
321 return -1;
322 }
323
324 static int _git_close(git_smart_subtransport *subtransport)
325 {
326 git_subtransport *t = (git_subtransport *) subtransport;
327
328 GIT_ASSERT(!t->current_stream);
329
330 GIT_UNUSED(t);
331
332 return 0;
333 }
334
335 static void _git_free(git_smart_subtransport *subtransport)
336 {
337 git_subtransport *t = (git_subtransport *) subtransport;
338
339 git__free(t);
340 }
341
342 int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner, void *param)
343 {
344 git_subtransport *t;
345
346 GIT_UNUSED(param);
347
348 if (!out)
349 return -1;
350
351 t = git__calloc(1, sizeof(git_subtransport));
352 GIT_ERROR_CHECK_ALLOC(t);
353
354 t->owner = owner;
355 t->parent.action = _git_action;
356 t->parent.close = _git_close;
357 t->parent.free = _git_free;
358
359 *out = (git_smart_subtransport *) t;
360 return 0;
361 }