]> matita.cs.unibo.it Git - helm.git/blob - helm/http_getter/http_getter.pl.in
Added control on some "open" call (with 'or die ...')
[helm.git] / helm / http_getter / http_getter.pl.in
1 #!@PERL_BINARY@
2
3 # Copyright (C) 2000, HELM Team.
4
5 # This file is part of HELM, an Hypertextual, Electronic
6 # Library of Mathematics, developed at the Computer Science
7 # Department, University of Bologna, Italy.
8
9 # HELM is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU General Public License
11 # as published by the Free Software Foundation; either version 2
12 # of the License, or (at your option) any later version.
13
14 # HELM is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18
19 # You should have received a copy of the GNU General Public License
20 # along with HELM; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 # For details, see the HELM World-Wide-Web page,
24 # http://cs.unibo.it/helm/.
25
26 # First of all, let's load HELM configuration
27 use Env;
28 my $HELM_LIB_DIR = $ENV{"HELM_LIB_DIR"};
29 # this should be the only fixed constant
30 my $DEFAULT_HELM_LIB_DIR = "@HELM_LIB_DIR@";
31 if (defined ($HELM_LIB_DIR)) {
32    $HELM_LIB_PATH = $HELM_LIB_DIR."/configuration.pl";
33 } else {
34    $HELM_LIB_PATH = $DEFAULT_HELM_LIB_DIR."/configuration.pl";
35 }
36
37 # <ZACK>: TODO temporary, move this setting to configuration file
38 # set the cache mode, may be "gzipped" or "normal"
39 my $cachemode = $ENV{'HTTP_GETTER_CACHE_MODE'} || 'gzipped';
40 if (($cachemode ne 'gzipped') and ($cachemode ne 'normal')) {
41         die "Invalid HTTP_GETTER_CACHE_MODE environment variable, must be 'normal' or 'gzipped'\n";
42 }
43 # </ZACK>
44
45 # next require defines: $helm_dir, $html_link, $dtd_dir, $uris_dbm
46 require $HELM_LIB_PATH;
47
48 use HTTP::Daemon;
49 use HTTP::Status;
50 use HTTP::Request;
51 use LWP::UserAgent;
52 use DB_File;
53 use Compress::Zlib;
54
55 #CSC: mancano i controlli sulle condizioni di errore di molte funzioni
56 #CSC: ==> non e' robusto
57 #CSC: altra roba da sistemare segnata con CSC
58
59 my $d = new HTTP::Daemon LocalPort => 8081;
60 tie(%map, 'DB_File', $uris_dbm.".db", O_RDONLY, 0664);
61 print "Please contact me at: <URL:", $d->url, ">\n";
62 print "helm_dir: $helm_dir\n";
63 print "dtd_dir: $dtd_dir\n";
64 print "urls_of_uris.db: $uris_dbm.db\n";
65 print "cache mode: $cachemode\n";
66
67 $SIG{CHLD} = "IGNORE"; # do not accumulate defunct processes
68 $SIG{USR1} = \&update; # sent by the child to make the parent update
69 while (my $c = $d->accept) {
70  if (fork() == 0) {
71     while (my $r = $c->get_request) {
72         #CSC: mancano i controlli di sicurezza
73         
74         my $inputuri = $r->url; 
75         $inputuri =~ s/^[^?]*\?uri=(.*)/$1/;
76         print "\nRequest: ".$r->url."\n\n";
77         my $http_method = $r->method;
78         my $http_path = $r->url->path;
79
80         if ($http_method eq 'GET' and $http_path eq "/getciconly") {
81             # finds the uri, url and filename
82             my $cicuri = $inputuri;
83
84             my $cicfilename = $cicuri;
85             $cicfilename =~ s/cic:(.*)/$1/;
86             $cicfilename =~ s/theory:(.*)/$1/;
87 #            $cicfilename = $helm_dir.$cicfilename.".xml";
88 # <gzip>
89             my $cicurl   = $map{$cicuri};
90                         my $extension;
91                         if ($cicurl =~ /\.xml$/) {      # non gzipped file
92                                         $extension = ".xml";
93                         } elsif ($cicurl =~ /\.xml\.gz$/) {     # gzipped file
94                                         $extension = ".xml.gz";
95                         } else {        # error: unknown extension
96                                 die "unexpected extension in url: $cicurl, might be '.xml' or '.xml.gz'";
97                         }
98             $cicfilename = $helm_dir.$cicfilename.$extension;
99
100             #my $cicurl   = $map{$cicuri};
101 # </gzip>
102             if (!defined($cicurl)) {
103              print "\nNOT FOUND!!!!!\n";
104              $c->send_error(RC_NOT_FOUND)
105             } else {
106                print_request("cic",$cicuri,$cicurl,$cicfilename);
107
108                # Retrieves the file
109                my $ciccontent = download(0,"cic",$cicurl,$cicfilename);
110
111                # Answering the client
112                answer($c,$ciccontent);
113             }
114         } elsif ($http_method eq 'GET' and $http_path eq "/get") {
115             # finds the uris, urls and filenames
116             my $cicuri = $inputuri,
117                $typesuri = $inputuri,
118                $annuri = $inputuri;
119             my $annsuffix;
120             if ($inputuri =~ /\.types$/) {
121                $cicuri    =~ s/(.*)\.types$/$1/;
122                undef($annuri);
123             } elsif ($inputuri =~ /\.types\.ann$/) {
124                $cicuri    =~ s/(.*)\.types\.ann$/$1/;
125                $typesuri  =~ s/(.*)\.ann$/$1/;
126                $annsuffix = ".types.ann";
127             } elsif ($inputuri =~ /\.ann$/) {
128                $cicuri  =~ s/(.*)\.ann$/$1/;
129                undef($typesuri);
130                $annsuffix = ".ann";
131             } else {
132                undef($typesuri);
133                undef($annuri);
134             }
135
136             my $cicfilename = $cicuri;
137             $cicfilename =~ s/cic:(.*)/$1/;
138             $cicfilename =~ s/theory:(.*)/$1/;
139             $cicfilename = $helm_dir.$cicfilename;
140
141 #            my $typesfilename = $cicfilename.".types.xml"     if $typesuri;
142 #            my $annfilename  = $cicfilename.$annsuffix.".xml" if $annuri;
143 #            $cicfilename .= ".xml";
144
145 # <gzip>
146             my $cicurl   = $map{$cicuri};
147             my $typesurl = $map{$typesuri} if (defined($typesuri));
148             my $annurl   = $map{$annuri}  if (defined($annuri));
149                         my ($cicext, $typesext, $annext);
150                         if ($cicurl =~ /\.xml$/) {      # normal file
151                                 $cicext = ".xml";
152                         } elsif ($cicurl =~ /\.xml\.gz$/) {     # gzipped file
153                                 $cicext = ".xml.gz";
154                         } else {
155                                 die "unexpected extension in url: $cicurl; might be '.xml' or '.xml.gz'";
156                         }
157                         if (defined($typesuri)) {       # extension selection for types file
158                                 if ($typesurl =~ /\.xml$/) {    # normal file
159                                         $typesext = ".types.xml";
160                                 } elsif ($typesurl =~ /\.xml\.gz$/) {   # gzipped file
161                                         $typesext = ".types.xml.gz";
162                                 } else {
163                                         die "unexpected extension in url: $typesurl; might be '.xml' or '.xml.gz'";
164                                 }
165                         }
166                         if (defined($annuri)) { # extension selection for annotation file
167                                 if ($annurl =~ /\.xml$/) {      # normal file
168                                         $annext = ".xml";
169                                 } elsif ($annurl =~ /\.xml\.gz$/) {     # gzipped file
170                                         $annext = ".xml.gz";
171                                 } else {
172                                         die "unexpected extension in url: $annurl might be '.xml' or '.xml.gz'";
173                                 }
174                         }
175             my $typesfilename = $cicfilename.$typesext if $typesuri;
176             my $annfilename  = $cicfilename.$annsuffix.$annext if $annuri;
177             $cicfilename .= $cicext;
178 # </gzip>
179
180
181             if (!defined($cicurl) ||
182                (!defined($typesurl) && $typesuri) ||
183                (!defined($annuri) && $annuri))
184             {
185              print "\nNOT FOUND!!!!!\n";
186              $c->send_error(RC_NOT_FOUND)
187             } else {
188                print_request("cic",$cicuri,$cicurl,$cicfilename);
189                print_request("types",$typesuri,$typesurl,$typesfilename)
190                 if ($typesuri);
191                print_request("ann",$annuri,$annurl,$annfilename)
192                 if ($annuri);
193  
194                # Retrieves the files
195
196                my $ciccontent = download(1,"cic",$cicurl,$cicfilename);
197                my $typescontent =
198                 download(1,"types",$typesurl,$typesfilename) if ($typesuri);
199                my $anncontent =
200                 download(1,"ann",$annurl,$annfilename) if ($annuri);
201  
202                # Merging the files together
203  
204                my $merged = <<EOT;
205 <?xml version="1.0" encoding="UTF-8"?>
206 <cicxml uri="$cicuri">
207 $ciccontent
208 $typescontent
209 $anncontent
210 </cicxml>
211 EOT
212
213                # Answering the client
214                answer($c,$merged);
215             }
216          } elsif ($http_method eq 'GET' and $http_path eq "/getdtd") {
217             my $filename = $inputuri;
218             $filename = $dtd_dir."/".$filename;
219             print "DTD: $inputuri ==> ($filename)\n";
220             if (stat($filename)) {
221                print "Using local copy\n";
222                open(FD, $filename) or die "Cannot open $filename\n";
223                $cont = "";
224                while(<FD>) { $cont .= $_; }
225                close(FD);
226                answer($c,$cont);
227             } else {
228                die "Could not find DTD!";
229             }
230         } elsif ($http_method eq 'GET' and $http_path eq "/conf") {
231             my $quoted_html_link = $html_link;
232             $quoted_html_link =~ s/&/&amp;/g;
233             $quoted_html_link =~ s/</&lt;/g;
234             $quoted_html_link =~ s/>/&gt;/g;
235             $quoted_html_link =~ s/'/&apos;/g;
236             $quoted_html_link =~ s/"/&quot;/g;
237             print "\nConfiguration requested, returned #$quoted_html_link#\n";
238             $cont = "<?xml version=\"1.0\"?><html_link>$quoted_html_link</html_link>";
239             answer($c,$cont);
240         } elsif ($http_method eq 'GET' and $http_path eq "/update") {
241            print "Update requested...";
242            update();
243            kill(USR1,getppid());
244            print " done\n";
245            answer($c,"<html><body><h1>Update done</h1></body></html>");
246         } else {
247             print "\nINVALID REQUEST!!!!!\n";
248             $c->send_error(RC_FORBIDDEN)
249         }
250         print "\nRequest solved: ".$r->url."\n\n";
251     }
252     $c->close;
253     undef($c);
254     print "\nCONNECTION CLOSED\n\n";
255     exit;
256   } # fork
257 }
258
259 #================================
260
261
262 #CSC: Too much powerful: creates even /home, /home/users/, ...
263 #CSC: Does not raise errors if could not create dirs/files
264 sub mkdirs
265 {
266  my ($pathname) = @_;
267  my @dirs = split /\//,$pathname;
268  my $tmp;
269  foreach $dir (@dirs) {
270   $tmp = ((defined($tmp)) ?  $tmp."\/".$dir : "");
271   mkdir($tmp,0777);
272  }
273  rmdir($tmp);
274 }
275
276 sub print_request
277 {
278  my ($str,$uri,$url,$filename) = @_;
279  print $str."uri: $uri\n";
280  print $str."url: $url\n";
281  print $str."filename: $filename\n\n";
282 }
283
284 sub callback
285 {
286  my ($data) = @_;
287  $cont .= $data;
288 }
289
290 sub gunzip { # gunzip a file and return the deflated content
291         my ($filename) = @_;
292
293         my ($gz, $buffer, $cont);
294
295         print "deflating $filename ...\n";
296         $gz = gzopen($filename, "r") or die "Cannot open gzip'ed file $filename: $gzerrno";
297         $cont = "";
298         while ( $gz->gzread($buffer) > 0 ) {
299                 $cont .= $buffer;
300         }
301         die "Error while reading : $gzerrno\n" if $gzerrno != Z_STREAM_END ;
302         $gz->gzclose();
303
304         return $cont;
305 }
306
307 sub gzip {      # gzip the content argument and save it to filename argument
308         my ($cont, $filename) = @_;
309
310         my ($gz, $cont);
311
312         $gz = gzopen($filename, "w") or die "Cannot gzopen for writing file $filename: $gzerrno";
313         $gz->gzwrite($cont) or die "error writing: $gzerrno\n" ;
314         $gz->gzclose();
315 }
316
317 sub download
318 {
319  my ($remove_headers,$str,$url,$filename) = @_;
320 # <gzip>
321  my ($gz, $buffer);
322
323  my $resourcetype;      # retrieve mode: "normal" (.xml) or "gzipped" (.xml.gz)
324  if ($filename =~ /\.xml$/) {   # set retrieve mode
325          $resourcetype = "normal";
326  } elsif ($filename =~ /\.xml\.gz$/) {
327          $resourcetype = "gzipped";
328  } else {
329          die "Unsupported download extension, might be '.gz' or '.xml'\n";
330  }
331  my $basefname = $filename;
332  $basefname =~ s/\.gz$//;               # get base resource name removing trailing .gz
333 # </gzip>
334  $cont = ""; # modified by side-effect by the callback function
335
336  my $localfname="";
337  if (stat($basefname)) {
338         $localfname=$basefname;
339  } elsif (stat($basefname.".gz")) {
340         $localfname=$basefname.".gz";
341  }
342  if ($localfname ne "") {               # we already have local copy of requested file
343                                                                 # check both possible cache entry: gzipped or normal
344     print "Using local copy for the $str file\n";
345 # <gzip>
346         if ($localfname =~ /\.xml\.gz$/) {      # deflating cached file and return it
347                 $cont = gunzip($localfname);
348         } elsif ($localfname =~ /\.xml$/) {     # just return cached file
349                 open(FD, $localfname) or die "Cannot open $localfname";
350                 while(<FD>) { $cont .= $_; }
351                 close(FD);
352         } else {        # error
353                 die "Internal error: unexpected file name $localfname, must end with '.gz' or '.xml.gz'\n";
354         }
355 # </gzip>
356  } else {       # download file from net
357     print "Downloading the $str file\n";        # download file
358     $ua = LWP::UserAgent->new;
359     $request = HTTP::Request->new(GET => "$url");
360     $response = $ua->request($request, \&callback);
361                
362                                 # cache retrieved file to disk
363 # <ZACK/> TODO: inefficent, I haven't yet undestood how to deflate in memory gzipped file,
364 #                               without call "gzopen"
365 # <gzip>
366         print "Storing the $str file\n";
367         mkdirs($filename);
368         open(FD, ">".$filename.".tmp") or die "Cannot open $filename.tmp\n";
369         print FD $cont;
370         close(FD);
371
372         # handle cache conversion normal->gzipped or gzipped->normal as user choice
373         if (($cachemode eq 'normal') and ($resourcetype eq 'normal')) { # cache the file as is
374                 rename "$filename.tmp", $filename;      
375         } elsif (($cachemode eq 'gzipped') and ($resourcetype eq 'gzipped')) {  # cache the file as is
376                                                                                         # and update the $cont variabile with deflated content
377                 rename "$filename.tmp", $filename;      
378                 $cont = gunzip($filename);
379         } elsif (($cachemode eq 'normal') and ($resourcetype eq 'gzipped')) {   # deflate cache entry
380                                                                                                                                                         # and update $cont
381                 open(FD, "> $basefname") or die "cannot open $basefname\n";
382                 $cont = gunzip($filename.".tmp");
383                 print FD $cont;
384                 close(FD);
385                 unlink "$filename.tmp"; # delete old gzipped file
386         } elsif (($cachemode eq 'gzipped') and ($resourcetype eq 'normal')) {   # compress cache entry
387                 gzip($cont, $basefname.".gz");
388                 unlink "$filename.tmp"; # delete old uncompressed file
389         } else {
390                 die "Internal error, unsopported cachemode, resourcetype couple\n";
391         }
392         # $cont now contained uncompressed data
393
394 # </gzip>
395  }
396  if ($remove_headers) {
397     $cont =~ s/<\?xml [^?]*\?>//sg;
398     $cont =~ s/<!DOCTYPE [^>]*>//sg;
399  }
400  return $cont;
401 }
402
403 sub answer
404 {
405  my ($c,$cont) = @_;
406  my $res = new HTTP::Response;
407  $res->content($cont);
408  $c->send_response($res);
409 }
410
411 sub update {
412  untie %map;
413  tie(%map, 'DB_File', $uris_dbm.".db", O_RDONLY, 0664);
414 }