2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
16 #include "git2/types.h"
17 #include "git2/errors.h"
18 #include "git2/refs.h"
19 #include "git2/revwalk.h"
23 #define PKT_LEN_SIZE 4
24 static const char pkt_done_str
[] = "0009done\n";
25 static const char pkt_flush_str
[] = "0000";
26 static const char pkt_have_prefix
[] = "0032have ";
27 static const char pkt_want_prefix
[] = "0032want ";
29 static int flush_pkt(git_pkt
**out
)
33 pkt
= git__malloc(sizeof(git_pkt
));
34 GIT_ERROR_CHECK_ALLOC(pkt
);
36 pkt
->type
= GIT_PKT_FLUSH
;
42 /* the rest of the line will be useful for multi_ack and multi_ack_detailed */
43 static int ack_pkt(git_pkt
**out
, const char *line
, size_t len
)
47 pkt
= git__calloc(1, sizeof(git_pkt_ack
));
48 GIT_ERROR_CHECK_ALLOC(pkt
);
49 pkt
->type
= GIT_PKT_ACK
;
51 if (git__prefixncmp(line
, len
, "ACK "))
56 if (len
< GIT_OID_HEXSZ
|| git_oid_fromstr(&pkt
->oid
, line
) < 0)
58 line
+= GIT_OID_HEXSZ
;
61 if (len
&& line
[0] == ' ') {
65 if (!git__prefixncmp(line
, len
, "continue"))
66 pkt
->status
= GIT_ACK_CONTINUE
;
67 else if (!git__prefixncmp(line
, len
, "common"))
68 pkt
->status
= GIT_ACK_COMMON
;
69 else if (!git__prefixncmp(line
, len
, "ready"))
70 pkt
->status
= GIT_ACK_READY
;
75 *out
= (git_pkt
*) pkt
;
80 git_error_set(GIT_ERROR_NET
, "error parsing ACK pkt-line");
85 static int nak_pkt(git_pkt
**out
)
89 pkt
= git__malloc(sizeof(git_pkt
));
90 GIT_ERROR_CHECK_ALLOC(pkt
);
92 pkt
->type
= GIT_PKT_NAK
;
98 static int comment_pkt(git_pkt
**out
, const char *line
, size_t len
)
100 git_pkt_comment
*pkt
;
103 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, sizeof(git_pkt_comment
), len
);
104 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, 1);
105 pkt
= git__malloc(alloclen
);
106 GIT_ERROR_CHECK_ALLOC(pkt
);
108 pkt
->type
= GIT_PKT_COMMENT
;
109 memcpy(pkt
->comment
, line
, len
);
110 pkt
->comment
[len
] = '\0';
112 *out
= (git_pkt
*) pkt
;
117 static int err_pkt(git_pkt
**out
, const char *line
, size_t len
)
119 git_pkt_err
*pkt
= NULL
;
122 /* Remove "ERR " from the line */
123 if (git__prefixncmp(line
, len
, "ERR "))
128 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, sizeof(git_pkt_progress
), len
);
129 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, 1);
130 pkt
= git__malloc(alloclen
);
131 GIT_ERROR_CHECK_ALLOC(pkt
);
132 pkt
->type
= GIT_PKT_ERR
;
135 memcpy(pkt
->error
, line
, len
);
136 pkt
->error
[len
] = '\0';
138 *out
= (git_pkt
*) pkt
;
143 git_error_set(GIT_ERROR_NET
, "error parsing ERR pkt-line");
148 static int data_pkt(git_pkt
**out
, const char *line
, size_t len
)
156 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, sizeof(git_pkt_progress
), len
);
157 pkt
= git__malloc(alloclen
);
158 GIT_ERROR_CHECK_ALLOC(pkt
);
160 pkt
->type
= GIT_PKT_DATA
;
162 memcpy(pkt
->data
, line
, len
);
164 *out
= (git_pkt
*) pkt
;
169 static int sideband_progress_pkt(git_pkt
**out
, const char *line
, size_t len
)
171 git_pkt_progress
*pkt
;
177 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, sizeof(git_pkt_progress
), len
);
178 pkt
= git__malloc(alloclen
);
179 GIT_ERROR_CHECK_ALLOC(pkt
);
181 pkt
->type
= GIT_PKT_PROGRESS
;
183 memcpy(pkt
->data
, line
, len
);
185 *out
= (git_pkt
*) pkt
;
190 static int sideband_error_pkt(git_pkt
**out
, const char *line
, size_t len
)
198 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, sizeof(git_pkt_err
), len
);
199 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, alloc_len
, 1);
200 pkt
= git__malloc(alloc_len
);
201 GIT_ERROR_CHECK_ALLOC(pkt
);
203 pkt
->type
= GIT_PKT_ERR
;
205 memcpy(pkt
->error
, line
, len
);
206 pkt
->error
[len
] = '\0';
208 *out
= (git_pkt
*)pkt
;
214 * Parse an other-ref line.
216 static int ref_pkt(git_pkt
**out
, const char *line
, size_t len
)
221 pkt
= git__calloc(1, sizeof(git_pkt_ref
));
222 GIT_ERROR_CHECK_ALLOC(pkt
);
223 pkt
->type
= GIT_PKT_REF
;
225 if (len
< GIT_OID_HEXSZ
|| git_oid_fromstr(&pkt
->head
.oid
, line
) < 0)
227 line
+= GIT_OID_HEXSZ
;
228 len
-= GIT_OID_HEXSZ
;
230 if (git__prefixncmp(line
, len
, " "))
238 if (line
[len
- 1] == '\n')
241 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, len
, 1);
242 pkt
->head
.name
= git__malloc(alloclen
);
243 GIT_ERROR_CHECK_ALLOC(pkt
->head
.name
);
245 memcpy(pkt
->head
.name
, line
, len
);
246 pkt
->head
.name
[len
] = '\0';
248 if (strlen(pkt
->head
.name
) < len
)
249 pkt
->capabilities
= strchr(pkt
->head
.name
, '\0') + 1;
251 *out
= (git_pkt
*)pkt
;
255 git_error_set(GIT_ERROR_NET
, "error parsing REF pkt-line");
257 git__free(pkt
->head
.name
);
262 static int ok_pkt(git_pkt
**out
, const char *line
, size_t len
)
267 pkt
= git__malloc(sizeof(*pkt
));
268 GIT_ERROR_CHECK_ALLOC(pkt
);
269 pkt
->type
= GIT_PKT_OK
;
271 if (git__prefixncmp(line
, len
, "ok "))
276 if (len
&& line
[len
- 1] == '\n')
279 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, len
, 1);
280 pkt
->ref
= git__malloc(alloc_len
);
281 GIT_ERROR_CHECK_ALLOC(pkt
->ref
);
283 memcpy(pkt
->ref
, line
, len
);
284 pkt
->ref
[len
] = '\0';
286 *out
= (git_pkt
*)pkt
;
290 git_error_set(GIT_ERROR_NET
, "error parsing OK pkt-line");
295 static int ng_pkt(git_pkt
**out
, const char *line
, size_t len
)
298 const char *ptr
, *eol
;
301 pkt
= git__malloc(sizeof(*pkt
));
302 GIT_ERROR_CHECK_ALLOC(pkt
);
305 pkt
->type
= GIT_PKT_NG
;
309 if (git__prefixncmp(line
, len
, "ng "))
313 if (!(ptr
= memchr(line
, ' ', eol
- line
)))
317 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, len
, 1);
318 pkt
->ref
= git__malloc(alloclen
);
319 GIT_ERROR_CHECK_ALLOC(pkt
->ref
);
321 memcpy(pkt
->ref
, line
, len
);
322 pkt
->ref
[len
] = '\0';
328 if (!(ptr
= memchr(line
, '\n', eol
- line
)))
332 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, len
, 1);
333 pkt
->msg
= git__malloc(alloclen
);
334 GIT_ERROR_CHECK_ALLOC(pkt
->msg
);
336 memcpy(pkt
->msg
, line
, len
);
337 pkt
->msg
[len
] = '\0';
339 *out
= (git_pkt
*)pkt
;
343 git_error_set(GIT_ERROR_NET
, "invalid packet line");
349 static int unpack_pkt(git_pkt
**out
, const char *line
, size_t len
)
353 pkt
= git__malloc(sizeof(*pkt
));
354 GIT_ERROR_CHECK_ALLOC(pkt
);
355 pkt
->type
= GIT_PKT_UNPACK
;
357 if (!git__prefixncmp(line
, len
, "unpack ok"))
362 *out
= (git_pkt
*)pkt
;
366 static int parse_len(size_t *out
, const char *line
, size_t linelen
)
368 char num
[PKT_LEN_SIZE
+ 1];
373 /* Not even enough for the length */
374 if (linelen
< PKT_LEN_SIZE
)
377 memcpy(num
, line
, PKT_LEN_SIZE
);
378 num
[PKT_LEN_SIZE
] = '\0';
380 for (i
= 0; i
< PKT_LEN_SIZE
; ++i
) {
381 if (!isxdigit(num
[i
])) {
382 /* Make sure there are no special characters before passing to error message */
383 for (k
= 0; k
< PKT_LEN_SIZE
; ++k
) {
384 if(!isprint(num
[k
])) {
389 git_error_set(GIT_ERROR_NET
, "invalid hex digit in length: '%s'", num
);
394 if ((error
= git__strntol32(&len
, num
, PKT_LEN_SIZE
, &num_end
, 16)) < 0)
405 * As per the documentation, the syntax is:
407 * pkt-line = data-pkt / flush-pkt
408 * data-pkt = pkt-len pkt-payload
409 * pkt-len = 4*(HEXDIG)
410 * pkt-payload = (pkt-len -4)*(OCTET)
413 * Which means that the first four bytes are the length of the line,
414 * in ASCII hexadecimal (including itself)
417 int git_pkt_parse_line(
418 git_pkt
**pkt
, const char **endptr
, const char *line
, size_t linelen
)
423 if ((error
= parse_len(&len
, line
, linelen
)) < 0) {
425 * If we fail to parse the length, it might be
426 * because the server is trying to send us the
427 * packfile already or because we do not yet have
430 if (error
== GIT_EBUFS
)
432 else if (!git__prefixncmp(line
, linelen
, "PACK"))
433 git_error_set(GIT_ERROR_NET
, "unexpected pack file");
435 git_error_set(GIT_ERROR_NET
, "bad packet length");
440 * Make sure there is enough in the buffer to satisfy
447 * The length has to be exactly 0 in case of a flush
448 * packet or greater than PKT_LEN_SIZE, as the decoded
449 * length includes its own encoded length of four bytes.
451 if (len
!= 0 && len
< PKT_LEN_SIZE
)
454 line
+= PKT_LEN_SIZE
;
456 * The Git protocol does not specify empty lines as part
457 * of the protocol. Not knowing what to do with an empty
458 * line, we should return an error upon hitting one.
460 if (len
== PKT_LEN_SIZE
) {
461 git_error_set_str(GIT_ERROR_NET
, "Invalid empty packet");
465 if (len
== 0) { /* Flush pkt */
467 return flush_pkt(pkt
);
470 len
-= PKT_LEN_SIZE
; /* the encoded length includes its own size */
472 if (*line
== GIT_SIDE_BAND_DATA
)
473 error
= data_pkt(pkt
, line
, len
);
474 else if (*line
== GIT_SIDE_BAND_PROGRESS
)
475 error
= sideband_progress_pkt(pkt
, line
, len
);
476 else if (*line
== GIT_SIDE_BAND_ERROR
)
477 error
= sideband_error_pkt(pkt
, line
, len
);
478 else if (!git__prefixncmp(line
, len
, "ACK"))
479 error
= ack_pkt(pkt
, line
, len
);
480 else if (!git__prefixncmp(line
, len
, "NAK"))
481 error
= nak_pkt(pkt
);
482 else if (!git__prefixncmp(line
, len
, "ERR"))
483 error
= err_pkt(pkt
, line
, len
);
484 else if (*line
== '#')
485 error
= comment_pkt(pkt
, line
, len
);
486 else if (!git__prefixncmp(line
, len
, "ok"))
487 error
= ok_pkt(pkt
, line
, len
);
488 else if (!git__prefixncmp(line
, len
, "ng"))
489 error
= ng_pkt(pkt
, line
, len
);
490 else if (!git__prefixncmp(line
, len
, "unpack"))
491 error
= unpack_pkt(pkt
, line
, len
);
493 error
= ref_pkt(pkt
, line
, len
);
495 *endptr
= line
+ len
;
500 void git_pkt_free(git_pkt
*pkt
)
505 if (pkt
->type
== GIT_PKT_REF
) {
506 git_pkt_ref
*p
= (git_pkt_ref
*) pkt
;
507 git__free(p
->head
.name
);
508 git__free(p
->head
.symref_target
);
511 if (pkt
->type
== GIT_PKT_OK
) {
512 git_pkt_ok
*p
= (git_pkt_ok
*) pkt
;
516 if (pkt
->type
== GIT_PKT_NG
) {
517 git_pkt_ng
*p
= (git_pkt_ng
*) pkt
;
525 int git_pkt_buffer_flush(git_str
*buf
)
527 return git_str_put(buf
, pkt_flush_str
, strlen(pkt_flush_str
));
530 static int buffer_want_with_caps(const git_remote_head
*head
, transport_smart_caps
*caps
, git_str
*buf
)
532 git_str str
= GIT_STR_INIT
;
533 char oid
[GIT_OID_HEXSZ
+1] = {0};
536 /* Prefer multi_ack_detailed */
537 if (caps
->multi_ack_detailed
)
538 git_str_puts(&str
, GIT_CAP_MULTI_ACK_DETAILED
" ");
539 else if (caps
->multi_ack
)
540 git_str_puts(&str
, GIT_CAP_MULTI_ACK
" ");
542 /* Prefer side-band-64k if the server supports both */
543 if (caps
->side_band_64k
)
544 git_str_printf(&str
, "%s ", GIT_CAP_SIDE_BAND_64K
);
545 else if (caps
->side_band
)
546 git_str_printf(&str
, "%s ", GIT_CAP_SIDE_BAND
);
548 if (caps
->include_tag
)
549 git_str_puts(&str
, GIT_CAP_INCLUDE_TAG
" ");
552 git_str_puts(&str
, GIT_CAP_THIN_PACK
" ");
555 git_str_puts(&str
, GIT_CAP_OFS_DELTA
" ");
557 if (git_str_oom(&str
))
560 len
= strlen("XXXXwant ") + GIT_OID_HEXSZ
+ 1 /* NUL */ +
561 git_str_len(&str
) + 1 /* LF */;
564 git_error_set(GIT_ERROR_NET
,
565 "tried to produce packet with invalid length %" PRIuZ
, len
);
569 git_str_grow_by(buf
, len
);
570 git_oid_fmt(oid
, &head
->oid
);
572 "%04xwant %s %s\n", (unsigned int)len
, oid
, git_str_cstr(&str
));
573 git_str_dispose(&str
);
575 GIT_ERROR_CHECK_ALLOC_STR(buf
);
581 * All "want" packets have the same length and format, so what we do
582 * is overwrite the OID each time.
585 int git_pkt_buffer_wants(
586 const git_remote_head
* const *refs
,
588 transport_smart_caps
*caps
,
592 const git_remote_head
*head
;
595 for (; i
< count
; ++i
) {
601 if (buffer_want_with_caps(refs
[i
], caps
, buf
) < 0)
607 for (; i
< count
; ++i
) {
608 char oid
[GIT_OID_HEXSZ
];
614 git_oid_fmt(oid
, &head
->oid
);
615 git_str_put(buf
, pkt_want_prefix
, strlen(pkt_want_prefix
));
616 git_str_put(buf
, oid
, GIT_OID_HEXSZ
);
617 git_str_putc(buf
, '\n');
618 if (git_str_oom(buf
))
622 return git_pkt_buffer_flush(buf
);
625 int git_pkt_buffer_have(git_oid
*oid
, git_str
*buf
)
627 char oidhex
[GIT_OID_HEXSZ
+ 1];
629 memset(oidhex
, 0x0, sizeof(oidhex
));
630 git_oid_fmt(oidhex
, oid
);
631 return git_str_printf(buf
, "%s%s\n", pkt_have_prefix
, oidhex
);
634 int git_pkt_buffer_done(git_str
*buf
)
636 return git_str_puts(buf
, pkt_done_str
);