+#---------- remote protocol support, common ----------
+
+# remote push initiator/responder protocol:
+# < dgit-remote-push-ready [optional extra info ignored by old initiators]
+#
+# > file begin parsed-changelog
+# [indicates that output of dpkg-parsechangelog follows]
+# > data-block NBYTES
+# > [NBYTES bytes of data (no newline)]
+# [maybe some more blocks]
+# > data-end
+#
+# > file begin dsc
+# [etc]
+#
+# > file begin changes
+# [etc]
+#
+# > want signed-tag
+# [indicates that signed tag is wanted]
+# < data-block NBYTES
+# < [NBYTES bytes of data (no newline)]
+# [maybe some more blocks]
+# < data-end
+# < files-end
+#
+# > want signed-changes-dsc
+# < data-block NBYTES [transfer of signed changes]
+# [etc]
+# < data-block NBYTES [transfer of signed dsc]
+# [etc]
+# < files-end
+#
+# > complete
+
+sub badproto ($$) {
+ my ($fh, $m) = @_;
+ fail "connection lost: $!" if $fh->error;
+ fail "connection terminated" if $fh->eof;
+ fail "protocol violation; $m not expected";
+}
+
+sub protocol_expect ($&) {
+ my ($fh, $match) = @_;
+ local $_;
+ $_ = <$fh>;
+ defined && chomp or badproto $fh, "eof";
+ return if &$match;
+ badproto $fh, "\`$_'";
+}
+
+sub protocol_send_file ($$) {
+ my ($fh, $ourfn) = @_;
+ open PF, "<", $ourfn or die "$ourfn: $!";
+ for (;;) {
+ my $d;
+ my $got = read PF, $d, 65536;
+ die "$ourfn: $!" unless defined $got;
+ last if $got;
+ print $fh "data-block ".length($d)."\n" or die $!;
+ print $d or die $!;
+ }
+ print $fh "data-end\n" or die $!;
+ close PF;
+}
+
+sub protocol_receive_file ($$) {
+ my ($fh, $ourfn) = @_;
+ open PF, ">", $ourfn or die "$ourfn: $!";
+ for (;;) {
+ protocol_expect \*STDIN, { m/^data-block (\d{1,6})$|data-end$/ };
+ length $1 or last;
+ my $d;
+ my $got = read $fh, $d, $1;
+ $got==$1 or badproto $fh, "eof during data block";
+ print PF $d or die $!;
+ }
+}
+
+#---------- remote protocol support, responder ----------
+
+sub responder_send_command ($) {
+ my ($command) = @_;
+ return unless $we_are_responder;
+ # called even without $we_are_responder
+ print DEBUG "<< $command\n";
+ print $command, "\n" or die $!;
+}
+
+sub responder_send_file ($$) {
+ my ($keyword, $ourfn) = @_;
+ return unless $we_are_responder;
+ responder_send_command "file begin $cmdprefix";
+ protocol_send_file \*STDOUT, $ourfn;
+}
+
+sub responder_receive_files ($@) {
+ my ($keyword, @ourfns) = @_;
+ die unless $we_are_responder;
+ responder_send_command "want $keyword";
+ foreach my $fn (@ourfns) {
+ protocol_receive_file \*STDIN, $fn;
+ }
+ protocol_expect \*STDIN, { m/^files-end$/ };
+}
+
+#---------- remote protocol support, initiator ----------
+
+
+
+#---------- end remote code ----------
+
+sub progress {
+ if ($we_are_responder) {
+ my $m = join '', @_;
+ responder_send_command "progress ".length($m) or die $!;
+ print $m or die $!;
+ } else {
+ print @_, "\n";
+ }
+}
+