X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?a=blobdiff_plain;f=pctb%2Fcommod-update-receiver;h=2c02c11bcb74603ada8f65b062988699d66ec00a;hb=8651c66dd983df3a05eff010cef635e641a41d8f;hp=531ff1a2e5ac05e17f7bff891ddc07c7f416d08d;hpb=c60024527fa80e1b572c95d4763350e691f9d4f3;p=ypp-sc-tools.db-live.git diff --git a/pctb/commod-update-receiver b/pctb/commod-update-receiver index 531ff1a..2c02c11 100755 --- a/pctb/commod-update-receiver +++ b/pctb/commod-update-receiver @@ -32,15 +32,147 @@ # clientname "ypp-sc-tools" # clientversion 2.1-g2e06a26 [from git-describe --tags HEAD] # clientfixes "lastpage" [space separated list] -# deduped.tsv.gz output of ypp-commodities --tsv +# data filename=deduped.tsv.gz output of ypp-commodities --tsv + use strict (qw(vars)); use POSIX; +use MIME::Entity; + +use Commods; $CGI::POST_MAX= 3*1024*1024; -$CGI::DISABLE_UPLOADS= 1; use CGI qw/:standard -private_tempfiles/; setlocale(LC_CTYPE, "en_GB.UTF-8"); +my $re_any= "^(.*)\$"; + +parse_masters(); + +sub fail ($) { + my ($msg) = @_; + print header(-status=>'400 Bad commodity update', + -type=>'text/plain', + -charset=>'us-ascii'); + print "Error: $msg\n"; + exit 0; +} + +sub must_param ($$) { + my ($n,$re)= @_; + my $v= param($n); + fail("missing form parameter $n") unless defined $v; + fail("invalid form parameter $n ($re)") unless $v =~ m/$re/; + return $1; +} + +my %o; + +$o{'clientname'}= must_param('clientname',$re_any); +my $clientinfo= $clients{$o{'clientname'}}; +fail('unknown client') unless defined $clientinfo; + +my $clientfixes= must_param('clientfixes', "^([-0-9a-z ]*)\$"); +my @clientfixes= sort grep { m/./ } split /\s+/, $clientfixes; +$o{'clientfixes'}= "@clientfixes"; +foreach my $bug (@$clientinfo) { + fail("client out of date - missing bugfix \`$bug'") + unless grep { $_ eq $bug } @clientfixes; +} + +$o{'clientversion'}= must_param('clientversion', "^(\\d[-+._0-9a-zA-Z]+)\$"); + +$o{'ocean'}= must_param('ocean', $re_any); +$o{'island'}= must_param('island', $re_any); + +my $arches= $oceans{$o{'ocean'}}; +fail("unknown ocean") unless $arches; + +my $island_found= 0; +foreach my $islands (values %$arches) { + my $sources= $islands->{$o{'island'}}; + next unless $sources; + die if $island_found; + $island_found= $sources; +} +fail("unknown island") unless $island_found; + +$o{'timestamp'}= must_param('timestamp', "^([1-9]\\d{1,20})\$"); +my $now= time; defined $now or die $!; +fail("clock skew") if $o{'timestamp'} >= $now; + +my $indatafh= upload('data'); +defined $indatafh or fail("data is not a file"); +my $datafile= must_param('data',"^(deduped\\.tsv\\.gz)\$"); + +my $mcontent= MIME::Entity->build(To => 'yarrg-commod-updates', + Type => 'multipart/mixed', + Boundary => '=', + Charset => 'utf-8'); + +get_our_version(\%o, 'server'); +foreach my $cs (qw(client server)) { + $o{"${cs}spec"}= join "\t", map { $o{$cs.$_} } qw(name version fixes); +} + +my $metadata= ''; + +sub ksmap ($) { + my ($v) = @_; + my $i=0; grep { return $i if $_ eq $v; $i++ } qw(ocean island timestamp); + sprintf "z %d %s", (length $v) / 8, $v; +} + +foreach my $vn (sort { ksmap($a) cmp ksmap($b) } keys %o) { + my $val= $o{$vn}; + die if $val =~ m/\n|\r/; + $metadata .= "$vn\t$o{$vn}\n"; +} + +my $mdpart= MIME::Entity->build(Top => 0, + Type => 'text/plain', + Charset => 'utf-8', + Disposition => 'inline', + Encoding => 'quoted-printable', + Filename => 'metadata', + Data => $metadata); +$mcontent->add_part($mdpart); + +my $gunzchild= open(GZ, "-|"); defined $gunzchild or die $!; +if (!$gunzchild) { + open STDIN, "<&=", $indatafh or die $!; + exec 'gunzip'; die $!; +} + +my $dedupedtsv= pipethrough_prep(); + +while () { + my @v= check_tsv_line($_,\&fail); + print $dedupedtsv join('\t',@v),"\n" or die $!; +} + +GZ->error and die $!; +$?=0; close GZ; $? and fail("gunzip for check failed code $?"); + +my $launderedgz= pipethrough_run($dedupedtsv,undef,'gzip','gzip'); + +my $mdatafile= MIME::Entity->build(Top => 0, + Type => 'application/octet-stream', + Disposition => 'attachment', + Encoding => 'base64', + Filename => 'deduped.tsv.gz', + Data => $launderedgz); +$mcontent->add_part($mdatafile); + +open M, "|/usr/sbin/sendmail -t -oi -oee -odb" + or fail("fork sendmail failed! ($!)"); +$mcontent->print(\*M); + +M->error and fail("write sendmail failed! ($!)"); +$?=0; close M; $? and fail("sendmail failed code $?"); + +print header(-type=>'text/plain', -charset=>'us-ascii'), + "OK\n" + or die $!;