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