smart_pkt.c 15.8 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3
 *
Vicent Marti committed
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
6 7
 */

Vicent Marti committed
8 9
#include "common.h"

10
#include "smart.h"
11
#include "util.h"
12
#include "netops.h"
13
#include "posix.h"
14
#include "str.h"
15
#include "oid.h"
16 17 18 19 20

#include "git2/types.h"
#include "git2/errors.h"
#include "git2/refs.h"
#include "git2/revwalk.h"
21

22 23
#include <ctype.h>

24 25 26 27 28 29 30 31
#define PKT_DONE_STR    "0009done\n"
#define PKT_FLUSH_STR   "0000"
#define PKT_HAVE_PREFIX "have "
#define PKT_WANT_PREFIX "want "

#define PKT_LEN_SIZE    4
#define PKT_MAX_SIZE    0xffff
#define PKT_MAX_WANTLEN (PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) + GIT_OID_MAX_HEXSIZE + 1)
32

33 34 35 36
static int flush_pkt(git_pkt **out)
{
	git_pkt *pkt;

37
	pkt = git__malloc(sizeof(git_pkt));
38
	GIT_ERROR_CHECK_ALLOC(pkt);
39 40 41 42

	pkt->type = GIT_PKT_FLUSH;
	*out = pkt;

43
	return 0;
44 45
}

46
/* the rest of the line will be useful for multi_ack and multi_ack_detailed */
47
static int ack_pkt(git_pkt **out, const char *line, size_t len)
Carlos Martín Nieto committed
48
{
49
	git_pkt_ack *pkt;
Carlos Martín Nieto committed
50

51
	pkt = git__calloc(1, sizeof(git_pkt_ack));
52
	GIT_ERROR_CHECK_ALLOC(pkt);
Carlos Martín Nieto committed
53
	pkt->type = GIT_PKT_ACK;
54

55 56 57 58 59
	if (git__prefixncmp(line, len, "ACK "))
		goto out_err;
	line += 4;
	len -= 4;

Edward Thomson committed
60
	if (len < GIT_OID_SHA1_HEXSIZE ||
61
	    git_oid__fromstr(&pkt->oid, line, GIT_OID_SHA1) < 0)
62
		goto out_err;
63 64
	line += GIT_OID_SHA1_HEXSIZE;
	len -= GIT_OID_SHA1_HEXSIZE;
65

66 67 68 69 70
	if (len && line[0] == ' ') {
		line++;
		len--;

		if (!git__prefixncmp(line, len, "continue"))
71
			pkt->status = GIT_ACK_CONTINUE;
72
		else if (!git__prefixncmp(line, len, "common"))
73
			pkt->status = GIT_ACK_COMMON;
74
		else if (!git__prefixncmp(line, len, "ready"))
75
			pkt->status = GIT_ACK_READY;
76 77
		else
			goto out_err;
78 79 80
	}

	*out = (git_pkt *) pkt;
Carlos Martín Nieto committed
81

82
	return 0;
83 84

out_err:
85
	git_error_set(GIT_ERROR_NET, "error parsing ACK pkt-line");
86 87
	git__free(pkt);
	return -1;
Carlos Martín Nieto committed
88 89
}

Carlos Martín Nieto committed
90
static int nak_pkt(git_pkt **out)
Carlos Martín Nieto committed
91 92 93
{
	git_pkt *pkt;

94
	pkt = git__malloc(sizeof(git_pkt));
95
	GIT_ERROR_CHECK_ALLOC(pkt);
Carlos Martín Nieto committed
96

Carlos Martín Nieto committed
97 98 99
	pkt->type = GIT_PKT_NAK;
	*out = pkt;

100
	return 0;
Carlos Martín Nieto committed
101 102
}

103 104 105
static int comment_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_comment *pkt;
106
	size_t alloclen;
107

108 109
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_comment), len);
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
110
	pkt = git__malloc(alloclen);
111
	GIT_ERROR_CHECK_ALLOC(pkt);
112 113 114 115 116 117 118

	pkt->type = GIT_PKT_COMMENT;
	memcpy(pkt->comment, line, len);
	pkt->comment[len] = '\0';

	*out = (git_pkt *) pkt;

