File Coverage

deps/libgit2/src/transports/git.c
Criterion Covered Total %
statement 0 153 0.0
branch 0 64 0.0
condition n/a
subroutine n/a
pod n/a
total 0 217 0.0


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