]> git.proxmox.com Git - libgit2.git/blob - src/transports/git.c
Merge pull request #3016 from pks-t/ignore-exclude-fix
[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 "git2.h"
9 #include "buffer.h"
10 #include "netops.h"
11 #include "git2/sys/transport.h"
12 #include "stream.h"
13 #include "socket_stream.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_buf *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 giterr_set(GITERR_NET, "Malformed URL");
49 return -1;
50 }
51
52 repo = delim;
53
54 delim = strchr(url, ':');
55 if (delim == NULL)
56 delim = strchr(url, '/');
57
58 len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
59
60 git_buf_grow(request, len);
61 git_buf_printf(request, "%04x%s %s%c%s",
62 (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
63 git_buf_put(request, url, delim - url);
64 git_buf_putc(request, '\0');
65
66 if (git_buf_oom(request))
67 return -1;
68
69 return 0;
70 }
71
72 static int send_command(git_proto_stream *s)
73 {
74 int error;
75 git_buf request = GIT_BUF_INIT;
76
77 error = gen_proto(&request, s->cmd, s->url);
78 if (error < 0)
79 goto cleanup;
80
81 error = git_stream_write(s->io, request.ptr, request.size, 0);
82 if (error >= 0)
83 s->sent_command = 1;
84
85 cleanup:
86 git_buf_free(&request);
87 return error;
88 }
89
90 static int git_proto_stream_read(
91 git_smart_subtransport_stream *stream,
92 char *buffer,
93 size_t buf_size,
94 size_t *bytes_read)
95 {
96 int error;
97 git_proto_stream *s = (git_proto_stream *)stream;
98 gitno_buffer buf;
99
100 *bytes_read = 0;
101
102 if (!s->sent_command && (error = send_command(s)) < 0)
103 return error;
104
105 gitno_buffer_setup_fromstream(s->io, &buf, buffer, buf_size);
106
107 if ((error = gitno_recv(&buf)) < 0)
108 return error;
109
110 *bytes_read = buf.offset;
111
112 return 0;
113 }
114
115 static int git_proto_stream_write(
116 git_smart_subtransport_stream *stream,
117 const char *buffer,
118 size_t len)
119 {
120 int error;
121 git_proto_stream *s = (git_proto_stream *)stream;
122
123 if (!s->sent_command && (error = send_command(s)) < 0)
124 return error;
125
126 return git_stream_write(s->io, buffer, len, 0);
127 }
128
129 static void git_proto_stream_free(git_smart_subtransport_stream *stream)
130 {
131 git_proto_stream *s = (git_proto_stream *)stream;
132 git_subtransport *t = OWNING_SUBTRANSPORT(s);
133 int ret;
134
135 GIT_UNUSED(ret);
136
137 t->current_stream = NULL;
138
139 git_stream_close(s->io);
140 git_stream_free(s->io);
141 git__free(s->url);
142 git__free(s);
143 }
144
145 static int git_proto_stream_alloc(
146 git_subtransport *t,
147 const char *url,
148 const char *cmd,
149 const char *host,
150 const char *port,
151 git_smart_subtransport_stream **stream)
152 {
153 git_proto_stream *s;
154
155 if (!stream)
156 return -1;
157
158 s = git__calloc(1, sizeof(git_proto_stream));
159 GITERR_CHECK_ALLOC(s);
160
161 s->parent.subtransport = &t->parent;
162 s->parent.read = git_proto_stream_read;
163 s->parent.write = git_proto_stream_write;
164 s->parent.free = git_proto_stream_free;
165
166 s->cmd = cmd;
167 s->url = git__strdup(url);
168
169 if (!s->url) {
170 git__free(s);
171 return -1;
172 }
173
174 if ((git_socket_stream_new(&s->io, host, port)) < 0)
175 return -1;
176
177 GITERR_CHECK_VERSION(s->io, GIT_STREAM_VERSION, "git_stream");
178
179 *stream = &s->parent;
180 return 0;
181 }
182
183 static int _git_uploadpack_ls(
184 git_subtransport *t,
185 const char *url,
186 git_smart_subtransport_stream **stream)
187 {
188 char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
189 const char *stream_url = url;
190 git_proto_stream *s;
191 int error;
192
193 *stream = NULL;
194
195 if (!git__prefixcmp(url, prefix_git))
196 stream_url += strlen(prefix_git);
197
198 if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0)
199 return error;
200
201 error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream);
202
203 git__free(host);
204 git__free(port);
205 git__free(path);
206 git__free(user);
207 git__free(pass);
208
209
210 if (error < 0) {
211 git_proto_stream_free(*stream);
212 return error;
213 }
214
215 s = (git_proto_stream *) *stream;
216 if ((error = git_stream_connect(s->io)) < 0) {
217 git_proto_stream_free(*stream);
218 return error;
219 }
220
221 t->current_stream = s;
222
223 return 0;
224 }
225
226 static int _git_uploadpack(
227 git_subtransport *t,
228 const char *url,
229 git_smart_subtransport_stream **stream)
230 {
231 GIT_UNUSED(url);
232
233 if (t->current_stream) {
234 *stream = &t->current_stream->parent;
235 return 0;
236 }
237
238 giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
239 return -1;
240 }
241
242 static int _git_receivepack_ls(
243 git_subtransport *t,
244 const char *url,
245 git_smart_subtransport_stream **stream)
246 {
247 char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
248 const char *stream_url = url;
249 git_proto_stream *s;
250 int error;
251
252 *stream = NULL;
253 if (!git__prefixcmp(url, prefix_git))
254 stream_url += strlen(prefix_git);
255
256 if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0)
257 return error;
258
259 error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, host, port, stream);
260
261 git__free(host);
262 git__free(port);
263 git__free(path);
264 git__free(user);
265 git__free(pass);
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 giterr_set(GITERR_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 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 assert(!t->current_stream);
340
341 git__free(t);
342 }
343
344 int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner, void *param)
345 {
346 git_subtransport *t;
347
348 GIT_UNUSED(param);
349
350 if (!out)
351 return -1;
352
353 t = git__calloc(1, sizeof(git_subtransport));
354 GITERR_CHECK_ALLOC(t);
355
356 t->owner = owner;
357 t->parent.action = _git_action;
358 t->parent.close = _git_close;
359 t->parent.free = _git_free;
360
361 *out = (git_smart_subtransport *) t;
362 return 0;
363 }