119
	return 0;
120 121
}

122 123
static int err_pkt(git_pkt **out, const char *line, size_t len)
{
124
	git_pkt_err *pkt = NULL;
125
	size_t alloclen;
126 127

	/* Remove "ERR " from the line */
128 129
	if (git__prefixncmp(line, len, "ERR "))
		goto out_err;
130 131
	line += 4;
	len -= 4;
132

133 134
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
135
	pkt = git__malloc(alloclen);
136
	GIT_ERROR_CHECK_ALLOC(pkt);
137
	pkt->type = GIT_PKT_ERR;
138 139
	pkt->len = len;

140 141 142 143 144 145
	memcpy(pkt->error, line, len);
	pkt->error[len] = '\0';

	*out = (git_pkt *) pkt;

	return 0;
146 147

out_err:
148
	git_error_set(GIT_ERROR_NET, "error parsing ERR pkt-line");
149 150
	git__free(pkt);
	return -1;
151 152
}

153 154 155
static int data_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_data *pkt;
156
	size_t alloclen;
157 158 159

	line++;
	len--;
160

161
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
162
	pkt = git__malloc(alloclen);
163
	GIT_ERROR_CHECK_ALLOC(pkt);
164 165

	pkt->type = GIT_PKT_DATA;
166
	pkt->len = len;
167 168 169 170 171 172 173
	memcpy(pkt->data, line, len);

	*out = (git_pkt *) pkt;

	return 0;
}

174
static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len)
175 176
{
	git_pkt_progress *pkt;
177
	size_t alloclen;
178 179 180

	line++;
	len--;
181

182
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
183
	pkt = git__malloc(alloclen);
184
	GIT_ERROR_CHECK_ALLOC(pkt);
185 186

	pkt->type = GIT_PKT_PROGRESS;
187
	pkt->len = len;
188 189 190 191 192 193 194
	memcpy(pkt->data, line, len);

	*out = (git_pkt *) pkt;

	return 0;
}

195 196 197
static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_err *pkt;
198
	size_t alloc_len;
199 200 201

	line++;
	len--;
202

203 204
	GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(git_pkt_err), len);
	GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
205
	pkt = git__malloc(alloc_len);
206
	GIT_ERROR_CHECK_ALLOC(pkt);
207 208 209 210 211 212 213 214 215 216 217

	pkt->type = GIT_PKT_ERR;
	pkt->len = (int)len;
	memcpy(pkt->error, line, len);
	pkt->error[len] = '\0';

	*out = (git_pkt *)pkt;

	return 0;
}

Edward Thomson committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
static int set_data(
	git_pkt_parse_data *data,
	const char *line,
	size_t len)
{
	const char *caps, *format_str = NULL, *eos;
	size_t format_len;
	git_oid_t remote_oid_type;

	GIT_ASSERT_ARG(data);

	if ((caps = memchr(line, '\0', len)) != NULL) {
		caps++;

		if (strncmp(caps, "object-format=", CONST_STRLEN("object-format=")) == 0)
			format_str = caps + CONST_STRLEN("object-format=");
		else if ((format_str = strstr(caps, " object-format=")) != NULL)
			format_str += CONST_STRLEN(" object-format=");
	}

	if (format_str) {
		if ((eos = strchr(format_str, ' ')) == NULL)
			eos = strchr(format_str, '\0');

		GIT_ASSERT(eos);

		format_len = eos - format_str;

		if ((remote_oid_type = git_oid_type_fromstrn(format_str, format_len)) == 0) {
			git_error_set(GIT_ERROR_INVALID, "unknown remote object format '%.*s'", (int)format_len, format_str);
			return -1;
		}
	} else {
		remote_oid_type = GIT_OID_SHA1;
	}

	if (!data->oid_type) {
		data->oid_type = remote_oid_type;
	} else if (data->oid_type != remote_oid_type) {
		git_error_set(GIT_ERROR_INVALID,
		              "the local object format '%s' does not match the remote object format '%s'",
		              git_oid_type_name(data->oid_type),
		              git_oid_type_name(remote_oid_type));
		return -1;
	}

	return 0;
}

267
/*
268 269
 * Parse an other-ref line.
 */
