smart_pkt.c 18.4 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 "posix.h"
13
#include "str.h"
14
#include "oid.h"
15 16 17 18 19

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

21 22
#include <ctype.h>

23 24 25 26 27 28 29 30
#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)
31

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

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

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

42
	return 0;
43 44
}

45
/* the rest of the line will be useful for multi_ack and multi_ack_detailed */
46 47 48 49 50
static int ack_pkt(
	git_pkt **out,
	const char *line,
	size_t len,
	git_pkt_parse_data *data)
Carlos Martín Nieto committed
51
{
52
	git_pkt_ack *pkt;
53
	size_t oid_hexsize = git_oid_hexsize(data->oid_type);
Carlos Martín Nieto committed
54

55 56
	GIT_ASSERT(data && data->oid_type);

57
	pkt = git__calloc(1, sizeof(git_pkt_ack));
58
	GIT_ERROR_CHECK_ALLOC(pkt);
Carlos Martín Nieto committed
59
	pkt->type = GIT_PKT_ACK;
60

61 62 63 64 65
	if (git__prefixncmp(line, len, "ACK "))
		goto out_err;
	line += 4;
	len -= 4;

66 67
	if (len < oid_hexsize ||
	    git_oid__fromstr(&pkt->oid, line, data->oid_type) < 0)
68
		goto out_err;
69 70
	line += oid_hexsize;
	len -= oid_hexsize;
71

72 73 74 75 76
	if (len && line[0] == ' ') {
		line++;
		len--;

		if (!git__prefixncmp(line, len, "continue"))
77
			pkt->status = GIT_ACK_CONTINUE;
78
		else if (!git__prefixncmp(line, len, "common"))
79
			pkt->status = GIT_ACK_COMMON;
80
		else if (!git__prefixncmp(line, len, "ready"))
81
			pkt->status = GIT_ACK_READY;
82 83
		else
			goto out_err;
84 85 86
	}

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

88
	return 0;
89 90

out_err:
91
	git_error_set(GIT_ERROR_NET, "error parsing ACK pkt-line");
92 93
	git__free(pkt);
	return -1;
Carlos Martín Nieto committed
94 95
}

Carlos Martín Nieto committed
96
static int nak_pkt(git_pkt **out)
Carlos Martín Nieto committed
97 98 99
{
	git_pkt *pkt;

100
	pkt = git__malloc(sizeof(git_pkt));
101
	GIT_ERROR_CHECK_ALLOC(pkt);
Carlos Martín Nieto committed
102

Carlos Martín Nieto committed
103 104 105
	pkt->type = GIT_PKT_NAK;
	*out = pkt;

106
	return 0;
Carlos Martín Nieto committed
107 108
}

109 110 111
static int comment_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_comment *pkt;
112
	size_t alloclen;
113

114 115
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_comment), len);
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
116
	pkt = git__malloc(alloclen);
117
	GIT_ERROR_CHECK_ALLOC(pkt);
118 119 120 121 122 123 124

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

	*out = (git_pkt *) pkt;

125
	return 0;
126 127
}

128 129
static int err_pkt(git_pkt **out, const char *line, size_t len)
{
130
	git_pkt_err *pkt = NULL;
131
	size_t alloclen;
132 133

	/* Remove "ERR " from the line */
134 135
	if (git__prefixncmp(line, len, "ERR "))
		goto out_err;
136 137
	line += 4;
	len -= 4;
138

139 140
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
141
	pkt = git__malloc(alloclen);
142
	GIT_ERROR_CHECK_ALLOC(pkt);
143
	pkt->type = GIT_PKT_ERR;
144 145
	pkt->len = len;

146 147 148 149 150 151
	memcpy(pkt->error, line, len);
	pkt->error[len] = '\0';

	*out = (git_pkt *) pkt;

	return 0;
152 153

out_err:
154
	git_error_set(GIT_ERROR_NET, "error parsing ERR pkt-line");
155 156
	git__free(pkt);
	return -1;
157 158
}

