]> git.proxmox.com Git - libgit2.git/blame - src/transports/git.c
Fix the build on Emscripten
[libgit2.git] / src / transports / git.c
CommitLineData
ecb6ca0e 1/*
bb742ede 2 * Copyright (C) 2009-2011 the libgit2 contributors
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
ecb6ca0e 8#include "git2/net.h"
ecb6ca0e
CMN
9#include "git2/common.h"
10#include "git2/types.h"
11#include "git2/errors.h"
22f65b9e
CMN
12#include "git2/net.h"
13#include "git2/revwalk.h"
ecb6ca0e
CMN
14
15#include "vector.h"
16#include "transport.h"
bdd18829 17#include "pkt.h"
ecb6ca0e 18#include "common.h"
1b4f8140 19#include "netops.h"
da290220
CMN
20#include "filebuf.h"
21#include "repository.h"
fc3e3c55 22#include "fetch.h"
40a40e8e 23#include "protocol.h"
ecb6ca0e
CMN
24
25typedef struct {
4e913309 26 git_transport parent;
40a40e8e 27 git_protocol proto;
ccc9872d 28 GIT_SOCKET socket;
ecb6ca0e
CMN
29 git_vector refs;
30 git_remote_head **heads;
0437d991 31 git_transport_caps caps;
fc3e3c55
CMN
32 char buff[1024];
33 gitno_buffer buf;
ccc9872d
CMN
34#ifdef GIT_WIN32
35 WSADATA wsd;
36#endif
4e913309
CMN
37} transport_git;
38
fd679021
CMN
39/*
40 * Create a git procol request.
41 *
42 * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
43 */
10063aeb 44static int gen_proto(git_buf *request, const char *cmd, const char *url)
fd679021 45{
a95aeb48 46 char *delim, *repo;
fd679021
CMN
47 char default_command[] = "git-upload-pack";
48 char host[] = "host=";
49 int len;
50
51 delim = strchr(url, '/');
52 if (delim == NULL)
53 return git__throw(GIT_EOBJCORRUPTED, "Failed to create proto-request: malformed URL");
54
55 repo = delim;
56
57 delim = strchr(url, ':');
58 if (delim == NULL)
59 delim = strchr(url, '/');
60
61 if (cmd == NULL)
62 cmd = default_command;
63
7ad994bb 64 len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
fd679021 65
10063aeb
VM
66 git_buf_grow(request, len);
67 git_buf_printf(request, "%04x%s %s%c%s", len, cmd, repo, 0, host);
68 git_buf_put(request, url, delim - url);
69 git_buf_putc(request, '\0');
fd679021 70
97769280 71 return git_buf_lasterror(request);
fd679021
CMN
72}
73
ccc9872d 74static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
fd679021 75{
10063aeb
VM
76 int error;
77 git_buf request = GIT_BUF_INIT;
fd679021 78
10063aeb 79 error = gen_proto(&request, cmd, url);
fd679021
CMN
80 if (error < GIT_SUCCESS)
81 goto cleanup;
82
10063aeb 83 error = gitno_send(s, request.ptr, request.size, 0);
fd679021
CMN
84
85cleanup:
10063aeb 86 git_buf_free(&request);
fd679021
CMN
87 return error;
88}
ecb6ca0e 89
ecb6ca0e
CMN
90/*
91 * Parse the URL and connect to a server, storing the socket in
92 * out. For convenience this also takes care of asking for the remote
93 * refs
94 */
4e913309 95static int do_connect(transport_git *t, const char *url)
ecb6ca0e 96{
ccc9872d 97 GIT_SOCKET s;
c4d0fa85 98 char *host, *port;
ecb6ca0e 99 const char prefix[] = "git://";
c4d0fa85 100 int error, connected = 0;
ecb6ca0e
CMN
101
102 if (!git__prefixcmp(url, prefix))
932669b8 103 url += strlen(prefix);
ecb6ca0e 104
db84b798 105 error = gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT);
c75a890b
KS
106 if (error < GIT_SUCCESS)
107 return error;
db84b798 108
1b4f8140
CMN
109 s = gitno_connect(host, port);
110 connected = 1;
fd679021 111 error = send_request(s, NULL, url);
4e913309 112 t->socket = s;
ecb6ca0e 113
3286c408
VM
114 git__free(host);
115 git__free(port);
ecb6ca0e
CMN
116
117 if (error < GIT_SUCCESS && s > 0)
118 close(s);
119 if (!connected)
120 error = git__throw(GIT_EOSERR, "Failed to connect to any of the addresses");
121
122 return error;
123}
124
125/*
126 * Read from the socket and store the references in the vector
127 */
4e913309 128static int store_refs(transport_git *t)
ecb6ca0e 129{
fc3e3c55 130 gitno_buffer *buf = &t->buf;
ecb6ca0e 131 int error = GIT_SUCCESS;
7632e249 132
ecb6ca0e 133 while (1) {
fc3e3c55 134 error = gitno_recv(buf);
c7c787ce
CMN
135 if (error < GIT_SUCCESS)
136 return git__rethrow(GIT_EOSERR, "Failed to receive data");
137 if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */
7632e249 138 return GIT_SUCCESS;
ecb6ca0e 139
40a40e8e
CMN
140 error = git_protocol_store_refs(&t->proto, buf->data, buf->offset);
141 if (error == GIT_ESHORTBUFFER) {
142 gitno_consume_n(buf, buf->len);
143 continue;
144 }
c7c787ce 145
40a40e8e
CMN
146 if (error < GIT_SUCCESS)
147 return git__rethrow(error, "Failed to store refs");
ecb6ca0e 148
40a40e8e 149 gitno_consume_n(buf, buf->offset);
ecb6ca0e 150
40a40e8e
CMN
151 if (t->proto.flush) { /* No more refs */
152 t->proto.flush = 0;
153 return GIT_SUCCESS;
ecb6ca0e 154 }
ecb6ca0e
CMN
155 }
156
157 return error;
158}
159
0437d991
CMN
160static int detect_caps(transport_git *t)
161{
162 git_vector *refs = &t->refs;
163 git_pkt_ref *pkt;
164 git_transport_caps *caps = &t->caps;
165 const char *ptr;
166
167 pkt = git_vector_get(refs, 0);
168 /* No refs or capabilites, odd but not a problem */
169 if (pkt == NULL || pkt->capabilities == NULL)
170 return GIT_SUCCESS;
171
172 ptr = pkt->capabilities;
173 while (ptr != NULL && *ptr != '\0') {
174 if (*ptr == ' ')
175 ptr++;
176
177 if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
178 caps->common = caps->ofs_delta = 1;
932669b8 179 ptr += strlen(GIT_CAP_OFS_DELTA);
0437d991
CMN
180 continue;
181 }
182
183 /* We don't know this capability, so skip it */
184 ptr = strchr(ptr, ' ');
185 }
186
187 return GIT_SUCCESS;
188}
189
ecb6ca0e
CMN
190/*
191 * Since this is a network connection, we need to parse and store the
192 * pkt-lines at this stage and keep them there.
193 */
0ac2726f 194static int git_connect(git_transport *transport, int direction)
ecb6ca0e 195{
4e913309 196 transport_git *t = (transport_git *) transport;
ecb6ca0e
CMN
197 int error = GIT_SUCCESS;
198
0ac2726f 199 if (direction == GIT_DIR_PUSH)
ecb6ca0e
CMN
200 return git__throw(GIT_EINVALIDARGS, "Pushing is not supported with the git protocol");
201
0ac2726f 202 t->parent.direction = direction;
4e913309 203 error = git_vector_init(&t->refs, 16, NULL);
ecb6ca0e
CMN
204 if (error < GIT_SUCCESS)
205 goto cleanup;
206
207 /* Connect and ask for the refs */
4e913309 208 error = do_connect(t, transport->url);
ecb6ca0e
CMN
209 if (error < GIT_SUCCESS)
210 return error;
211
fc3e3c55
CMN
212 gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket);
213
5da5321d 214 t->parent.connected = 1;
4e913309 215 error = store_refs(t);
0437d991
CMN
216 if (error < GIT_SUCCESS)
217 return error;
218
219 error = detect_caps(t);
ecb6ca0e
CMN
220
221cleanup:
222 if (error < GIT_SUCCESS) {
4e913309 223 git_vector_free(&t->refs);
ecb6ca0e
CMN
224 }
225
226 return error;
227}
228
d88d4311 229static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
ecb6ca0e 230{
4e913309
CMN
231 transport_git *t = (transport_git *) transport;
232 git_vector *refs = &t->refs;
ecb6ca0e 233 unsigned int i;
d88d4311 234 git_pkt *p = NULL;
ecb6ca0e 235
d88d4311
VM
236 git_vector_foreach(refs, i, p) {
237 git_pkt_ref *pkt = NULL;
ecb6ca0e 238
ecb6ca0e
CMN
239 if (p->type != GIT_PKT_REF)
240 continue;
241
d88d4311
VM
242 pkt = (git_pkt_ref *)p;
243
244 if (list_cb(&pkt->head, opaque) < 0)
245 return git__throw(GIT_ERROR,
246 "The user callback returned an error code");
ecb6ca0e 247 }
ecb6ca0e
CMN
248
249 return GIT_SUCCESS;
250}
251
d88d4311 252static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
22f65b9e
CMN
253{
254 transport_git *t = (transport_git *) transport;
255 git_revwalk *walk;
256 git_reference *ref;
257 git_strarray refs;
258 git_oid oid;
259 int error;
260 unsigned int i;
fc3e3c55 261 gitno_buffer *buf = &t->buf;
1636ba5a 262
51760bc1 263 error = git_pkt_send_wants(wants, &t->caps, t->socket);
1636ba5a
CMN
264 if (error < GIT_SUCCESS)
265 return git__rethrow(error, "Failed to send wants list");
427ca3d3 266
22f65b9e
CMN
267 error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
268 if (error < GIT_ERROR)
269 return git__rethrow(error, "Failed to list all references");
270
271 error = git_revwalk_new(&walk, repo);
272 if (error < GIT_ERROR) {
273 error = git__rethrow(error, "Failed to list all references");
274 goto cleanup;
275 }
276 git_revwalk_sorting(walk, GIT_SORT_TIME);
277
278 for (i = 0; i < refs.count; ++i) {
427ca3d3
CMN
279 /* No tags */
280 if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
281 continue;
282
22f65b9e
CMN
283 error = git_reference_lookup(&ref, repo, refs.strings[i]);
284 if (error < GIT_ERROR) {
285 error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]);
286 goto cleanup;
287 }
288
427ca3d3
CMN
289 if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
290 continue;
d88d4311 291
22f65b9e
CMN
292 error = git_revwalk_push(walk, git_reference_oid(ref));
293 if (error < GIT_ERROR) {
294 error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
295 goto cleanup;
296 }
297 }
298 git_strarray_free(&refs);
299
300 /*
301 * We don't support any kind of ACK extensions, so the negotiation
302 * boils down to sending what we have and listening for an ACK
303 * every once in a while.
304 */
305 i = 0;
306 while ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) {
51760bc1 307 error = git_pkt_send_have(&oid, t->socket);
22f65b9e 308 i++;
427ca3d3 309 if (i % 20 == 0) {
fc3e3c55 310 const char *ptr = buf->data, *line_end;
427ca3d3 311 git_pkt *pkt;
51760bc1 312 git_pkt_send_flush(t->socket);
427ca3d3 313 while (1) {
74bd343a 314 /* Wait for max. 1 second */
fc3e3c55 315 error = gitno_select_in(buf, 1, 0);
7adba5f4
CMN
316 if (error < GIT_SUCCESS) {
317 error = git__throw(GIT_EOSERR, "Error in select");
318 } else if (error == 0) {
319 /*
320 * Some servers don't respond immediately, so if this
321 * happens, we keep sending information until it
322 * answers.
323 */
324 break;
325 }
326
fc3e3c55 327 error = gitno_recv(buf);
427ca3d3 328 if (error < GIT_SUCCESS) {
87d9869f
VM
329 error = git__rethrow(error, "Error receiving data");
330 goto cleanup;
427ca3d3 331 }
fc3e3c55 332 error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
427ca3d3
CMN
333 if (error == GIT_ESHORTBUFFER)
334 continue;
335 if (error < GIT_SUCCESS) {
336 error = git__rethrow(error, "Failed to get answer");
337 goto cleanup;
338 }
339
fc3e3c55 340 gitno_consume(buf, line_end);
427ca3d3
CMN
341
342 if (pkt->type == GIT_PKT_ACK) {
3286c408 343 git__free(pkt);
427ca3d3
CMN
344 error = GIT_SUCCESS;
345 goto done;
346 } else if (pkt->type == GIT_PKT_NAK) {
3286c408 347 git__free(pkt);
427ca3d3
CMN
348 break;
349 } else {
350 error = git__throw(GIT_ERROR, "Got unexpected pkt type");
351 goto cleanup;
352 }
353 }
354 }
22f65b9e
CMN
355 }
356 if (error == GIT_EREVWALKOVER)
357 error = GIT_SUCCESS;
358
427ca3d3 359done:
51760bc1
CMN
360 git_pkt_send_flush(t->socket);
361 git_pkt_send_done(t->socket);
22f65b9e
CMN
362
363cleanup:
364 git_revwalk_free(walk);
fc3e3c55 365
22f65b9e
CMN
366 return error;
367}
368
da290220
CMN
369static int git_send_flush(git_transport *transport)
370{
371 transport_git *t = (transport_git *) transport;
372
51760bc1 373 return git_pkt_send_flush(t->socket);
da290220
CMN
374}
375
7e1a94db
CMN
376static int git_send_done(git_transport *transport)
377{
378 transport_git *t = (transport_git *) transport;
379
51760bc1 380 return git_pkt_send_done(t->socket);
b4c90630
CMN
381}
382
9cf0f287 383static int git_download_pack(char **out, git_transport *transport, git_repository *repo)
da290220
CMN
384{
385 transport_git *t = (transport_git *) transport;
fc3e3c55
CMN
386 int error = GIT_SUCCESS;
387 gitno_buffer *buf = &t->buf;
da290220
CMN
388 git_pkt *pkt;
389 const char *line_end, *ptr;
390
da290220 391 /*
7284c105 392 * For now, we ignore everything and wait for the pack
da290220
CMN
393 */
394 while (1) {
fc3e3c55 395 ptr = buf->data;
da290220 396 /* Whilst we're searching for the pack */
7284c105 397 while (1) {
fc3e3c55 398 if (buf->offset == 0) {
da290220 399 break;
fc3e3c55
CMN
400 }
401
402 error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
da290220
CMN
403 if (error == GIT_ESHORTBUFFER)
404 break;
fc3e3c55 405
da290220
CMN
406 if (error < GIT_SUCCESS)
407 return error;
408
fc3e3c55 409 if (pkt->type == GIT_PKT_PACK) {
3286c408 410 git__free(pkt);
fc3e3c55
CMN
411 return git_fetch__download_pack(out, buf->data, buf->offset, t->socket, repo);
412 }
427ca3d3 413
da290220 414 /* For now we don't care about anything */
3286c408 415 git__free(pkt);
fc3e3c55
CMN
416 gitno_consume(buf, line_end);
417 }
418
419 error = gitno_recv(buf);
420 if (error < GIT_SUCCESS)
421 return git__rethrow(GIT_EOSERR, "Failed to receive data");
422 if (error == 0) { /* Orderly shutdown */
423 return GIT_SUCCESS;
da290220 424 }
fc3e3c55 425
da290220 426 }
da290220
CMN
427}
428
429
ecb6ca0e
CMN
430static int git_close(git_transport *transport)
431{
4e913309 432 transport_git *t = (transport_git*) transport;
ecb6ca0e
CMN
433 int error;
434
34bfb4b0 435 /* Can't do anything if there's an error, so don't bother checking */
51760bc1 436 git_pkt_send_flush(t->socket);
bad53552 437 error = gitno_close(t->socket);
34bfb4b0 438
ecb6ca0e
CMN
439 if (error < 0)
440 error = git__throw(GIT_EOSERR, "Failed to close socket");
441
bad53552
CMN
442#ifdef GIT_WIN32
443 WSACleanup();
444#endif
445
ecb6ca0e
CMN
446 return error;
447}
448
449static void git_free(git_transport *transport)
450{
4e913309
CMN
451 transport_git *t = (transport_git *) transport;
452 git_vector *refs = &t->refs;
ecb6ca0e
CMN
453 unsigned int i;
454
455 for (i = 0; i < refs->length; ++i) {
456 git_pkt *p = git_vector_get(refs, i);
be9fe679 457 git_pkt_free(p);
ecb6ca0e
CMN
458 }
459
460 git_vector_free(refs);
3286c408 461 git__free(t->heads);
40a40e8e 462 git_buf_free(&t->proto.buf);
3286c408
VM
463 git__free(t->parent.url);
464 git__free(t);
ecb6ca0e
CMN
465}
466
4e913309 467int git_transport_git(git_transport **out)
ecb6ca0e 468{
4e913309 469 transport_git *t;
ccc9872d
CMN
470#ifdef GIT_WIN32
471 int ret;
472#endif
4e913309
CMN
473
474 t = git__malloc(sizeof(transport_git));
475 if (t == NULL)
476 return GIT_ENOMEM;
477
5da5321d
CMN
478 memset(t, 0x0, sizeof(transport_git));
479
4e913309
CMN
480 t->parent.connect = git_connect;
481 t->parent.ls = git_ls;
22f65b9e 482 t->parent.negotiate_fetch = git_negotiate_fetch;
da290220 483 t->parent.send_flush = git_send_flush;
7e1a94db 484 t->parent.send_done = git_send_done;
da290220 485 t->parent.download_pack = git_download_pack;
4e913309
CMN
486 t->parent.close = git_close;
487 t->parent.free = git_free;
40a40e8e
CMN
488 t->proto.refs = &t->refs;
489 t->proto.transport = (git_transport *) t;
4e913309
CMN
490
491 *out = (git_transport *) t;
ecb6ca0e 492
ccc9872d
CMN
493#ifdef GIT_WIN32
494 ret = WSAStartup(MAKEWORD(2,2), &t->wsd);
495 if (ret != 0) {
496 git_free(*out);
497 return git__throw(GIT_EOSERR, "Winsock init failed");
498 }
499#endif
500
ecb6ca0e
CMN
501 return GIT_SUCCESS;
502}