Edward Thomson committed
270 271 272 273 274
static int ref_pkt(
	git_pkt **out,
	const char *line,
	size_t len,
	git_pkt_parse_data *data)
275 276
{
	git_pkt_ref *pkt;
Edward Thomson committed
277
	size_t alloclen, oid_hexsize;
278

279
	pkt = git__calloc(1, sizeof(git_pkt_ref));
280
	GIT_ERROR_CHECK_ALLOC(pkt);
281 282
	pkt->type = GIT_PKT_REF;

Edward Thomson committed
283 284 285 286 287 288 289 290 291
	/* Determine OID type from capabilities */
	if (!data->seen_capabilities && set_data(data, line, len) < 0)
		return -1;

	GIT_ASSERT(data->oid_type);
	oid_hexsize = git_oid_hexsize(data->oid_type);

	if (len < oid_hexsize ||
	    git_oid__fromstr(&pkt->head.oid, line, data->oid_type) < 0)
292
		goto out_err;
Edward Thomson committed
293 294
	line += oid_hexsize;
	len -= oid_hexsize;
295 296 297

	if (git__prefixncmp(line, len, " "))
		goto out_err;
Edward Thomson committed
298

299 300 301 302 303
	line++;
	len--;

	if (!len)
		goto out_err;
304

305 306
	if (line[len - 1] == '\n')
		--len;
307

308
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
309
	pkt->head.name = git__malloc(alloclen);
310
	GIT_ERROR_CHECK_ALLOC(pkt->head.name);
311

312 313
	memcpy(pkt->head.name, line, len);
	pkt->head.name[len] = '\0';
314

Edward Thomson committed
315 316 317 318 319 320 321 322
	if (strlen(pkt->head.name) < len) {
		if (!data->seen_capabilities)
			pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
		else
			goto out_err;
	}

	data->seen_capabilities = 1;
323

324 325 326
	*out = (git_pkt *)pkt;
	return 0;

327
out_err:
328
	git_error_set(GIT_ERROR_NET, "error parsing REF pkt-line");
329 330
	if (pkt)
		git__free(pkt->head.name);
331
	git__free(pkt);
332
	return -1;
333 334
}

335 336 337
static int ok_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_ok *pkt;
338
	size_t alloc_len;
339

340
	pkt = git__malloc(sizeof(*pkt));
341
	GIT_ERROR_CHECK_ALLOC(pkt);
342 343
	pkt->type = GIT_PKT_OK;

344 345 346 347 348
	if (git__prefixncmp(line, len, "ok "))
		goto out_err;
	line += 3;
	len -= 3;

349
	if (len && line[len - 1] == '\n')
350
		--len;
351

352
	GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
353
	pkt->ref = git__malloc(alloc_len);
354
	GIT_ERROR_CHECK_ALLOC(pkt->ref);
355 356 357 358 359 360

	memcpy(pkt->ref, line, len);
	pkt->ref[len] = '\0';

	*out = (git_pkt *)pkt;
	return 0;
361 362

out_err:
363
	git_error_set(GIT_ERROR_NET, "error parsing OK pkt-line");
364 365
	git__free(pkt);
	return -1;
366 367 368 369 370
}

static int ng_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_ng *pkt;
371
	const char *ptr, *eol;
372
	size_t alloclen;
373

374
	pkt = git__malloc(sizeof(*pkt));
375
	GIT_ERROR_CHECK_ALLOC(pkt);
376

377
	pkt->ref = NULL;
378 379
	pkt->type = GIT_PKT_NG;

380 381
	eol = line + len;

382
	if (git__prefixncmp(line, len, "ng "))
383
		goto out_err;
384
	line += 3;
385 386

	if (!(ptr = memchr(line, ' ', eol - line)))
387
		goto out_err;
388 389
	len = ptr - line;

390
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
391
	pkt->ref = git__malloc(alloclen);
392
	GIT_ERROR_CHECK_ALLOC(pkt->ref);
393 394 395 396 397

	memcpy(pkt->ref, line, len);
	pkt->ref[len] = '\0';

	line = ptr + 1;
398 399 400 401
	if (line >= eol)
		goto out_err;

	if (!(ptr = memchr(line, '\n', eol - line)))
