File Coverage

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