159 160 161
static int data_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_data *pkt;
162
	size_t alloclen;
163 164 165

	line++;
	len--;
166

167
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
168
	pkt = git__malloc(alloclen);
169
	GIT_ERROR_CHECK_ALLOC(pkt);
170 171

	pkt->type = GIT_PKT_DATA;
172
	pkt->len = len;
173 174 175 176 177 178 179
	memcpy(pkt->data, line, len);

	*out = (git_pkt *) pkt;

	return 0;
}

180
static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len)
181 182
{
	git_pkt_progress *pkt;
183
	size_t alloclen;
184 185 186

	line++;
	len--;
187

188
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
189
	pkt = git__malloc(alloclen);
190
	GIT_ERROR_CHECK_ALLOC(pkt);
191 192

	pkt->type = GIT_PKT_PROGRESS;
193
	pkt->len = len;
194 195 196 197 198 199 200
	memcpy(pkt->data, line, len);

	*out = (git_pkt *) pkt;

	return 0;
}

201 202 203
static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_err *pkt;
204
	size_t alloc_len;
205 206 207

	line++;
	len--;
208

209 210
	GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(git_pkt_err), len);
	GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
211
	pkt = git__malloc(alloc_len);
212
	GIT_ERROR_CHECK_ALLOC(pkt);
213 214 215 216 217 218 219 220 221 222 223

	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
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 267 268 269 270 271 272
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;
}

273
/*
274 275
 * Parse an other-ref line.
 */
Edward Thomson committed
276 277 278 279 280
static int ref_pkt(
	git_pkt **out,
	const char *line,
	size_t len,
	git_pkt_parse_data *data)