402
		goto out_err;
403 404
	len = ptr - line;

405
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
406
	pkt->msg = git__malloc(alloclen);
407
	GIT_ERROR_CHECK_ALLOC(pkt->msg);
408 409 410 411 412 413

	memcpy(pkt->msg, line, len);
	pkt->msg[len] = '\0';

	*out = (git_pkt *)pkt;
	return 0;
414 415

out_err:
416
	git_error_set(GIT_ERROR_NET, "invalid packet line");
417 418 419
	git__free(pkt->ref);
	git__free(pkt);
	return -1;
420 421 422 423 424 425
}

static int unpack_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_unpack *pkt;

426
	pkt = git__malloc(sizeof(*pkt));
427
	GIT_ERROR_CHECK_ALLOC(pkt);
428
	pkt->type = GIT_PKT_UNPACK;
429 430

	if (!git__prefixncmp(line, len, "unpack ok"))
431 432 433 434 435 436 437 438
		pkt->unpack_ok = 1;
	else
		pkt->unpack_ok = 0;

	*out = (git_pkt *)pkt;
	return 0;
}

439
static int parse_len(size_t *out, const char *line, size_t linelen)
440 441
{
	char num[PKT_LEN_SIZE + 1];
442
	int i, k, error;
443
	int32_t len;
444 445
	const char *num_end;

446 447 448 449
	/* Not even enough for the length */
	if (linelen < PKT_LEN_SIZE)
		return GIT_EBUFS;

450 451 452 453
	memcpy(num, line, PKT_LEN_SIZE);
	num[PKT_LEN_SIZE] = '\0';

	for (i = 0; i < PKT_LEN_SIZE; ++i) {
454
		if (!isxdigit(num[i])) {
455 456 457 458 459 460
			/* Make sure there are no special characters before passing to error message */
			for (k = 0; k < PKT_LEN_SIZE; ++k) {
				if(!isprint(num[k])) {
					num[k] = '.';
				}
			}
461

462
			git_error_set(GIT_ERROR_NET, "invalid hex digit in length: '%s'", num);
463 464
			return -1;
		}
465 466
	}

467
	if ((error = git__strntol32(&len, num, PKT_LEN_SIZE, &num_end, 16)) < 0)
468
		return error;
469

470 471 472 473 474
	if (len < 0)
		return -1;

	*out = (size_t) len;
	return 0;
475 476
}

477
/*
478 479
 * As per the documentation, the syntax is:
 *
Vicent Marti committed
480 481 482
 * pkt-line	= data-pkt / flush-pkt
 * data-pkt	= pkt-len pkt-payload
 * pkt-len		= 4*(HEXDIG)
483
 * pkt-payload = (pkt-len -4)*(OCTET)
Vicent Marti committed
484
 * flush-pkt	= "0000"
485 486 487 488 489
 *
 * Which means that the first four bytes are the length of the line,
 * in ASCII hexadecimal (including itself)
 */

490
int git_pkt_parse_line(
Edward Thomson committed
491 492 493 494 495
	git_pkt **pkt,
	const char **endptr,
	const char *line,
	size_t linelen,
	git_pkt_parse_data *data)
