chiark / gitweb /
show instance in upload emails
[ypp-sc-tools.web-live.git] / pctb / dictionary-update-receiver
1 #!/usr/bin/perl -w
2 #
3 # This script is invoked when the YPP SC PCTB client talks to the
4 # dictionary server.  See README.privacy.
5
6
7 # upload testing runes:
8 #
9 # YPPSC_PCTB_DICT_UPDATE=./ YPPSC_PCTB_DICT_SUBMIT=./ ./ypp-commodities --ocean midnight --pirate aristarchus --find-island --same --raw-tsv >raw.tsv  
10 # ./dictionary-manager --debug --approve-updates '' . .
11
12 use strict (qw(vars));
13 use POSIX;
14
15 $CGI::POST_MAX= 1024*1024;
16 $CGI::DISABLE_UPLOADS= 1;
17
18 use CGI qw/:standard -private_tempfiles/;
19 use IO::Pipe;
20 use IO::Handle;
21
22 #---------- pixmaps ----------
23
24 sub parseentryin__pixmap ($) {
25     my ($entry_in) = @_;
26     $entry_in =~
27         m/^(\S+ \- .*)\nP3\n([1-9]\d{1,3}) ([1-9]\d{1,3})\n255\n/s or die;
28     my ($def,$w,$h)= ($1, $2+0, $3+0);
29     my @d= grep { m/./ } split /\s+/, $';
30     @d == $w*$h*3 or die "$d[0]|$d[1]|...|$d[$#d-1]|$d[$#d] ?";
31     map {
32         m/\D/ and die "$& ?";
33         $_ += 0;
34         $_ >= 0 or die "$_ ?";
35         $_ <= 255 or die "$_ ?";
36     } @d;
37     my $ppm= "P3\n$w $h\n255\n";
38     my $di=0;
39     for (my $y=0; $y<$h; $y++) {
40         for (my $x=0; $x<$w; $x++, $di+=3) {
41 #print STDERR ">$x,$y,$di,",scalar(@d),"<\n";
42             $ppm .= sprintf "  %3d %3d %3d", @d[$di..$di+2];
43         }
44         $ppm .= "\n";
45     }
46
47     my $icon= pipeval($ppm,
48                          'ppmtopgm',
49                          'pnmscale -xysize 156 80',
50                          'pnmnorm -bpercent 40 -wpercent 20',
51                          'pgmtopbm -threshold',
52                          'pnminvert',
53                          'pbmtoascii -2x4');
54
55     my $whole= pipeval($ppm,
56                        'ppmtopgm',
57                        'pnmnorm -bpercent 10 -wpercent 5',
58                        'pgmtopbm -threshold',
59                        'pnminvert',
60                        'pbmtoascii');
61
62     my $entry= "$def\n$ppm";
63
64     return ('',$def,$ppm,$ppm,$def, $w,$icon,$whole,$entry);
65 }
66
67 #---------- characters ----------
68
69 sub parseentryin__char ($$) {
70     my ($ei,$h) = @_;
71     $ei =~ m/^(Digit|Upper|Lower|Word)\n([^\n]+)\n/s or die;
72     my ($ctx,$str)= ($1,$2);
73 print STDERR ">ctx=$ctx|str=$str|$'<\n";
74     my @d= grep { m/./ } split /\n/, $';
75 print STDERR ">@d<\n";
76     die if $h>100;
77     die if @d>400;
78
79     my $w= @d;
80
81     my $pgm= "P2\n$h $w\n7\n";
82     map { # x, left to right
83         m/[^0-7]/ and die "$_ ?";
84         my $l= $_;
85         $l =~ s/./ $&/g;
86         $pgm .= "$l\n";
87     } @d;
88
89     my $key= join ' ', $ctx, @d;
90
91     $pgm= pipeval($pgm,
92                   "pnmflip -xy",
93                   "pnmnoraw");
94
95     my $icon= pipeval($pgm,
96                       "pnmscale -xysize 156 ".($h*4),
97                       'pgmtopbm -threshold',
98                       'pnminvert',
99                       'pbmtoascii -2x4');
100
101     my $whole= pipeval($pgm,
102                        "pnmscale 4",
103                        'pgmtopbm -fs',
104                        'pnminvert',
105                        'pbmtoascii');
106
107     my $entry= "$ctx\n$str\n$key\n";
108     
109     return ($ctx,$str,$pgm,$key,$str, $w*4,$icon,$whole,$entry);
110 }
111
112 #---------- useful stuff ----------
113
114 sub pipeval ($@) {
115     my ($val, @cmds) = @_;
116     my (@pids);
117
118     my $lastpipe;
119     
120     foreach my $cmd ('',@cmds) {
121         my $pipe= new IO::Pipe or die $!;
122         my $pid= fork();  defined $pid or die $!;
123
124         if (!$pid) {
125             $pipe->writer();
126             if (!$lastpipe) {
127                  print $pipe $val or die $!;
128                  exit 0;
129              } else {
130                  open STDIN, '<&', $lastpipe or die $!;
131                  open STDOUT, '>&', $pipe or die $!;
132                  close $lastpipe or die $!;
133                  close $pipe or die $!;
134                  exec $cmd; die $!;
135              }
136         }
137         $pipe->reader();
138         if ($lastpipe) { close $lastpipe or die $!; }
139         $lastpipe= $pipe;
140         push @pids, $pid;
141     }
142
143     $!=0; { local ($/)=undef; $val= <$lastpipe>; }
144     defined $val or die $!;
145     $lastpipe->error and die $!;  close $lastpipe or die $!;
146
147     foreach my $cmd ('(paste)', @cmds) {
148         my $pid= shift @pids;
149         waitpid($pid,0) == $pid or die "$pid $? $!";
150         $?==0 or $?==13 or die "$cmd $?";
151     }
152     return $val;
153 }
154
155 #========== main program ==========
156
157 #---------- determine properties of the submission ----------
158
159 my $dict= param('dict');
160 my $entry_in= param('entry');
161 defined $entry_in or die Dump()." ?";
162
163 my $ocean= param('ocean');
164 my $pirate= param('pirate');
165 if (defined $ocean && defined $pirate) {
166     $pirate= "$ocean - $pirate";
167 } else {
168     $pirate= '';
169 }
170
171 my $caller= $ENV{'REMOTE_ADDR'};
172 $caller= 'LOCAL' unless defined $caller;
173
174 my $fwdf= $ENV{'HTTP_X_FORWARDED_FOR'};
175 if (defined $fwdf) {
176     $fwdf =~ s/\s//g;
177     $fwdf =~ s/[^0-9.,]/?/g;
178     $caller= "$fwdf,$pirate";
179 }
180
181 my $kind;
182 my @xa;
183
184 if ($dict =~ m/^pixmap$/) {
185     $kind= $&;
186 } elsif ($dict =~ m/^(char)([1-9]\d?)$/) {
187     ($kind,@xa)= ($1,$2);
188 } else {
189     die "$dict ?";
190 }
191 $dict= $&;
192
193 my ($ctx,$def,$image,$key,$val, $width,$icon,$whole,$entry)=
194     &{"parseentryin__$kind"}($entry_in, @xa);
195
196 my $du=$ENV{'YPPSC_DICTUPDATES'};
197 chdir $du or die "$du $!"
198     if defined $du;
199
200 my $instance= $du;
201 $instance =~ s,ypp-sc-tools,,ig;
202 $instance =~ s,ypp,,ig;
203 $instance =~ s,pctb,,ig;
204 $instance =~ s,/\W+/,/,g;
205 $instance =~ s,/+$,,;
206 $instance =~ s,^.*/,,;
207
208 #---------- compute the email to send ----------
209
210 my $whoami= `whoami`; $? and die $?;
211 chomp $whoami;
212
213 my $email= <<END
214 To: $whoami
215 Subject: pctb /$instance/ $dict $ctx $def [ypp-sc-tools]
216
217 Pirate:     $pirate
218 Caller:     $caller
219 Dictionary: $dict
220 Context:    $ctx
221 Definition: $def
222
223 END
224     ;
225
226 if (length $icon) {
227     $icon =~ s/^/ /gm;
228     $email .= "$icon\n\n";
229 }
230
231 $whole =~ s/(.*)\n/ sprintf "%-${width}s\n", $1 /mge;
232 $whole =~ s/^/|/mg;
233 $whole =~ s/\n/|\n/mg;
234 $whole =~ s/^(.*)/ ",".('_' x $width).".\n".$1 /e;
235 $whole =~ s/(.*)$/ $1."\n\`".('~' x $width)."'\n" /e;
236
237 my $lw= 79;
238
239 while ($whole =~ m/../) {
240     my $lhs= $whole;
241     $lhs =~ s/^(.{0,$lw}).*$/$1/mg;
242     $whole =~ s/^.{1,$lw}//mg;
243 #print STDERR "[[[[[$lhs########$whole]]]]]\n";
244     $email .= $lhs;
245 }
246
247 END
248     ;
249
250 my $cutline= "-8<-\n";
251 $email .= $cutline.$entry.$cutline;
252
253 #---------- prepare the database entry ----------
254
255 my $fn_t= "_update.$$-xxxxxxxxxxxxxxxx.tmp";
256 open F, "> $fn_t" or die "$fn_t $!";
257 (stat F) or die $!;
258 my $fn_i= sprintf "_update.$$-%016x.rdy", (stat _)[1];
259
260 print F "ypp-sc-tools dictionary update v2\n";
261
262 foreach my $v ($pirate,$caller,$dict,$ctx,$def,$image,$key,$val) {
263     printf F "%d\n", length($v) or die $!;
264     print F $v,"\n" or die $!;
265 }
266
267 close F or die $!;
268
269 my @tm= localtime;
270 my $tm= strftime "%Y-%m-%d %H:%M:%S %Z", @tm;
271
272 open L, ">> _dict.log" or die $!;
273 my $ll= sprintf "%s %-6s %-31s %-31s %s", $tm, $dict, $pirate, $caller, $fn_i;
274
275 #---------- commit everything ----------
276
277 print L "$ll submit\n" or die $!;
278 L->flush or die $!;
279
280 if (eval {
281
282     open S, "|/usr/lib/sendmail -odb -oee -oi -t" or die $!;
283     print S $email or die $!;
284     $!=0; $?=0; close S or die $!; $? and die $?;
285
286     rename $fn_t, $fn_i or die "$fn_t $fn_i $!";
287
288     1;
289 }) {
290     print L "$ll stored\n" or die $!;
291 } else {
292     print L "$ll ERROR! $@\n" or die $!;
293     exit 1;
294 }
295 close L or die $!;
296
297 print header('text/plain'), "OK $fn_i\n" or die $!;