281 282
{
	git_pkt_ref *pkt;
Edward Thomson committed
283
	size_t alloclen, oid_hexsize;
284

285
	pkt = git__calloc(1, sizeof(git_pkt_ref));
286
	GIT_ERROR_CHECK_ALLOC(pkt);
287 288
	pkt->type = GIT_PKT_REF;

Edward Thomson committed
289 290 291 292 293 294 295 296 297
	/* 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)
298
		goto out_err;
Edward Thomson committed
299 300
	line += oid_hexsize;
	len -= oid_hexsize;
301 302 303

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

305 306 307 308 309
	line++;
	len--;

	if (!len)
		goto out_err;
310

311 312
	if (line[len - 1] == '\n')
		--len;
313

314
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
315
	pkt->head.name = git__malloc(alloclen);
316
	GIT_ERROR_CHECK_ALLOC(pkt->head.name);
317

318 319
	memcpy(pkt->head.name, line, len);
	pkt->head.name[len] = '\0';
320

Edward Thomson committed
321 322 323 324 325 326 327 328
	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;
329

330 331 332
	*out = (git_pkt *)pkt;
	return 0;

333
out_err:
334
	git_error_set(GIT_ERROR_NET, "error parsing REF pkt-line");
335 336
	if (pkt)
		git__free(pkt->head.name);
337
	git__free(pkt);
338
	return -1;
339 340
}

341 342 343
static int ok_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_ok *pkt;
344
	size_t alloc_len;
345

346
	pkt = git__malloc(sizeof(*pkt));
347
	GIT_ERROR_CHECK_ALLOC(pkt);
348 349
	pkt->type = GIT_PKT_OK;

350 351 352 353 354
	if (git__prefixncmp(line, len, "ok "))
		goto out_err;
	line += 3;
	len -= 3;

355
	if (len && line[len - 1] == '\n')
356
		--len;
357

358
	GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
359
	pkt->ref = git__malloc(alloc_len);
360
	GIT_ERROR_CHECK_ALLOC(pkt->ref);
361 362 363 364 365 366

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

	*out = (git_pkt *)pkt;
	return 0;
367 368

out_err:
369
	git_error_set(GIT_ERROR_NET, "error parsing OK pkt-line");
370 371
	git__free(pkt);
	return -1;
372 373 374 375 376
}

static int ng_pkt(git_pkt **out, const char *line, size_t len)
{
	git_pkt_ng *pkt;
377
	const char *ptr, *eol;
378
	size_t alloclen;
379

380
	pkt = git__malloc(sizeof(*pkt));
381
	GIT_ERROR_CHECK_ALLOC(pkt);
382

383
	pkt->ref = NULL;
384 385
	pkt->type = GIT_PKT_NG;

386 387
	eol = line + len;

388
	if (git__prefixncmp(line, len, "ng "))
389
		goto out_err;
390
	line += 3;
391 392

	if (!(ptr = memchr(line, ' ', eol - line)))
393
		goto out_err;
394 395
	len = ptr - line;

396
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
397
	pkt->ref = git__malloc(alloclen);
398
	GIT_ERROR_CHECK_ALLOC(pkt->ref);
399 400 401 402 403

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

	line = ptr + 1;
404 405 406 407
	if (line >= eol)
		goto out_err;

	if (!(ptr = memchr(line, '\n', eol - line)))
408
		goto out_err;
409 410
	len = ptr - line;

411
	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
412
	pkt->msg = git__malloc(alloclen);
413
	GIT_ERROR_CHECK_ALLOC(pkt->msg);
414 415 416 417 418 419

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

	*out = (git_pkt *)pkt;
	return 0;
420 421

out_err:
422
	git_error_set(GIT_ERROR_NET, "invalid packet line");
423 424 425
	git__free(pkt->ref);
	git__free(pkt);
	return -1;
426 427 428 429 430 431
}

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

432
	pkt = git__malloc(sizeof(*pkt));
433
	GIT_ERROR_CHECK_ALLOC(pkt);
434
	pkt->type = GIT_PKT_UNPACK;
435 436

	if (!git__prefixncmp(line, len, "unpack ok"))
437 438 439 440 441 442 443 444
		pkt->unpack_ok = 1;
	else
		pkt->unpack_ok = 0;

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

445 446 447 448 449
static int shallow_pkt(
	git_pkt **out,
	const char *line,
	size_t len,
	git_pkt_parse_data *data)
450 451
{
	git_pkt_shallow *pkt;
452
	size_t oid_hexsize = git_oid_hexsize(data->oid_type);
453

454 455
	GIT_ASSERT(data && data->oid_type);

456 457 458 459
	pkt = git__calloc(1, sizeof(git_pkt_shallow));
	GIT_ERROR_CHECK_ALLOC(pkt);

	pkt->type = GIT_PKT_SHALLOW;
460 461 462 463 464 465

	if (git__prefixncmp(line, len, "shallow "))
		goto out_err;

	line += 8;
	len -= 8;
466

467 468
	if (len != oid_hexsize)
		goto out_err;
469

470 471 472 473 474
	git_oid__fromstr(&pkt->oid, line, data->oid_type);
	line += oid_hexsize + 1;
	len -= oid_hexsize + 1;

	*out = (git_pkt *)pkt;
475 476

	return 0;
477 478 479 480 481

out_err:
	git_error_set(GIT_ERROR_NET, "invalid packet line");
	git__free(pkt);
	return -1;
482 483
}

484 485 486 487 488
static int unshallow_pkt(
	git_pkt **out,
	const char *line,
	size_t len,
	git_pkt_parse_data *data)
489 490
{
	git_pkt_shallow *pkt;
491
	size_t oid_hexsize = git_oid_hexsize(data->oid_type);
492

493 494
	GIT_ASSERT(data && data->oid_type);

495 496 497 498
	pkt = git__calloc(1, sizeof(git_pkt_shallow));
	GIT_ERROR_CHECK_ALLOC(pkt);

	pkt->type = GIT_PKT_UNSHALLOW;
499 500 501 502 503 504

	if (git__prefixncmp(line, len, "unshallow "))
		goto out_err;

	line += 10;
	len -= 10;
505

506 507 508 509 510 511
	if (len != oid_hexsize)
		goto out_err;

	git_oid__fromstr(&pkt->oid, line, data->oid_type);
	line += oid_hexsize + 1;
	len -= oid_hexsize + 1;
512 513 514 515

	*out = (git_pkt *) pkt;

	return 0;
516 517 518 519 520

out_err:
	git_error_set(GIT_ERROR_NET, "invalid packet line");
	git__free(pkt);
	return -1;
521 522
}

523
static int parse_len(size_t *out, const char *line, size_t linelen)
524 525
{
	char num[PKT_LEN_SIZE + 1];
526
	int i, k, error;
527
	int32_t len;
528 529
	const char *num_end;

530 531 532 533
	/* Not even enough for the length */
	if (linelen < PKT_LEN_SIZE)
		return GIT_EBUFS;

534 535 536 537
	memcpy(num, line, PKT_LEN_SIZE);
	num[PKT_LEN_SIZE] = '\0';

	for (i = 0; i < PKT_LEN_SIZE; ++i) {
538
		if (!isxdigit(num[i])) {
539 540 541 542 543 544
			/* 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] = '.';
				}
			}
545

546
			git_error_set(GIT_ERROR_NET, "invalid hex digit in length: '%s'", num);
547 548
			return -1;
		}
549 550
	}

551
	if ((error = git__strntol32(&len, num, PKT_LEN_SIZE, &num_end, 16)) < 0)
552
		return error;
553

554 555 556 557 558
	if (len < 0)
		return -1;

	*out = (size_t) len;
	return 0;
559 560
}

561
/*
562 563
 * As per the documentation, the syntax is:
 *
Vicent Marti committed
564 565 566
 * pkt-line	= data-pkt / flush-pkt
 * data-pkt	= pkt-len pkt-payload
 * pkt-len		= 4*(HEXDIG)
567
 * pkt-payload = (pkt-len -4)*(OCTET)
Vicent Marti committed
568
 * flush-pkt	= "0000"
569 570 571 572 573
 *
 * Which means that the first four bytes are the length of the line,
 * in ASCII hexadecimal (including itself)
 */

574
int git_pkt_parse_line(
Edward Thomson committed
575 576 577 578 579
	git_pkt **pkt,
	const char **endptr,
	const char *line,
	size_t linelen,
	git_pkt_parse_data *data)
580
{
581 582
	int error;
	size_t len;
583

584
	if ((error = parse_len(&len, line, linelen)) < 0) {
585
		/*
586 587 588 589
		 * 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.
590
		 */
591 592 593
		if (error == GIT_EBUFS)
			;
		else if (!git__prefixncmp(line, linelen, "PACK"))
594
			git_error_set(GIT_ERROR_NET, "unexpected pack file");
595
		else
596
			git_error_set(GIT_ERROR_NET, "bad packet length");
597
		return error;
598
	}
599

600
	/*
601 602
	 * Make sure there is enough in the buffer to satisfy
	 * this line.
603
	 */
604
	if (linelen < len)
605
		return GIT_EBUFS;
606

607 608 609 610 611 612 613 614
	/*
	 * 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;

615
	line += PKT_LEN_SIZE;
616
	/*
617 618 619
	 * 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.
620
	 */
621
	if (len == PKT_LEN_SIZE) {
622
		git_error_set_str(GIT_ERROR_NET, "Invalid empty packet");
623
		return GIT_ERROR;
624 625
	}

626
	if (len == 0) { /* Flush pkt */
627 628
		*endptr = line;
		return flush_pkt(pkt);
629 630
	}

631
	len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
632

633
	if (*line == GIT_SIDE_BAND_DATA)
634
		error = data_pkt(pkt, line, len);
635
	else if (*line == GIT_SIDE_BAND_PROGRESS)
636
		error = sideband_progress_pkt(pkt, line, len);
637
	else if (*line == GIT_SIDE_BAND_ERROR)
638
		error = sideband_error_pkt(pkt, line, len);
639
	else if (!git__prefixncmp(line, len, "ACK"))
640
		error = ack_pkt(pkt, line, len, data);
641
	else if (!git__prefixncmp(line, len, "NAK"))
642
		error = nak_pkt(pkt);
643
	else if (!git__prefixncmp(line, len, "ERR"))
644
		error = err_pkt(pkt, line, len);
645
	else if (*line == '#')
646
		error = comment_pkt(pkt, line, len);
647
	else if (!git__prefixncmp(line, len, "ok"))
648
		error = ok_pkt(pkt, line, len);
649
	else if (!git__prefixncmp(line, len, "ng"))
650
		error = ng_pkt(pkt, line, len);
651
	else if (!git__prefixncmp(line, len, "unpack"))
652
		error = unpack_pkt(pkt, line, len);
653
	else if (!git__prefixcmp(line, "shallow"))
654
		error = shallow_pkt(pkt, line, len, data);
655
	else if (!git__prefixcmp(line, "unshallow"))
656
		error = unshallow_pkt(pkt, line, len, data);
Carlos Martín Nieto committed
657
	else
Edward Thomson committed
658
		error = ref_pkt(pkt, line, len, data);
659

660
	*endptr = line + len;
661

662
	return error;
663
}
664

665 666
void git_pkt_free(git_pkt *pkt)
{
667 668 669
	if (pkt == NULL) {
		return;
	}
670
	if (pkt->type == GIT_PKT_REF) {
671
		git_pkt_ref *p = (git_pkt_ref *) pkt;
672
		git__free(p->head.name);
673
		git__free(p->head.symref_target);
674 675
	}

676 677 678 679 680 681 682 683 684 685 686
	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);
	}

687
	git__free(pkt);
688 689
}

690
int git_pkt_buffer_flush(git_str *buf)
691
{
692
	return git_str_put(buf, PKT_FLUSH_STR, CONST_STRLEN(PKT_FLUSH_STR));
693 694
}

695 696 697 698 699
static int buffer_want_with_caps(
	const git_remote_head *head,
	transport_smart_caps *caps,
	git_oid_t oid_type,
	git_str *buf)
700
{
701
	git_str str = GIT_STR_INIT;
702
	char oid[GIT_OID_MAX_HEXSIZE];
Edward Thomson committed
703 704
	size_t oid_hexsize, len;

705
	oid_hexsize = git_oid_hexsize(oid_type);
Edward Thomson committed
706
	git_oid_fmt(oid, &head->oid);
707

708 709
	/* Prefer multi_ack_detailed */
	if (caps->multi_ack_detailed)
710
		git_str_puts(&str, GIT_CAP_MULTI_ACK_DETAILED " ");
711
	else if (caps->multi_ack)
712
		git_str_puts(&str, GIT_CAP_MULTI_ACK " ");
713

714 715
	/* Prefer side-band-64k if the server supports both */
	if (caps->side_band_64k)
716
		git_str_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
717
	else if (caps->side_band)
718
		git_str_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
719

720
	if (caps->include_tag)
721
		git_str_puts(&str, GIT_CAP_INCLUDE_TAG " ");
722

723
	if (caps->thin_pack)
724
		git_str_puts(&str, GIT_CAP_THIN_PACK " ");
725

726
	if (caps->ofs_delta)
727
		git_str_puts(&str, GIT_CAP_OFS_DELTA " ");
728

729
	if (caps->shallow)
730
		git_str_puts(&str, GIT_CAP_SHALLOW " ");
731

732
	if (git_str_oom(&str))
733
		return -1;
734

735
	if (str.size > (PKT_MAX_SIZE - (PKT_MAX_WANTLEN + 1))) {
736
		git_error_set(GIT_ERROR_NET,
737
			"tried to produce packet with invalid caps length %" PRIuZ, str.size);
738 739 740
		return -1;
	}

741 742 743 744
	len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
	      oid_hexsize + 1 /* NUL */ +
	      git_str_len(&str) + 1 /* LF */;

745 746
	git_str_grow_by(buf, len);
	git_str_printf(buf,
747
		"%04x%s%.*s %s\n", (unsigned int)len, PKT_WANT_PREFIX,
Edward Thomson committed
748
		(int)oid_hexsize, oid, git_str_cstr(&str));
749
	git_str_dispose(&str);
750

751
	GIT_ERROR_CHECK_ALLOC_STR(buf);
752 753

	return 0;
754 755
}

756 757 758 759 760
/*
 * All "want" packets have the same length and format, so what we do
 * is overwrite the OID each time.
 */

761
int git_pkt_buffer_wants(
762
	const git_fetch_negotiation *wants,
763
	transport_smart_caps *caps,
764
	git_str *buf)
765
{
766
	const git_remote_head *head;
767 768 769 770 771
	char oid[GIT_OID_MAX_HEXSIZE];
	git_oid_t oid_type;
	size_t oid_hexsize, want_len, i = 0;

#ifdef GIT_EXPERIMENTAL_SHA256
772
	oid_type = wants->refs_len > 0 ? wants->refs[0]->oid.type : GIT_OID_SHA1;
773 774 775 776 777 778 779 780
#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 */;
781 782

	if (caps->common) {
783
		for (; i < wants->refs_len; ++i) {
784
			head = wants->refs[i];
785 786 787 788
			if (!head->local)
				break;
		}

lmcglash committed
789
		if (buffer_want_with_caps(wants->refs[i], caps, oid_type, buf) < 0)
790
			return -1;
791 792 793 794

		i++;
	}

795
	for (; i < wants->refs_len; ++i) {
796
		head = wants->refs[i];
Edward Thomson committed
797

798 799 800 801
		if (head->local)
			continue;

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

803 804 805
		git_str_printf(buf, "%04x%s%.*s\n",
			(unsigned int)want_len, PKT_WANT_PREFIX,
			(int)oid_hexsize, oid);
Edward Thomson committed
806

807
		if (git_str_oom(buf))
808
			return -1;
809 810
	}

811
	/* Tell the server about our shallow objects */
812
	for (i = 0; i < wants->shallow_roots_len; i++) {
813
		char oid[GIT_OID_MAX_HEXSIZE + 1];
yuangli committed
814
		git_str shallow_buf = GIT_STR_INIT;
815

816
		git_oid_tostr(oid, GIT_OID_MAX_HEXSIZE + 1, &wants->shallow_roots[i]);
yuangli committed
817
		git_str_puts(&shallow_buf, "shallow ");
818
		git_str_puts(&shallow_buf, oid);
yuangli committed
819
		git_str_putc(&shallow_buf, '\n');
820

yuangli committed
821
		git_str_printf(buf, "%04x%s", (unsigned int)git_str_len(&shallow_buf) + 4, git_str_cstr(&shallow_buf));
822

yuangli committed
823 824
		git_str_dispose(&shallow_buf);

yuangli committed
825
		if (git_str_oom(buf))
826 827 828 829
			return -1;
	}

	if (wants->depth > 0) {
yuangli committed
830
		git_str deepen_buf = GIT_STR_INIT;
831

yuangli committed
832 833
		git_str_printf(&deepen_buf, "deepen %d\n", wants->depth);
		git_str_printf(buf,"%04x%s", (unsigned int)git_str_len(&deepen_buf) + 4, git_str_cstr(&deepen_buf));
834

835
		git_str_dispose(&deepen_buf);
836

yuangli committed
837
		if (git_str_oom(buf))
838 839 840
			return -1;
	}

841 842 843
	return git_pkt_buffer_flush(buf);
}

844
int git_pkt_buffer_have(git_oid *oid, git_str *buf)
845
{
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
	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);
864 865
}

866
int git_pkt_buffer_done(git_str *buf)
867
{
868
	return git_str_put(buf, PKT_DONE_STR, CONST_STRLEN(PKT_DONE_STR));
869
}