496
{
497 498
	int error;
	size_t len;
499

500
	if ((error = parse_len(&len, line, linelen)) < 0) {
501
		/*
502 503 504 505
		 * If we fail to parse the length, it might be
		 * because the server is trying to send us the
		 * packfile already or because we do not yet have
		 * enough data.
506
		 */
507 508 509
		if (error == GIT_EBUFS)
			;
		else if (!git__prefixncmp(line, linelen, "PACK"))
510
			git_error_set(GIT_ERROR_NET, "unexpected pack file");
511
		else
512
			git_error_set(GIT_ERROR_NET, "bad packet length");
513
		return error;
514
	}
515

516
	/*
517 518
	 * Make sure there is enough in the buffer to satisfy
	 * this line.
519
	 */
520
	if (linelen < len)
521
		return GIT_EBUFS;
522

523 524 525 526 527 528 529 530
	/*
	 * The length has to be exactly 0 in case of a flush
	 * packet or greater than PKT_LEN_SIZE, as the decoded
	 * length includes its own encoded length of four bytes.
	 */
	if (len != 0 && len < PKT_LEN_SIZE)
		return GIT_ERROR;

531
	line += PKT_LEN_SIZE;
532
	/*
533 534 535
	 * The Git protocol does not specify empty lines as part
	 * of the protocol. Not knowing what to do with an empty
	 * line, we should return an error upon hitting one.
536
	 */
537
	if (len == PKT_LEN_SIZE) {
538
		git_error_set_str(GIT_ERROR_NET, "Invalid empty packet");
539
		return GIT_ERROR;
540 541
	}

542
	if (len == 0) { /* Flush pkt */
543 544
		*endptr = line;
		return flush_pkt(pkt);
545 546
	}

547
	len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
548

549
	if (*line == GIT_SIDE_BAND_DATA)
550
		error = data_pkt(pkt, line, len);
551
	else if (*line == GIT_SIDE_BAND_PROGRESS)
552
		error = sideband_progress_pkt(pkt, line, len);
553
	else if (*line == GIT_SIDE_BAND_ERROR)
554
		error = sideband_error_pkt(pkt, line, len);
555
	else if (!git__prefixncmp(line, len, "ACK"))
556
		error = ack_pkt(pkt, line, len);
557
	else if (!git__prefixncmp(line, len, "NAK"))
558
		error = nak_pkt(pkt);
559
	else if (!git__prefixncmp(line, len, "ERR"))
560
		error = err_pkt(pkt, line, len);
561
	else if (*line == '#')
562
		error = comment_pkt(pkt, line, len);
563
	else if (!git__prefixncmp(line, len, "ok"))
564
		error = ok_pkt(pkt, line, len);
565
	else if (!git__prefixncmp(line, len, "ng"))
566
		error = ng_pkt(pkt, line, len);
567
	else if (!git__prefixncmp(line, len, "unpack"))
568
		error = unpack_pkt(pkt, line, len);
Carlos Martín Nieto committed
569
	else
Edward Thomson committed
570
		error = ref_pkt(pkt, line, len, data);
571

572
	*endptr = line + len;
573

574
	return error;
575
}
576

577 578
void git_pkt_free(git_pkt *pkt)
{
579 580 581
	if (pkt == NULL) {
		return;
	}
582
	if (pkt->type == GIT_PKT_REF) {
583
		git_pkt_ref *p = (git_pkt_ref *) pkt;
584
		git__free(p->head.name);
585
		git__free(p->head.symref_target);
586 587
	}

588 589 590 591 592 593 594 595 596 597 598
	if (pkt->type == GIT_PKT_OK) {
		git_pkt_ok *p = (git_pkt_ok *) pkt;
		git__free(p->ref);
	}

	if (pkt->type == GIT_PKT_NG) {
		git_pkt_ng *p = (git_pkt_ng *) pkt;
		git__free(p->ref);
		git__free(p->msg);
	}

599
	git__free(pkt);
600 601
}

602
int git_pkt_buffer_flush(git_str *buf)
603
{
604
	return git_str_put(buf, PKT_FLUSH_STR, CONST_STRLEN(PKT_FLUSH_STR));
605 606
}

607 608 609 610 611
static int buffer_want_with_caps(
	const git_remote_head *head,
	transport_smart_caps *caps,
	git_oid_t oid_type,
	git_str *buf)
