1 /*
2  * Copyright (C) the libgit2 contributors. All rights reserved.
3  *
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.
6  */
7 
8 #include "common.h"
9 
10 #include "git2.h"
11 #include "git2/odb_backend.h"
12 
13 #include "smart.h"
14 #include "refs.h"
15 #include "repository.h"
16 #include "push.h"
17 #include "pack-objects.h"
18 #include "remote.h"
19 #include "util.h"
20 
21 #define NETWORK_XFER_THRESHOLD (100*1024)
22 /* The minimal interval between progress updates (in seconds). */
23 #define MIN_PROGRESS_UPDATE_INTERVAL 0.5
24 
25 bool git_smart__ofs_delta_enabled = true;
26 
git_smart__store_refs(transport_smart * t,int flushes)27 int git_smart__store_refs(transport_smart *t, int flushes)
28 {
29 	gitno_buffer *buf = &t->buffer;
30 	git_vector *refs = &t->refs;
31 	int error, flush = 0, recvd;
32 	const char *line_end = NULL;
33 	git_pkt *pkt = NULL;
34 	size_t i;
35 
36 	/* Clear existing refs in case git_remote_connect() is called again
37 	 * after git_remote_disconnect().
38 	 */
39 	git_vector_foreach(refs, i, pkt) {
40 		git_pkt_free(pkt);
41 	}
42 	git_vector_clear(refs);
43 	pkt = NULL;
44 
45 	do {
46 		if (buf->offset > 0)
47 			error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset);
48 		else
49 			error = GIT_EBUFS;
50 
51 		if (error < 0 && error != GIT_EBUFS)
52 			return error;
53 
54 		if (error == GIT_EBUFS) {
55 			if ((recvd = gitno_recv(buf)) < 0)
56 				return recvd;
57 
58 			if (recvd == 0) {
59 				git_error_set(GIT_ERROR_NET, "early EOF");
60 				return GIT_EEOF;
61 			}
62 
63 			continue;
64 		}
65 
66 		gitno_consume(buf, line_end);
67 		if (pkt->type == GIT_PKT_ERR) {
68 			git_error_set(GIT_ERROR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error);
69 			git__free(pkt);
70 			return -1;
71 		}
72 
73 		if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
74 			return -1;
75 
76 		if (pkt->type == GIT_PKT_FLUSH) {
77 			flush++;
78 			git_pkt_free(pkt);
79 		}
80 	} while (flush < flushes);
81 
82 	return flush;
83 }
84 
append_symref(const char ** out,git_vector * symrefs,const char * ptr)85 static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
86 {
87 	int error;
88 	const char *end;
89 	git_buf buf = GIT_BUF_INIT;
90 	git_refspec *mapping = NULL;
91 
92 	ptr += strlen(GIT_CAP_SYMREF);
93 	if (*ptr != '=')
94 		goto on_invalid;
95 
96 	ptr++;
97 	if (!(end = strchr(ptr, ' ')) &&
98 	    !(end = strchr(ptr, '\0')))
99 		goto on_invalid;
100 
101 	if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0)
102 		return error;
103 
104 	/* symref mapping has refspec format */
105 	mapping = git__calloc(1, sizeof(git_refspec));
106 	GIT_ERROR_CHECK_ALLOC(mapping);
107 
108 	error = git_refspec__parse(mapping, git_buf_cstr(&buf), true);
109 	git_buf_dispose(&buf);
110 
111 	/* if the error isn't OOM, then it's a parse error; let's use a nicer message */
112 	if (error < 0) {
113 		if (git_error_last()->klass != GIT_ERROR_NOMEMORY)
114 			goto on_invalid;
115 
116 		git__free(mapping);
117 		return error;
118 	}
119 
120 	if ((error = git_vector_insert(symrefs, mapping)) < 0)
121 		return error;
122 
123 	*out = end;
124 	return 0;
125 
126 on_invalid:
127 	git_error_set(GIT_ERROR_NET, "remote sent invalid symref");
128 	git_refspec__dispose(mapping);
129 	git__free(mapping);
130 	return -1;
131 }
132 
git_smart__detect_caps(git_pkt_ref * pkt,transport_smart_caps * caps,git_vector * symrefs)133 int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
134 {
135 	const char *ptr;
136 
137 	/* No refs or capabilites, odd but not a problem */
138 	if (pkt == NULL || pkt->capabilities == NULL)
139 		return GIT_ENOTFOUND;
140 
141 	ptr = pkt->capabilities;
142 	while (ptr != NULL && *ptr != '\0') {
143 		if (*ptr == ' ')
144 			ptr++;
145 
146 		if (git_smart__ofs_delta_enabled && !git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
147 			caps->common = caps->ofs_delta = 1;
148 			ptr += strlen(GIT_CAP_OFS_DELTA);
149 			continue;
150 		}
151 
152 		/* Keep multi_ack_detailed before multi_ack */
153 		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) {
154 			caps->common = caps->multi_ack_detailed = 1;
155 			ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED);
156 			continue;
157 		}
158 
159 		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
160 			caps->common = caps->multi_ack = 1;
161 			ptr += strlen(GIT_CAP_MULTI_ACK);
162 			continue;
163 		}
164 
165 		if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
166 			caps->common = caps->include_tag = 1;
167 			ptr += strlen(GIT_CAP_INCLUDE_TAG);
168 			continue;
169 		}
170 
171 		/* Keep side-band check after side-band-64k */
172 		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
173 			caps->common = caps->side_band_64k = 1;
174 			ptr += strlen(GIT_CAP_SIDE_BAND_64K);
175 			continue;
176 		}
177 
178 		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
179 			caps->common = caps->side_band = 1;
180 			ptr += strlen(GIT_CAP_SIDE_BAND);
181 			continue;
182 		}
183 
184 		if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
185 			caps->common = caps->delete_refs = 1;
186 			ptr += strlen(GIT_CAP_DELETE_REFS);
187 			continue;
188 		}
189 
190 		if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
191 			caps->common = caps->thin_pack = 1;
192 			ptr += strlen(GIT_CAP_THIN_PACK);
193 			continue;
194 		}
195 
196 		if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
197 			int error;
198 
199 			if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
200 				return error;
201 
202 			continue;
203 		}
204 
205 		/* We don't know this capability, so skip it */
206 		ptr = strchr(ptr, ' ');
207 	}
208 
209 	return 0;
210 }
211 
recv_pkt(git_pkt ** out_pkt,git_pkt_type * out_type,gitno_buffer * buf)212 static int recv_pkt(git_pkt **out_pkt, git_pkt_type *out_type, gitno_buffer *buf)
213 {
214 	const char *ptr = buf->data, *line_end = ptr;
215 	git_pkt *pkt = NULL;
216 	int error = 0, ret;
217 
218 	do {
219 		if (buf->offset > 0)
220 			error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset);
221 		else
222 			error = GIT_EBUFS;
223 
224 		if (error == 0)
225 			break; /* return the pkt */
226 
227 		if (error < 0 && error != GIT_EBUFS)
228 			return error;
229 
230 		if ((ret = gitno_recv(buf)) < 0) {
231 			return ret;
232 		} else if (ret == 0) {
233 			git_error_set(GIT_ERROR_NET, "early EOF");
234 			return GIT_EEOF;
235 		}
236 	} while (error);
237 
238 	gitno_consume(buf, line_end);
239 	if (out_type != NULL)
240 		*out_type = pkt->type;
241 	if (out_pkt != NULL)
242 		*out_pkt = pkt;
243 	else
244 		git__free(pkt);
245 
246 	return error;
247 }
248 
store_common(transport_smart * t)249 static int store_common(transport_smart *t)
250 {
251 	git_pkt *pkt = NULL;
252 	gitno_buffer *buf = &t->buffer;
253 	int error;
254 
255 	do {
256 		if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
257 			return error;
258 
259 		if (pkt->type != GIT_PKT_ACK) {
260 			git__free(pkt);
261 			return 0;
262 		}
263 
264 		if (git_vector_insert(&t->common, pkt) < 0) {
265 			git__free(pkt);
266 			return -1;
267 		}
268 	} while (1);
269 
270 	return 0;
271 }
272 
fetch_setup_walk(git_revwalk ** out,git_repository * repo)273 static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
274 {
275 	git_revwalk *walk = NULL;
276 	git_strarray refs;
277 	unsigned int i;
278 	git_reference *ref = NULL;
279 	int error;
280 
281 	if ((error = git_reference_list(&refs, repo)) < 0)
282 		return error;
283 
284 	if ((error = git_revwalk_new(&walk, repo)) < 0)
285 		return error;
286 
287 	git_revwalk_sorting(walk, GIT_SORT_TIME);
288 
289 	for (i = 0; i < refs.count; ++i) {
290 		git_reference_free(ref);
291 		ref = NULL;
292 
293 		/* No tags */
294 		if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
295 			continue;
296 
297 		if ((error = git_reference_lookup(&ref, repo, refs.strings[i])) < 0)
298 			goto on_error;
299 
300 		if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC)
301 			continue;
302 
303 		if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0)
304 			goto on_error;
305 	}
306 
307 	*out = walk;
308 
309 on_error:
310 	if (error)
311 		git_revwalk_free(walk);
312 	git_reference_free(ref);
313 	git_strarray_free(&refs);
314 	return error;
315 }
316 
wait_while_ack(gitno_buffer * buf)317 static int wait_while_ack(gitno_buffer *buf)
318 {
319 	int error;
320 	git_pkt *pkt = NULL;
321 	git_pkt_ack *ack = NULL;
322 
323 	while (1) {
324 		git_pkt_free(pkt);
325 
326 		if ((error = recv_pkt(&pkt, NULL, buf)) < 0)
327 			return error;
328 
329 		if (pkt->type == GIT_PKT_NAK)
330 			break;
331 		if (pkt->type != GIT_PKT_ACK)
332 			continue;
333 
334 		ack = (git_pkt_ack*)pkt;
335 
336 		if (ack->status != GIT_ACK_CONTINUE &&
337 		    ack->status != GIT_ACK_COMMON &&
338 		    ack->status != GIT_ACK_READY) {
339 			break;
340 		}
341 	}
342 
343 	git_pkt_free(pkt);
344 	return 0;
345 }
346 
git_smart__negotiate_fetch(git_transport * transport,git_repository * repo,const git_remote_head * const * wants,size_t count)347 int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
348 {
349 	transport_smart *t = (transport_smart *)transport;
350 	gitno_buffer *buf = &t->buffer;
351 	git_buf data = GIT_BUF_INIT;
352 	git_revwalk *walk = NULL;
353 	int error = -1;
354 	git_pkt_type pkt_type;
355 	unsigned int i;
356 	git_oid oid;
357 
358 	if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
359 		return error;
360 
361 	if ((error = fetch_setup_walk(&walk, repo)) < 0)
362 		goto on_error;
363 
364 	/*
365 	 * Our support for ACK extensions is simply to parse them. On
366 	 * the first ACK we will accept that as enough common
367 	 * objects. We give up if we haven't found an answer in the
368 	 * first 256 we send.
369 	 */
370 	i = 0;
371 	while (i < 256) {
372 		error = git_revwalk_next(&oid, walk);
373 
374 		if (error < 0) {
375 			if (GIT_ITEROVER == error)
376 				break;
377 
378 			goto on_error;
379 		}
380 
381 		git_pkt_buffer_have(&oid, &data);
382 		i++;
383 		if (i % 20 == 0) {
384 			if (t->cancelled.val) {
385 				git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user");
386 				error = GIT_EUSER;
387 				goto on_error;
388 			}
389 
390 			git_pkt_buffer_flush(&data);
391 			if (git_buf_oom(&data)) {
392 				error = -1;
393 				goto on_error;
394 			}
395 
396 			if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
397 				goto on_error;
398 
399 			git_buf_clear(&data);
400 			if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
401 				if ((error = store_common(t)) < 0)
402 					goto on_error;
403 			} else {
404 				if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0)
405 					goto on_error;
406 
407 				if (pkt_type == GIT_PKT_ACK) {
408 					break;
409 				} else if (pkt_type == GIT_PKT_NAK) {
410 					continue;
411 				} else {
412 					git_error_set(GIT_ERROR_NET, "Unexpected pkt type");
413 					error = -1;
414 					goto on_error;
415 				}
416 			}
417 		}
418 
419 		if (t->common.length > 0)
420 			break;
421 
422 		if (i % 20 == 0 && t->rpc) {
423 			git_pkt_ack *pkt;
424 			unsigned int j;
425 
426 			if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
427 				goto on_error;
428 
429 			git_vector_foreach(&t->common, j, pkt) {
430 				if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
431 					goto on_error;
432 			}
433 
434 			if (git_buf_oom(&data)) {
435 				error = -1;
436 				goto on_error;
437 			}
438 		}
439 	}
440 
441 	/* Tell the other end that we're done negotiating */
442 	if (t->rpc && t->common.length > 0) {
443 		git_pkt_ack *pkt;
444 		unsigned int j;
445 
446 		if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
447 			goto on_error;
448 
449 		git_vector_foreach(&t->common, j, pkt) {
450 			if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
451 				goto on_error;
452 		}
453 
454 		if (git_buf_oom(&data)) {
455 			error = -1;
456 			goto on_error;
457 		}
458 	}
459 
460 	if ((error = git_pkt_buffer_done(&data)) < 0)
461 		goto on_error;
462 
463 	if (t->cancelled.val) {
464 		git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user");
465 		error = GIT_EUSER;
466 		goto on_error;
467 	}
468 	if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
469 		goto on_error;
470 
471 	git_buf_dispose(&data);
472 	git_revwalk_free(walk);
473 
474 	/* Now let's eat up whatever the server gives us */
475 	if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
476 		if ((error = recv_pkt(NULL, &pkt_type, buf)) < 0)
477 			return error;
478 
479 		if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
480 			git_error_set(GIT_ERROR_NET, "Unexpected pkt type");
481 			return -1;
482 		}
483 	} else {
484 		error = wait_while_ack(buf);
485 	}
486 
487 	return error;
488 
489 on_error:
490 	git_revwalk_free(walk);
491 	git_buf_dispose(&data);
492 	return error;
493 }
494 
no_sideband(transport_smart * t,struct git_odb_writepack * writepack,gitno_buffer * buf,git_indexer_progress * stats)495 static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_indexer_progress *stats)
496 {
497 	int recvd;
498 
499 	do {
500 		if (t->cancelled.val) {
501 			git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user");
502 			return GIT_EUSER;
503 		}
504 
505 		if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
506 			return -1;
507 
508 		gitno_consume_n(buf, buf->offset);
509 
510 		if ((recvd = gitno_recv(buf)) < 0)
511 			return recvd;
512 	} while(recvd > 0);
513 
514 	if (writepack->commit(writepack, stats) < 0)
515 		return -1;
516 
517 	return 0;
518 }
519 
520 struct network_packetsize_payload
521 {
522 	git_indexer_progress_cb callback;
523 	void *payload;
524 	git_indexer_progress *stats;
525 	size_t last_fired_bytes;
526 };
527 
network_packetsize(size_t received,void * payload)528 static int network_packetsize(size_t received, void *payload)
529 {
530 	struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
531 
532 	/* Accumulate bytes */
533 	npp->stats->received_bytes += received;
534 
535 	/* Fire notification if the threshold is reached */
536 	if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
537 		npp->last_fired_bytes = npp->stats->received_bytes;
538 
539 		if (npp->callback(npp->stats, npp->payload))
540 			return GIT_EUSER;
541 	}
542 
543 	return 0;
544 }
545 
git_smart__download_pack(git_transport * transport,git_repository * repo,git_indexer_progress * stats,git_indexer_progress_cb progress_cb,void * progress_payload)546 int git_smart__download_pack(
547 	git_transport *transport,
548 	git_repository *repo,
549 	git_indexer_progress *stats,
550 	git_indexer_progress_cb progress_cb,
551 	void *progress_payload)
552 {
553 	transport_smart *t = (transport_smart *)transport;
554 	gitno_buffer *buf = &t->buffer;
555 	git_odb *odb;
556 	struct git_odb_writepack *writepack = NULL;
557 	int error = 0;
558 	struct network_packetsize_payload npp = {0};
559 
560 	memset(stats, 0, sizeof(git_indexer_progress));
561 
562 	if (progress_cb) {
563 		npp.callback = progress_cb;
564 		npp.payload = progress_payload;
565 		npp.stats = stats;
566 		t->packetsize_cb = &network_packetsize;
567 		t->packetsize_payload = &npp;
568 
569 		/* We might have something in the buffer already from negotiate_fetch */
570 		if (t->buffer.offset > 0 && !t->cancelled.val)
571 			if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
572 				git_atomic_set(&t->cancelled, 1);
573 	}
574 
575 	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
576 		((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0))
577 		goto done;
578 
579 	/*
580 	 * If the remote doesn't support the side-band, we can feed
581 	 * the data directly to the pack writer. Otherwise, we need to
582 	 * check which one belongs there.
583 	 */
584 	if (!t->caps.side_band && !t->caps.side_band_64k) {
585 		error = no_sideband(t, writepack, buf, stats);
586 		goto done;
587 	}
588 
589 	do {
590 		git_pkt *pkt = NULL;
591 
592 		/* Check cancellation before network call */
593 		if (t->cancelled.val) {
594 			git_error_clear();
595 			error = GIT_EUSER;
596 			goto done;
597 		}
598 
599 		if ((error = recv_pkt(&pkt, NULL, buf)) >= 0) {
600 			/* Check cancellation after network call */
601 			if (t->cancelled.val) {
602 				git_error_clear();
603 				error = GIT_EUSER;
604 			} else if (pkt->type == GIT_PKT_PROGRESS) {
605 				if (t->progress_cb) {
606 					git_pkt_progress *p = (git_pkt_progress *) pkt;
607 
608 					if (p->len > INT_MAX) {
609 						git_error_set(GIT_ERROR_NET, "oversized progress message");
610 						error = GIT_ERROR;
611 						goto done;
612 					}
613 
614 					error = t->progress_cb(p->data, (int)p->len, t->message_cb_payload);
615 				}
616 			} else if (pkt->type == GIT_PKT_DATA) {
617 				git_pkt_data *p = (git_pkt_data *) pkt;
618 
619 				if (p->len)
620 					error = writepack->append(writepack, p->data, p->len, stats);
621 			} else if (pkt->type == GIT_PKT_FLUSH) {
622 				/* A flush indicates the end of the packfile */
623 				git__free(pkt);
624 				break;
625 			}
626 		}
627 
628 		git_pkt_free(pkt);
629 
630 		if (error < 0)
631 			goto done;
632 
633 	} while (1);
634 
635 	/*
636 	 * Trailing execution of progress_cb, if necessary...
637 	 * Only the callback through the npp datastructure currently
638 	 * updates the last_fired_bytes value. It is possible that
639 	 * progress has already been reported with the correct
640 	 * "received_bytes" value, but until (if?) this is unified
641 	 * then we will report progress again to be sure that the
642 	 * correct last received_bytes value is reported.
643 	 */
644 	if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) {
645 		error = npp.callback(npp.stats, npp.payload);
646 		if (error != 0)
647 			goto done;
648 	}
649 
650 	error = writepack->commit(writepack, stats);
651 
652 done:
653 	if (writepack)
654 		writepack->free(writepack);
655 	if (progress_cb) {
656 		t->packetsize_cb = NULL;
657 		t->packetsize_payload = NULL;
658 	}
659 
660 	return error;
661 }
662 
gen_pktline(git_buf * buf,git_push * push)663 static int gen_pktline(git_buf *buf, git_push *push)
664 {
665 	push_spec *spec;
666 	size_t i, len;
667 	char old_id[GIT_OID_HEXSZ+1], new_id[GIT_OID_HEXSZ+1];
668 
669 	old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0';
670 
671 	git_vector_foreach(&push->specs, i, spec) {
672 		len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->refspec.dst);
673 
674 		if (i == 0) {
675 			++len; /* '\0' */
676 			if (push->report_status)
677 				len += strlen(GIT_CAP_REPORT_STATUS) + 1;
678 			len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
679 		}
680 
681 		git_oid_fmt(old_id, &spec->roid);
682 		git_oid_fmt(new_id, &spec->loid);
683 
684 		git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst);
685 
686 		if (i == 0) {
687 			git_buf_putc(buf, '\0');
688 			/* Core git always starts their capabilities string with a space */
689 			if (push->report_status) {
690 				git_buf_putc(buf, ' ');
691 				git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
692 			}
693 			git_buf_putc(buf, ' ');
694 			git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K);
695 		}
696 
697 		git_buf_putc(buf, '\n');
698 	}
699 
700 	git_buf_puts(buf, "0000");
701 	return git_buf_oom(buf) ? -1 : 0;
702 }
703 
add_push_report_pkt(git_push * push,git_pkt * pkt)704 static int add_push_report_pkt(git_push *push, git_pkt *pkt)
705 {
706 	push_status *status;
707 
708 	switch (pkt->type) {
709 		case GIT_PKT_OK:
710 			status = git__calloc(1, sizeof(push_status));
711 			GIT_ERROR_CHECK_ALLOC(status);
712 			status->msg = NULL;
713 			status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
714 			if (!status->ref ||
715 				git_vector_insert(&push->status, status) < 0) {
716 				git_push_status_free(status);
717 				return -1;
718 			}
719 			break;
720 		case GIT_PKT_NG:
721 			status = git__calloc(1, sizeof(push_status));
722 			GIT_ERROR_CHECK_ALLOC(status);
723 			status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
724 			status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
725 			if (!status->ref || !status->msg ||
726 				git_vector_insert(&push->status, status) < 0) {
727 				git_push_status_free(status);
728 				return -1;
729 			}
730 			break;
731 		case GIT_PKT_UNPACK:
732 			push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
733 			break;
734 		case GIT_PKT_FLUSH:
735 			return GIT_ITEROVER;
736 		default:
737 			git_error_set(GIT_ERROR_NET, "report-status: protocol error");
738 			return -1;
739 	}
740 
741 	return 0;
742 }
743 
add_push_report_sideband_pkt(git_push * push,git_pkt_data * data_pkt,git_buf * data_pkt_buf)744 static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_buf *data_pkt_buf)
745 {
746 	git_pkt *pkt;
747 	const char *line, *line_end = NULL;
748 	size_t line_len;
749 	int error;
750 	int reading_from_buf = data_pkt_buf->size > 0;
751 
752 	if (reading_from_buf) {
753 		/* We had an existing partial packet, so add the new
754 		 * packet to the buffer and parse the whole thing */
755 		git_buf_put(data_pkt_buf, data_pkt->data, data_pkt->len);
756 		line = data_pkt_buf->ptr;
757 		line_len = data_pkt_buf->size;
758 	}
759 	else {
760 		line = data_pkt->data;
761 		line_len = data_pkt->len;
762 	}
763 
764 	while (line_len > 0) {
765 		error = git_pkt_parse_line(&pkt, &line_end, line, line_len);
766 
767 		if (error == GIT_EBUFS) {
768 			/* Buffer the data when the inner packet is split
769 			 * across multiple sideband packets */
770 			if (!reading_from_buf)
771 				git_buf_put(data_pkt_buf, line, line_len);
772 			error = 0;
773 			goto done;
774 		}
775 		else if (error < 0)
776 			goto done;
777 
778 		/* Advance in the buffer */
779 		line_len -= (line_end - line);
780 		line = line_end;
781 
782 		error = add_push_report_pkt(push, pkt);
783 
784 		git_pkt_free(pkt);
785 
786 		if (error < 0 && error != GIT_ITEROVER)
787 			goto done;
788 	}
789 
790 	error = 0;
791 
792 done:
793 	if (reading_from_buf)
794 		git_buf_consume(data_pkt_buf, line_end);
795 	return error;
796 }
797 
parse_report(transport_smart * transport,git_push * push)798 static int parse_report(transport_smart *transport, git_push *push)
799 {
800 	git_pkt *pkt = NULL;
801 	const char *line_end = NULL;
802 	gitno_buffer *buf = &transport->buffer;
803 	int error, recvd;
804 	git_buf data_pkt_buf = GIT_BUF_INIT;
805 
806 	for (;;) {
807 		if (buf->offset > 0)
808 			error = git_pkt_parse_line(&pkt, &line_end,
809 						   buf->data, buf->offset);
810 		else
811 			error = GIT_EBUFS;
812 
813 		if (error < 0 && error != GIT_EBUFS) {
814 			error = -1;
815 			goto done;
816 		}
817 
818 		if (error == GIT_EBUFS) {
819 			if ((recvd = gitno_recv(buf)) < 0) {
820 				error = recvd;
821 				goto done;
822 			}
823 
824 			if (recvd == 0) {
825 				git_error_set(GIT_ERROR_NET, "early EOF");
826 				error = GIT_EEOF;
827 				goto done;
828 			}
829 			continue;
830 		}
831 
832 		gitno_consume(buf, line_end);
833 
834 		error = 0;
835 
836 		switch (pkt->type) {
837 			case GIT_PKT_DATA:
838 				/* This is a sideband packet which contains other packets */
839 				error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt, &data_pkt_buf);
840 				break;
841 			case GIT_PKT_ERR:
842 				git_error_set(GIT_ERROR_NET, "report-status: Error reported: %s",
843 					((git_pkt_err *)pkt)->error);
844 				error = -1;
845 				break;
846 			case GIT_PKT_PROGRESS:
847 				if (transport->progress_cb) {
848 					git_pkt_progress *p = (git_pkt_progress *) pkt;
849 
850 					if (p->len > INT_MAX) {
851 						git_error_set(GIT_ERROR_NET, "oversized progress message");
852 						error = GIT_ERROR;
853 						goto done;
854 					}
855 
856 					error = transport->progress_cb(p->data, (int)p->len, transport->message_cb_payload);
857 				}
858 				break;
859 			default:
860 				error = add_push_report_pkt(push, pkt);
861 				break;
862 		}
863 
864 		git_pkt_free(pkt);
865 
866 		/* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
867 		if (error == GIT_ITEROVER) {
868 			error = 0;
869 			if (data_pkt_buf.size > 0) {
870 				/* If there was data remaining in the pack data buffer,
871 				 * then the server sent a partial pkt-line */
872 				git_error_set(GIT_ERROR_NET, "Incomplete pack data pkt-line");
873 				error = GIT_ERROR;
874 			}
875 			goto done;
876 		}
877 
878 		if (error < 0) {
879 			goto done;
880 		}
881 	}
882 done:
883 	git_buf_dispose(&data_pkt_buf);
884 	return error;
885 }
886 
add_ref_from_push_spec(git_vector * refs,push_spec * push_spec)887 static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
888 {
889 	git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref));
890 	GIT_ERROR_CHECK_ALLOC(added);
891 
892 	added->type = GIT_PKT_REF;
893 	git_oid_cpy(&added->head.oid, &push_spec->loid);
894 	added->head.name = git__strdup(push_spec->refspec.dst);
895 
896 	if (!added->head.name ||
897 		git_vector_insert(refs, added) < 0) {
898 		git_pkt_free((git_pkt *)added);
899 		return -1;
900 	}
901 
902 	return 0;
903 }
904 
update_refs_from_report(git_vector * refs,git_vector * push_specs,git_vector * push_report)905 static int update_refs_from_report(
906 	git_vector *refs,
907 	git_vector *push_specs,
908 	git_vector *push_report)
909 {
910 	git_pkt_ref *ref;
911 	push_spec *push_spec;
912 	push_status *push_status;
913 	size_t i, j, refs_len;
914 	int cmp;
915 
916 	/* For each push spec we sent to the server, we should have
917 	 * gotten back a status packet in the push report */
918 	if (push_specs->length != push_report->length) {
919 		git_error_set(GIT_ERROR_NET, "report-status: protocol error");
920 		return -1;
921 	}
922 
923 	/* We require that push_specs be sorted with push_spec_rref_cmp,
924 	 * and that push_report be sorted with push_status_ref_cmp */
925 	git_vector_sort(push_specs);
926 	git_vector_sort(push_report);
927 
928 	git_vector_foreach(push_specs, i, push_spec) {
929 		push_status = git_vector_get(push_report, i);
930 
931 		/* For each push spec we sent to the server, we should have
932 		 * gotten back a status packet in the push report which matches */
933 		if (strcmp(push_spec->refspec.dst, push_status->ref)) {
934 			git_error_set(GIT_ERROR_NET, "report-status: protocol error");
935 			return -1;
936 		}
937 	}
938 
939 	/* We require that refs be sorted with ref_name_cmp */
940 	git_vector_sort(refs);
941 	i = j = 0;
942 	refs_len = refs->length;
943 
944 	/* Merge join push_specs with refs */
945 	while (i < push_specs->length && j < refs_len) {
946 		push_spec = git_vector_get(push_specs, i);
947 		push_status = git_vector_get(push_report, i);
948 		ref = git_vector_get(refs, j);
949 
950 		cmp = strcmp(push_spec->refspec.dst, ref->head.name);
951 
952 		/* Iterate appropriately */
953 		if (cmp <= 0) i++;
954 		if (cmp >= 0) j++;
955 
956 		/* Add case */
957 		if (cmp < 0 &&
958 			!push_status->msg &&
959 			add_ref_from_push_spec(refs, push_spec) < 0)
960 			return -1;
961 
962 		/* Update case, delete case */
963 		if (cmp == 0 &&
964 			!push_status->msg)
965 			git_oid_cpy(&ref->head.oid, &push_spec->loid);
966 	}
967 
968 	for (; i < push_specs->length; i++) {
969 		push_spec = git_vector_get(push_specs, i);
970 		push_status = git_vector_get(push_report, i);
971 
972 		/* Add case */
973 		if (!push_status->msg &&
974 			add_ref_from_push_spec(refs, push_spec) < 0)
975 			return -1;
976 	}
977 
978 	/* Remove any refs which we updated to have a zero OID. */
979 	git_vector_rforeach(refs, i, ref) {
980 		if (git_oid_is_zero(&ref->head.oid)) {
981 			git_vector_remove(refs, i);
982 			git_pkt_free((git_pkt *)ref);
983 		}
984 	}
985 
986 	git_vector_sort(refs);
987 
988 	return 0;
989 }
990 
991 struct push_packbuilder_payload
992 {
993 	git_smart_subtransport_stream *stream;
994 	git_packbuilder *pb;
995 	git_push_transfer_progress_cb cb;
996 	void *cb_payload;
997 	size_t last_bytes;
998 	double last_progress_report_time;
999 };
1000 
stream_thunk(void * buf,size_t size,void * data)1001 static int stream_thunk(void *buf, size_t size, void *data)
1002 {
1003 	int error = 0;
1004 	struct push_packbuilder_payload *payload = data;
1005 
1006 	if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
1007 		return error;
1008 
1009 	if (payload->cb) {
1010 		double current_time = git__timer();
1011 		payload->last_bytes += size;
1012 
1013 		if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
1014 			payload->last_progress_report_time = current_time;
1015 			error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
1016 		}
1017 	}
1018 
1019 	return error;
1020 }
1021 
git_smart__push(git_transport * transport,git_push * push,const git_remote_callbacks * cbs)1022 int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs)
1023 {
1024 	transport_smart *t = (transport_smart *)transport;
1025 	struct push_packbuilder_payload packbuilder_payload = {0};
1026 	git_buf pktline = GIT_BUF_INIT;
1027 	int error = 0, need_pack = 0;
1028 	push_spec *spec;
1029 	unsigned int i;
1030 
1031 	packbuilder_payload.pb = push->pb;
1032 
1033 	if (cbs && cbs->push_transfer_progress) {
1034 		packbuilder_payload.cb = cbs->push_transfer_progress;
1035 		packbuilder_payload.cb_payload = cbs->payload;
1036 	}
1037 
1038 #ifdef PUSH_DEBUG
1039 {
1040 	git_remote_head *head;
1041 	char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0';
1042 
1043 	git_vector_foreach(&push->remote->refs, i, head) {
1044 		git_oid_fmt(hex, &head->oid);
1045 		fprintf(stderr, "%s (%s)\n", hex, head->name);
1046 	}
1047 
1048 	git_vector_foreach(&push->specs, i, spec) {
1049 		git_oid_fmt(hex, &spec->roid);
1050 		fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
1051 		git_oid_fmt(hex, &spec->loid);
1052 		fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
1053 			spec->rref : spec->lref);
1054 	}
1055 }
1056 #endif
1057 
1058 	/*
1059 	 * Figure out if we need to send a packfile; which is in all
1060 	 * cases except when we only send delete commands
1061 	 */
1062 	git_vector_foreach(&push->specs, i, spec) {
1063 		if (spec->refspec.src && spec->refspec.src[0] != '\0') {
1064 			need_pack = 1;
1065 			break;
1066 		}
1067 	}
1068 
1069 	if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
1070 		(error = gen_pktline(&pktline, push)) < 0 ||
1071 		(error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
1072 		goto done;
1073 
1074 	if (need_pack &&
1075 		(error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
1076 		goto done;
1077 
1078 	/* If we sent nothing or the server doesn't support report-status, then
1079 	 * we consider the pack to have been unpacked successfully */
1080 	if (!push->specs.length || !push->report_status)
1081 		push->unpack_ok = 1;
1082 	else if ((error = parse_report(t, push)) < 0)
1083 		goto done;
1084 
1085 	/* If progress is being reported write the final report */
1086 	if (cbs && cbs->push_transfer_progress) {
1087 		error = cbs->push_transfer_progress(
1088 					push->pb->nr_written,
1089 					push->pb->nr_objects,
1090 					packbuilder_payload.last_bytes,
1091 					cbs->payload);
1092 
1093 		if (error < 0)
1094 			goto done;
1095 	}
1096 
1097 	if (push->status.length) {
1098 		error = update_refs_from_report(&t->refs, &push->specs, &push->status);
1099 		if (error < 0)
1100 			goto done;
1101 
1102 		error = git_smart__update_heads(t, NULL);
1103 	}
1104 
1105 done:
1106 	git_buf_dispose(&pktline);
1107 	return error;
1108 }
1109