]> git.proxmox.com Git - libgit2.git/blame - src/transport_git.c
Merge pull request #392 from sschuberth/development
[libgit2.git] / src / transport_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"
ecb6ca0e
CMN
22
23typedef struct {
4e913309 24 git_transport parent;
ecb6ca0e
CMN
25 int socket;
26 git_vector refs;
27 git_remote_head **heads;
0437d991 28 git_transport_caps caps;
4e913309
CMN
29} transport_git;
30
fd679021
CMN
31/*
32 * Create a git procol request.
33 *
34 * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
35 */
36static int gen_proto(char **out, int *outlen, const char *cmd, const char *url)
37{
38 char *delim, *repo, *ptr;
39 char default_command[] = "git-upload-pack";
40 char host[] = "host=";
41 int len;
42
43 delim = strchr(url, '/');
44 if (delim == NULL)
45 return git__throw(GIT_EOBJCORRUPTED, "Failed to create proto-request: malformed URL");
46
47 repo = delim;
48
49 delim = strchr(url, ':');
50 if (delim == NULL)
51 delim = strchr(url, '/');
52
53 if (cmd == NULL)
54 cmd = default_command;
55
932669b8 56 len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 2;
fd679021
CMN
57
58 *out = git__malloc(len);
59 if (*out == NULL)
60 return GIT_ENOMEM;
61
62 *outlen = len - 1;
63 ptr = *out;
64 memset(ptr, 0x0, len);
65 /* We expect the return value to be > len - 1 so don't bother checking it */
66 snprintf(ptr, len -1, "%04x%s %s%c%s%s", len - 1, cmd, repo, 0, host, url);
67
68 return GIT_SUCCESS;
69}
70
71static int send_request(int s, const char *cmd, const char *url)
72{
73 int error, len;
74 char *msg = NULL;
75
76 error = gen_proto(&msg, &len, cmd, url);
77 if (error < GIT_SUCCESS)
78 goto cleanup;
79
80 error = gitno_send(s, msg, len, 0);
81
82cleanup:
83 free(msg);
84 return error;
85}
ecb6ca0e
CMN
86
87/* The URL should already have been stripped of the protocol */
88static int extract_host_and_port(char **host, char **port, const char *url)
89{
90 char *colon, *slash, *delim;
91 int error = GIT_SUCCESS;
92
93 colon = strchr(url, ':');
94 slash = strchr(url, '/');
95
96 if (slash == NULL)
97 return git__throw(GIT_EOBJCORRUPTED, "Malformed URL: missing /");
98
99 if (colon == NULL) {
100 *port = git__strdup(GIT_DEFAULT_PORT);
101 } else {
102 *port = git__strndup(colon + 1, slash - colon - 1);
103 }
104 if (*port == NULL)
105 return GIT_ENOMEM;;
106
107
108 delim = colon == NULL ? slash : colon;
109 *host = git__strndup(url, delim - url);
110 if (*host == NULL) {
111 free(*port);
112 error = GIT_ENOMEM;
113 }
114
115 return error;
116}
117
118/*
119 * Parse the URL and connect to a server, storing the socket in
120 * out. For convenience this also takes care of asking for the remote
121 * refs
122 */
4e913309 123static int do_connect(transport_git *t, const char *url)
ecb6ca0e
CMN
124{
125 int s = -1;
c4d0fa85 126 char *host, *port;
ecb6ca0e 127 const char prefix[] = "git://";
c4d0fa85 128 int error, connected = 0;
ecb6ca0e
CMN
129
130 if (!git__prefixcmp(url, prefix))
932669b8 131 url += strlen(prefix);
ecb6ca0e
CMN
132
133 error = extract_host_and_port(&host, &port, url);
c75a890b
KS
134 if (error < GIT_SUCCESS)
135 return error;
1b4f8140
CMN
136 s = gitno_connect(host, port);
137 connected = 1;
fd679021 138 error = send_request(s, NULL, url);
4e913309 139 t->socket = s;
ecb6ca0e 140
ecb6ca0e
CMN
141 free(host);
142 free(port);
143
144 if (error < GIT_SUCCESS && s > 0)
145 close(s);
146 if (!connected)
147 error = git__throw(GIT_EOSERR, "Failed to connect to any of the addresses");
148
149 return error;
150}
151
152/*
153 * Read from the socket and store the references in the vector
154 */
4e913309 155static int store_refs(transport_git *t)
ecb6ca0e 156{
c7c787ce 157 gitno_buffer buf;
4e913309
CMN
158 int s = t->socket;
159 git_vector *refs = &t->refs;
ecb6ca0e 160 int error = GIT_SUCCESS;
7632e249 161 char buffer[1024];
ecb6ca0e 162 const char *line_end, *ptr;
ecb6ca0e
CMN
163 git_pkt *pkt;
164
c7c787ce 165 gitno_buffer_setup(&buf, buffer, sizeof(buffer), s);
7632e249 166
ecb6ca0e 167 while (1) {
c7c787ce
CMN
168 error = gitno_recv(&buf);
169 if (error < GIT_SUCCESS)
170 return git__rethrow(GIT_EOSERR, "Failed to receive data");
171 if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */
7632e249 172 return GIT_SUCCESS;
ecb6ca0e 173
c7c787ce 174 ptr = buf.data;
ecb6ca0e 175 while (1) {
c7c787ce 176 if (buf.offset == 0)
7632e249 177 break;
c7c787ce 178 error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset);
ecb6ca0e 179 /*
7632e249
CMN
180 * If the error is GIT_ESHORTBUFFER, it means the buffer
181 * isn't long enough to satisfy the request. Break out and
182 * wait for more input.
183 * On any other error, fail.
ecb6ca0e 184 */
7632e249 185 if (error == GIT_ESHORTBUFFER) {
ecb6ca0e 186 break;
7632e249
CMN
187 }
188 if (error < GIT_SUCCESS) {
189 return error;
190 }
ecb6ca0e 191
c7c787ce
CMN
192 /* Get rid of the part we've used already */
193 gitno_consume(&buf, line_end);
194
ecb6ca0e
CMN
195 error = git_vector_insert(refs, pkt);
196 if (error < GIT_SUCCESS)
197 return error;
198
7632e249 199 if (pkt->type == GIT_PKT_FLUSH)
ecb6ca0e
CMN
200 return GIT_SUCCESS;
201
ecb6ca0e 202 }
ecb6ca0e
CMN
203 }
204
205 return error;
206}
207
0437d991
CMN
208static int detect_caps(transport_git *t)
209{
210 git_vector *refs = &t->refs;
211 git_pkt_ref *pkt;
212 git_transport_caps *caps = &t->caps;
213 const char *ptr;
214
215 pkt = git_vector_get(refs, 0);
216 /* No refs or capabilites, odd but not a problem */
217 if (pkt == NULL || pkt->capabilities == NULL)
218 return GIT_SUCCESS;
219
220 ptr = pkt->capabilities;
221 while (ptr != NULL && *ptr != '\0') {
222 if (*ptr == ' ')
223 ptr++;
224
225 if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
226 caps->common = caps->ofs_delta = 1;
932669b8 227 ptr += strlen(GIT_CAP_OFS_DELTA);
0437d991
CMN
228 continue;
229 }
230
231 /* We don't know this capability, so skip it */
232 ptr = strchr(ptr, ' ');
233 }
234
235 return GIT_SUCCESS;
236}
237
ecb6ca0e
CMN
238/*
239 * Since this is a network connection, we need to parse and store the
240 * pkt-lines at this stage and keep them there.
241 */
0ac2726f 242static int git_connect(git_transport *transport, int direction)
ecb6ca0e 243{
4e913309 244 transport_git *t = (transport_git *) transport;
ecb6ca0e
CMN
245 int error = GIT_SUCCESS;
246
0ac2726f 247 if (direction == GIT_DIR_PUSH)
ecb6ca0e
CMN
248 return git__throw(GIT_EINVALIDARGS, "Pushing is not supported with the git protocol");
249
0ac2726f 250 t->parent.direction = direction;
4e913309 251 error = git_vector_init(&t->refs, 16, NULL);
ecb6ca0e
CMN
252 if (error < GIT_SUCCESS)
253 goto cleanup;
254
255 /* Connect and ask for the refs */
4e913309 256 error = do_connect(t, transport->url);
ecb6ca0e
CMN
257 if (error < GIT_SUCCESS)
258 return error;
259
5da5321d 260 t->parent.connected = 1;
4e913309 261 error = store_refs(t);
0437d991
CMN
262 if (error < GIT_SUCCESS)
263 return error;
264
265 error = detect_caps(t);
ecb6ca0e
CMN
266
267cleanup:
268 if (error < GIT_SUCCESS) {
4e913309 269 git_vector_free(&t->refs);
ecb6ca0e
CMN
270 }
271
272 return error;
273}
274
275static int git_ls(git_transport *transport, git_headarray *array)
276{
4e913309
CMN
277 transport_git *t = (transport_git *) transport;
278 git_vector *refs = &t->refs;
ecb6ca0e
CMN
279 int len = 0;
280 unsigned int i;
281
282 array->heads = git__calloc(refs->length, sizeof(git_remote_head *));
283 if (array->heads == NULL)
284 return GIT_ENOMEM;
285
286 for (i = 0; i < refs->length; ++i) {
287 git_pkt *p = git_vector_get(refs, i);
288 if (p->type != GIT_PKT_REF)
289 continue;
290
291 ++len;
292 array->heads[i] = &(((git_pkt_ref *) p)->head);
293 }
294 array->len = len;
4e913309 295 t->heads = array->heads;
ecb6ca0e
CMN
296
297 return GIT_SUCCESS;
298}
299
0e20ba60
CMN
300static int git_send_wants(git_transport *transport, git_headarray *array)
301{
302 transport_git *t = (transport_git *) transport;
303
0437d991 304 return git_pkt_send_wants(array, &t->caps, t->socket);
0e20ba60
CMN
305}
306
7e1a94db 307static int git_send_have(git_transport *transport, git_oid *oid)
b4c90630
CMN
308{
309 transport_git *t = (transport_git *) transport;
310
7e1a94db
CMN
311 return git_pkt_send_have(oid, t->socket);
312}
313
427ca3d3 314static int git_negotiate_fetch(git_transport *transport, git_repository *repo, git_headarray *GIT_UNUSED(list))
22f65b9e
CMN
315{
316 transport_git *t = (transport_git *) transport;
317 git_revwalk *walk;
318 git_reference *ref;
319 git_strarray refs;
320 git_oid oid;
321 int error;
322 unsigned int i;
427ca3d3
CMN
323 char buff[128];
324 gitno_buffer buf;
325 GIT_UNUSED_ARG(list);
326
327 gitno_buffer_setup(&buf, buff, sizeof(buff), t->socket);
22f65b9e
CMN
328
329 error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
330 if (error < GIT_ERROR)
331 return git__rethrow(error, "Failed to list all references");
332
333 error = git_revwalk_new(&walk, repo);
334 if (error < GIT_ERROR) {
335 error = git__rethrow(error, "Failed to list all references");
336 goto cleanup;
337 }
338 git_revwalk_sorting(walk, GIT_SORT_TIME);
339
340 for (i = 0; i < refs.count; ++i) {
427ca3d3
CMN
341 /* No tags */
342 if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
343 continue;
344
22f65b9e
CMN
345 error = git_reference_lookup(&ref, repo, refs.strings[i]);
346 if (error < GIT_ERROR) {
347 error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]);
348 goto cleanup;
349 }
350
427ca3d3
CMN
351 if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
352 continue;
22f65b9e
CMN
353 error = git_revwalk_push(walk, git_reference_oid(ref));
354 if (error < GIT_ERROR) {
355 error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
356 goto cleanup;
357 }
358 }
359 git_strarray_free(&refs);
360
361 /*
362 * We don't support any kind of ACK extensions, so the negotiation
363 * boils down to sending what we have and listening for an ACK
364 * every once in a while.
365 */
366 i = 0;
367 while ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) {
368 error = git_pkt_send_have(&oid, t->socket);
369 i++;
427ca3d3
CMN
370 if (i % 20 == 0) {
371 const char *ptr = buf.data, *line_end;
372 git_pkt *pkt;
373 git_pkt_send_flush(t->socket);
374 while (1) {
74bd343a
CMN
375 /* Wait for max. 1 second */
376 error = gitno_select_in(&buf, 1, 0);
7adba5f4
CMN
377 if (error < GIT_SUCCESS) {
378 error = git__throw(GIT_EOSERR, "Error in select");
379 } else if (error == 0) {
380 /*
381 * Some servers don't respond immediately, so if this
382 * happens, we keep sending information until it
383 * answers.
384 */
385 break;
386 }
387
427ca3d3
CMN
388 error = gitno_recv(&buf);
389 if (error < GIT_SUCCESS) {
87d9869f
VM
390 error = git__rethrow(error, "Error receiving data");
391 goto cleanup;
427ca3d3
CMN
392 }
393 error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset);
394 if (error == GIT_ESHORTBUFFER)
395 continue;
396 if (error < GIT_SUCCESS) {
397 error = git__rethrow(error, "Failed to get answer");
398 goto cleanup;
399 }
400
401 gitno_consume(&buf, line_end);
402
403 if (pkt->type == GIT_PKT_ACK) {
404 error = GIT_SUCCESS;
405 goto done;
406 } else if (pkt->type == GIT_PKT_NAK) {
407 break;
408 } else {
409 error = git__throw(GIT_ERROR, "Got unexpected pkt type");
410 goto cleanup;
411 }
412 }
413 }
22f65b9e
CMN
414 }
415 if (error == GIT_EREVWALKOVER)
416 error = GIT_SUCCESS;
417
427ca3d3 418done:
22f65b9e
CMN
419 git_pkt_send_flush(t->socket);
420 git_pkt_send_done(t->socket);
421
422cleanup:
423 git_revwalk_free(walk);
424 return error;
425}
426
da290220
CMN
427static int git_send_flush(git_transport *transport)
428{
429 transport_git *t = (transport_git *) transport;
430
431 return git_pkt_send_flush(t->socket);
432}
433
7e1a94db
CMN
434static int git_send_done(git_transport *transport)
435{
436 transport_git *t = (transport_git *) transport;
437
438 return git_pkt_send_done(t->socket);
b4c90630
CMN
439}
440
9cf0f287 441static int store_pack(char **out, gitno_buffer *buf, git_repository *repo)
da290220
CMN
442{
443 git_filebuf file;
444 int error;
9cf0f287 445 char path[GIT_PATH_MAX], suff[] = "/objects/pack/pack-received\0";
da290220
CMN
446 off_t off = 0;
447
2f512ff8 448 strcpy(path, repo->path_repository);
da290220 449 off += strlen(repo->path_repository);
2f512ff8 450 strcat(path, suff);
932669b8 451 //memcpy(path + off, suff, GIT_PATH_MAX - off - strlen(suff) - 1);
da290220 452
932669b8 453 if (memcmp(buf->data, "PACK", strlen("PACK"))) {
7284c105
CMN
454 return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
455 }
456
da290220
CMN
457 error = git_filebuf_open(&file, path, GIT_FILEBUF_TEMPORARY);
458 if (error < GIT_SUCCESS)
459 goto cleanup;
460
461 while (1) {
427ca3d3 462 /* Part of the packfile has been received, don't loose it */
da290220
CMN
463 error = git_filebuf_write(&file, buf->data, buf->offset);
464 if (error < GIT_SUCCESS)
465 goto cleanup;
466
467 gitno_consume_n(buf, buf->offset);
427ca3d3
CMN
468 error = gitno_recv(buf);
469 if (error < GIT_SUCCESS)
470 goto cleanup;
471 if (error == 0) /* Orderly shutdown */
472 break;
da290220
CMN
473 }
474
9cf0f287 475 *out = git__strdup(file.path_lock);
48a65a07 476 if (*out == NULL) {
9cf0f287 477 error = GIT_ENOMEM;
48a65a07
CMN
478 goto cleanup;
479 }
9cf0f287 480
48a65a07
CMN
481 /* A bit dodgy, but we need to keep the pack at the temporary path */
482 error = git_filebuf_commit_at(&file, file.path_lock);
da290220
CMN
483cleanup:
484 if (error < GIT_SUCCESS)
485 git_filebuf_cleanup(&file);
48a65a07 486
da290220
CMN
487 return error;
488}
489
9cf0f287 490static int git_download_pack(char **out, git_transport *transport, git_repository *repo)
da290220
CMN
491{
492 transport_git *t = (transport_git *) transport;
7284c105 493 int s = t->socket, error = GIT_SUCCESS;
da290220
CMN
494 gitno_buffer buf;
495 char buffer[1024];
496 git_pkt *pkt;
497 const char *line_end, *ptr;
498
499 gitno_buffer_setup(&buf, buffer, sizeof(buffer), s);
500 /*
7284c105 501 * For now, we ignore everything and wait for the pack
da290220
CMN
502 */
503 while (1) {
504 error = gitno_recv(&buf);
505 if (error < GIT_SUCCESS)
506 return git__rethrow(GIT_EOSERR, "Failed to receive data");
48a65a07 507 if (error == 0) /* Orderly shutdown */
da290220
CMN
508 return GIT_SUCCESS;
509
510 ptr = buf.data;
511 /* Whilst we're searching for the pack */
7284c105 512 while (1) {
da290220
CMN
513 if (buf.offset == 0)
514 break;
515 error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset);
516 if (error == GIT_ESHORTBUFFER)
517 break;
518 if (error < GIT_SUCCESS)
519 return error;
520
427ca3d3 521 if (pkt->type == GIT_PKT_PACK)
7284c105 522 return store_pack(out, &buf, repo);
427ca3d3 523
da290220
CMN
524 /* For now we don't care about anything */
525 free(pkt);
7284c105 526 gitno_consume(&buf, line_end);
da290220 527 }
da290220 528 }
da290220
CMN
529}
530
531
ecb6ca0e
CMN
532static int git_close(git_transport *transport)
533{
4e913309
CMN
534 transport_git *t = (transport_git*) transport;
535 int s = t->socket;
ecb6ca0e
CMN
536 int error;
537
87d9869f 538 /* Can't do anything if there's an error, so don't bother checking */
4e913309 539 git_pkt_send_flush(s);
ecb6ca0e
CMN
540 error = close(s);
541 if (error < 0)
542 error = git__throw(GIT_EOSERR, "Failed to close socket");
543
544 return error;
545}
546
547static void git_free(git_transport *transport)
548{
4e913309
CMN
549 transport_git *t = (transport_git *) transport;
550 git_vector *refs = &t->refs;
ecb6ca0e
CMN
551 unsigned int i;
552
553 for (i = 0; i < refs->length; ++i) {
554 git_pkt *p = git_vector_get(refs, i);
be9fe679 555 git_pkt_free(p);
ecb6ca0e
CMN
556 }
557
558 git_vector_free(refs);
4e913309
CMN
559 free(t->heads);
560 free(t->parent.url);
561 free(t);
ecb6ca0e
CMN
562}
563
4e913309 564int git_transport_git(git_transport **out)
ecb6ca0e 565{
4e913309
CMN
566 transport_git *t;
567
568 t = git__malloc(sizeof(transport_git));
569 if (t == NULL)
570 return GIT_ENOMEM;
571
5da5321d
CMN
572 memset(t, 0x0, sizeof(transport_git));
573
4e913309
CMN
574 t->parent.connect = git_connect;
575 t->parent.ls = git_ls;
0e20ba60 576 t->parent.send_wants = git_send_wants;
7e1a94db 577 t->parent.send_have = git_send_have;
22f65b9e 578 t->parent.negotiate_fetch = git_negotiate_fetch;
da290220 579 t->parent.send_flush = git_send_flush;
7e1a94db 580 t->parent.send_done = git_send_done;
da290220 581 t->parent.download_pack = git_download_pack;
4e913309
CMN
582 t->parent.close = git_close;
583 t->parent.free = git_free;
584
585 *out = (git_transport *) t;
ecb6ca0e
CMN
586
587 return GIT_SUCCESS;
588}