612
{
613
	git_str str = GIT_STR_INIT;
614
	char oid[GIT_OID_MAX_HEXSIZE];
Edward Thomson committed
615 616
	size_t oid_hexsize, len;

617
	oid_hexsize = git_oid_hexsize(oid_type);
Edward Thomson committed
618
	git_oid_fmt(oid, &head->oid);
619

620 621
	/* Prefer multi_ack_detailed */
	if (caps->multi_ack_detailed)
622
		git_str_puts(&str, GIT_CAP_MULTI_ACK_DETAILED " ");
623
	else if (caps->multi_ack)
624
		git_str_puts(&str, GIT_CAP_MULTI_ACK " ");
625

626 627
	/* Prefer side-band-64k if the server supports both */
	if (caps->side_band_64k)
628
		git_str_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
629
	else if (caps->side_band)
630
		git_str_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
631

632
	if (caps->include_tag)
633
		git_str_puts(&str, GIT_CAP_INCLUDE_TAG " ");
634

635
	if (caps->thin_pack)
636
		git_str_puts(&str, GIT_CAP_THIN_PACK " ");
637

638
	if (caps->ofs_delta)
639
		git_str_puts(&str, GIT_CAP_OFS_DELTA " ");
640

641
	if (git_str_oom(&str))
642
		return -1;
643

644
	if (str.size > (PKT_MAX_SIZE - (PKT_MAX_WANTLEN + 1))) {
645
		git_error_set(GIT_ERROR_NET,
646
			"tried to produce packet with invalid caps length %" PRIuZ, str.size);
647 648 649
		return -1;
	}

650 651 652 653
	len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
	      oid_hexsize + 1 /* NUL */ +
	      git_str_len(&str) + 1 /* LF */;

654 655
	git_str_grow_by(buf, len);
	git_str_printf(buf,
656
		"%04x%s%.*s %s\n", (unsigned int)len, PKT_WANT_PREFIX,
Edward Thomson committed
657
		(int)oid_hexsize, oid, git_str_cstr(&str));
658
	git_str_dispose(&str);
659

660
	GIT_ERROR_CHECK_ALLOC_STR(buf);
661 662

	return 0;
663 664
}

665 666 667 668 669
/*
 * All "want" packets have the same length and format, so what we do
 * is overwrite the OID each time.
 */

670 671 672 673
int git_pkt_buffer_wants(
	const git_remote_head * const *refs,
	size_t count,
	transport_smart_caps *caps,
674
	git_str *buf)
675
{
676
	const git_remote_head *head;
677 678 679 680 681 682 683 684 685 686 687 688 689 690
	char oid[GIT_OID_MAX_HEXSIZE];
	git_oid_t oid_type;
	size_t oid_hexsize, want_len, i = 0;

#ifdef GIT_EXPERIMENTAL_SHA256
	oid_type = count > 0 ? refs[0]->oid.type : GIT_OID_SHA1;
#else
	oid_type = GIT_OID_SHA1;
#endif

	oid_hexsize = git_oid_hexsize(oid_type);

	want_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
	      oid_hexsize + 1 /* LF */;
691 692

	if (caps->common) {
693 694
		for (; i < count; ++i) {
			head = refs[i];
695 696 697 698
			if (!head->local)
				break;
		}

699
		if (buffer_want_with_caps(refs[i], caps, oid_type, buf) < 0)
700
			return -1;
701 702 703 704

		i++;
	}

705 706
	for (; i < count; ++i) {
		head = refs[i];
Edward Thomson committed
707

708 709 710 711
		if (head->local)
			continue;

		git_oid_fmt(oid, &head->oid);
Edward Thomson committed
712

713 714 715
		git_str_printf(buf, "%04x%s%.*s\n",
			(unsigned int)want_len, PKT_WANT_PREFIX,
			(int)oid_hexsize, oid);
Edward Thomson committed
716

717
		if (git_str_oom(buf))
718
			return -1;
719 720 721 722 723
	}

	return git_pkt_buffer_flush(buf);
}

724
int git_pkt_buffer_have(git_oid *oid, git_str *buf)
725
{
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
	char oid_str[GIT_OID_MAX_HEXSIZE];
	git_oid_t oid_type;
	size_t oid_hexsize, have_len;

#ifdef GIT_EXPERIMENTAL_SHA256
	oid_type = oid->type;
#else
	oid_type = GIT_OID_SHA1;
#endif

	oid_hexsize = git_oid_hexsize(oid_type);
	have_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_HAVE_PREFIX) +
	      oid_hexsize + 1 /* LF */;

	git_oid_fmt(oid_str, oid);
	return git_str_printf(buf, "%04x%s%.*s\n",
		(unsigned int)have_len, PKT_HAVE_PREFIX,
		(int)oid_hexsize, oid_str);
744 745
}

746
int git_pkt_buffer_done(git_str *buf)
747
{
748
	return git_str_put(buf, PKT_DONE_STR, CONST_STRLEN(PKT_DONE_STR));
749
}