Fix 'Play' to correctly pass data to unpack-objects
[VCS-Git-Torrent.git] / lib / VCS / Git / Torrent / PWP / Message / Play.pm
bloba3e63178354392a776df0d0a87039520ded1eb15
2 package VCS::Git::Torrent::PWP::Message::Play;
4 =head1 NAME
6 VCS::Git::Torrent::PWP::Message::Play
8 =head2 DESCRIPTION
10 Implements the Play message from the RFC.
11 L<http://gittorrent.utsl.gen.nz/rfc.html#pwp-play>
13 =cut
15 use IO::Plumbing qw(hose vent);
16 use Moose;
17 with "VCS::Git::Torrent::PWP::Message";
18 use Carp;
19 use VCS::Git::Torrent::PWP qw(:pwp_constants unpack_hex pack_hex pack_num unpack_num);
21 has 'data' =>
22 isa => 'Value',
23 is => 'rw';
25 has 'data_len' =>
26 isa => 'Int',
27 is => 'rw';
29 has 'offset' =>
30 isa => 'Int',
31 is => 'rw';
33 has 'reel_sha1_pair' =>
34 isa => 'ArrayRef',
35 is => 'rw';
37 sub pack_payload {
38 my $self = shift;
39 my $payload = '';
41 $payload .= join('', map { pack_hex($_ ) } @{ $self->reel_sha1_pair });
42 $payload .= pack_num($self->offset);
43 $payload .= pack_num($self->data_len) if ( $self->data_len );
44 $payload .= $self->data if ( $self->data );
46 $payload;
49 sub unpack_payload {
50 my $self = shift;
51 my $payload = shift;
53 my @sha1_pair = map { unpack_hex($_) } (
54 substr($payload, 0, 20),
55 substr($payload, 20, 20)
57 $self->reel_sha1_pair(\@sha1_pair);
59 my $offset = unpack_num(substr($payload, 40, 4));
60 $self->offset($offset);
62 if ( length($payload) > 44 ) {
63 my $data_len = unpack_num(substr($payload, 44, 4));
64 $self->data_len($data_len);
66 my $data = substr($payload, 48, $data_len);
67 $self->data($data);
71 sub args {
72 my $class = shift;
73 my $reel;
74 my $reel_sha1_pair;
76 if ( ref($_[0]) eq 'VCS::Git::Torrent::CommitReel' ) {
77 $reel = shift;
78 $reel_sha1_pair = $reel->reel_id;
80 else {
81 $reel_sha1_pair = [ shift, shift ];
84 my $offset = shift;
85 my $data_len = shift;
86 my $data = shift;
88 my %args;
90 $args{'data'} = $data if ( $data );
91 $args{'data_len'} = $data_len if ( $data_len );
92 $args{'offset'} = $offset;
93 $args{'reel_sha1_pair'} = $reel_sha1_pair;
95 return(%args);
98 sub action {
99 my $self = shift;
100 my $local_peer = shift;
101 my $connection = shift;
103 my ($start, $end) = @{ $self->reel_sha1_pair };
105 if ( $self->data_len && $self->data ) { # we got data
106 my $hose = hose();
108 my $unpack = $local_peer->torrent->plumb(
109 [ 'unpack-objects', '-q' ],
110 input => $hose,
111 # stderr => vent(),
114 $unpack->execute();
115 $hose->print($self->data);
116 $hose->close();
117 $unpack->wait();
119 my $pack_name = $local_peer->torrent->state_dir .
120 '/commit-' . $self->offset . '.pack';
121 open(PACK, '>', $pack_name)
122 || die 'failed to save pack file';
123 syswrite PACK, $self->data, $self->data_len;
124 close(PACK);
126 else { # it was a request for data
127 my $reel;
129 foreach( @{ $local_peer->torrent->reels } ) {
130 $reel = $_;
132 last if (
133 $reel->reel_id->[0] eq $start &&
134 $reel->reel_id->[1] eq $end
138 if ( $reel ) {
139 my $commit = $reel->commit_info->[$self->offset];
141 my $pack;
142 my $pack_name = $local_peer->torrent->state_dir .
143 '/commit-' . $commit->{'objectid'} . '.pack';
144 my $alt_pack_name = $local_peer->torrent->state_dir .
145 '/commit-' . $self->offset . '.pack';
147 if ( -e $pack_name ) {
148 open(PACK, '<', $pack_name)
149 || die 'failed to open pack file';
150 read PACK, $pack, -s $pack_name;
151 close(PACK);
152 } elsif ( -e $alt_pack_name ) {
153 open(PACK, '<', $alt_pack_name)
154 || die 'failed to open pack file';
155 read PACK, $pack, -s $alt_pack_name;
156 close(PACK);
157 } else {
158 my @parents = (
159 $commit->{'parents'} &&
160 scalar(@{ $commit->{'parents'} })
161 ? map {
162 '^' . $_
163 } @{ $commit->{'parents'} }
164 : ()
167 my @cmd = ( 'rev-list', '--objects-edge' );
168 push @cmd, @parents if ( @parents );
169 push @cmd, ( $commit->{'objectid'} );
171 my $rev_list = $local_peer->torrent->plumb(
172 \@cmd,
173 # stderr => vent(),
176 $rev_list->output($local_peer->torrent->plumb(
177 [ 'pack-objects', '--stdout', '-q' ],
178 # stderr => vent(),
181 $pack = $rev_list->terminus->contents;
183 open(PACK, '>', $pack_name)
184 || die 'failed to save pack data';
185 syswrite PACK, $pack;
186 close(PACK);
189 $local_peer->send_message(
190 $connection->remote, GTP_PWP_PLAY,
191 $start, $end, $self->offset,
192 length($pack), $pack
193 ) if ( length($pack) );