3 # fexsrv : web server for F*EX service
5 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
11 use Fcntl qw':flock :seek';
16 $SIG{CHLD} = "DEFAULT";
18 $ENV{LC_ALL} = 'en_US.UTF-8';
19 unshift @INC,(getpwuid($<))[7].'/perl';
21 $SIG{__DIE__} = $SIG{__WARN__} = sub {
23 my $url = $ENV{REQUEST_URL}||'';
24 my @d = localtime time;
25 my $time = sprintf('%d-%02d-%02d %02d:%02d:%02d',
26 $d[5]+1900,$d[4]+1,$d[3],$d[2],$d[1],$d[0]);
28 my $mailto = "mailto:$admin?subject=fex%20bug";
29 $info = "<h3>send this error to <a href=\"$mailto\">$admin</a></h3>";
37 "<h1>INTERNAL ERROR in $0</h1>",
38 "<pre>\n$_\n</pre>\n<p>",
45 unless ($HTTP_HEADER) {
46 print "HTTP/1.0 200 ERROR\r\n";
47 print "Content-Type: text/html\r\n";
48 print "Content-Length: $length\r\n";
59 # setrlimit(RLIMIT_CPU,999,999) or die "$0: $!\n";
61 # SSL remote address provided by stunnel
62 if (@ARGV and $ARGV[0] eq 'stunnel' and $ENV{REMOTE_HOST} =~ /(.+)/) {
66 # KEEP_ALIVE <== callback from CGI
67 if ($ENV{KEEP_ALIVE}) {
68 $keep_alive = $ENV{KEEP_ALIVE};
70 %ENV = ( PERLINIT => $ENV{PERLINIT} ); # clean environment
73 $ENV{HOME} = (getpwuid($<))[7] or die "no HOME";
75 # fexsrv MUST be run with full path!
76 if ($0 =~ m:^(/.+)/bin/fexsrv:) {
80 $ENV{FEXHOME} = $FEXHOME;
86 '/usr/local/share/fex',
89 $ENV{FEXLIB} = $FEXLIB = $lib and last if -f "$lib/fex.pp";
90 $ENV{FEXLIB} = $FEXLIB = "$lib/lib" and last if -f "$lib/lib/fex.pp";
94 our ($hostname,$debug,$timeout,$max_error,$max_error_handler);
95 our ($spooldir,@logdir,$docdir,$xkeydir,$akeydir,$lockdir);
96 our ($force_https,$default_locale,$bs,$MB,$adlm,@forbidden_user_agents);
99 # load common code (local config: $FEXHOME/lib/fex.ph)
100 require "$FEXLIB/fex.pp" or die "cannot load $FEXLIB/fex.pp - $!\n";
102 chdir $spooldir or http_die("$0: $spooldir - $!\n");
104 our $log = 'fexsrv.log';
105 our $error = 'F*EX ERROR';
107 our $hid = ''; # header ID
112 $ENV{GATEWAY_INTERFACE} = 'CGI/1.1f';
113 $ENV{SERVER_NAME} = $hostname;
114 $ENV{REQUEST_METHOD} = '';
115 $ENV{QUERY_STRING} = '';
116 $ENV{HTTP_COOKIE} = '';
117 $ENV{PATH_INFO} = '';
118 $ENV{RANDOM} = randstring(8);
119 $ENV{FEATURES} = join(',',qw(
120 SID CHECKRECIPIENT GROUPS QUOTA FILEID MULTIPOST XKEY FILEQUERY FILESTREAM
121 JUP NOSTORE AXEL FEXMAIL FILELINK
128 if ($ENV{HTTP_HOST} =~ /(.+):(.+)/) {
132 $hostname = $ENV{HTTP_HOST};
133 if ($ENV{PROTO} eq 'https') { $port = 443 }
136 $ra = $ENV{REMOTE_ADDR};
137 $rh = $ENV{REMOTE_HOST};
146 $ENV{PROTO} = 'https';
147 $ENV{REMOTE_ADDR} = $ra = $ssl_ra;
148 if ($ssl_ra =~ /[\w:]:\w/) {
149 # ($rh) = `host $ssl_ra 2>/dev/null` =~ /name pointer (.+)\.$/;
150 $^W = 0; eval 'use Socket6'; $^W = 1;
151 http_error(503) if $@;
152 $iaddr = inet_pton(AF_INET6,$ssl_ra) and
153 $rh = gethostbyaddr($iaddr,AF_INET6);
155 $rh = gethostbyaddr(inet_aton($ra),AF_INET);
159 # print {$log} "X-SSL-Remote-Host: $ssl_ra\n";
164 $ENV{PROTO} = 'http';
165 my $sa = getpeername(STDIN) or die "no network stream on STDIN\n";
166 if (sockaddr_family($sa) == AF_INET) {
167 ($ENV{REMOTE_PORT},$iaddr) = sockaddr_in($sa);
168 $ENV{REMOTE_ADDR} = $ra = inet_ntoa($iaddr);
169 $rh = gethostbyaddr($iaddr,AF_INET);
170 ($port) = sockaddr_in(getsockname(STDIN));
171 } elsif (sockaddr_family($sa) == AF_INET6) {
172 $^W = 0; eval 'use Socket6'; $^W = 1;
173 http_error(503) if $@;
174 ($ENV{REMOTE_PORT},$iaddr) = unpack_sockaddr_in6($sa);
175 $ENV{REMOTE_ADDR} = $ra = inet_ntop(AF_INET6,$iaddr);
176 $rh = gethostbyaddr($iaddr,AF_INET6);
177 ($port) = unpack_sockaddr_in6(getsockname(STDIN));
179 die "unknown IP version\n";
181 $port = 80 unless $port;
184 $ENV{REMOTE_HOST} = $rh || '';
186 $ENV{HTTP_HOST} = ($port == 80 or $port == 443)
187 ? $hostname : "$hostname:$port";
192 if ($reverse_proxy_ip and $reverse_proxy_ip eq $ra) {
193 $ENV{FEATURES} =~ s/SID,//;
196 if (@anonymous_upload and ipin($ra,@anonymous_upload)) {
197 $ENV{FEATURES} .= ',ANONYMOUS';
202 $SIG{CHLD} = "DEFAULT"; # stunnel workaround
205 # printf {$log} "\nTIMEOUT %s %s\n",isodate(time),$connect;
207 debuglog('TIMEOUT',isodate(time));
208 fexlog($connect,@log,"TIMEOUT");
213 REQUEST: while (*STDIN) {
215 if (defined $ENV{REQUESTCOUNT}) { $ENV{REQUESTCOUNT}++ }
216 else { $ENV{REQUESTCOUNT} = 0 }
218 $connect = sprintf "%s:%s %s %s %s [%s_%s]",
219 $keep_alive ? 'CONTINUE' : 'CONNECT',
224 $$,$ENV{REQUESTCOUNT};
225 $hid = sprintf("%s %s\n",$rh||'-',$ra);
230 # read complete HTTP header
231 while (defined ($_ = &getaline)) {
236 # URL-encode non-printable chars
237 s/([\x00-\x08\x0E-\x1F\x7F-\x9F])/sprintf "%%%02X",ord($1)/ge;
239 if (@header and s/^\s+/ /) {
243 $header{$1} = $2 if /(.+)\s*:\s*(.+)/;
247 fexlog($connect,@log,"OVERRUN");
251 if (/^(GET \/|\S*Forwarded|\S*Client-IP|\S*Coming-From|User-Agent)/i) {
256 # (only IPv4 support!)
257 if ($reverse_proxy_ip and $reverse_proxy_ip eq $ra and
258 /^\S*(Forwarded|Client-IP|Coming-From)\S*: ([\da-f:.]+)/i
260 $ENV{REMOTE_ADDR} = $ra = $2;
261 $ENV{REMOTE_HOST} = $rh = gethostbyaddr(inet_aton($ra),AF_INET) || '';
262 $ENV{HTTP_HOST} = $hostname;
263 if ($ENV{PROTO} eq 'https') { $port = 443 }
269 exit if $header =~ /^\s*$/;
271 $ENV{HTTP_HEADER} = $header;
273 # http_die("<pre>$header</pre>");
275 $ENV{'HTTP_HEADER_LENGTH'} = $hl;
276 $ENV{REQUEST_URI} = $uri = '';
279 # is it a HTTP-request at all?
280 $request = shift @header;
281 if ($request !~ /^(GET|HEAD|POST|OPTIONS).*HTTP\/\d\.\d$/i) {
282 fexlog($connect,$request,"DISCONNECT: no HTTP request");
283 badlog("no HTTP request: $request");
287 if ($force_https and $port != 443
288 and $request =~ /^(GET|HEAD|POST)\s+(.+)\s+(HTTP\/[\d\.]+$)/i) {
291 "HTTP/1.1 301 Moved Permanently",
292 "Location: https://$hostname$request",
296 fexlog($connect,@log);
300 $request =~ s{^(GET|HEAD|POST) https?://$hostname(:\d+)?}{$1 }i;
302 if ($request =~ m"^(GET|HEAD) /fop/\w+/") {
303 # no header inquisition on regular fop request
306 &$header_hook($connect,$request,$ra) if $header_hook;
309 unless ($keep_alive) {
310 if ($request =~ m:(HTTP/1.(\d)): and $2) {
311 $ENV{KEEP_ALIVE} = $keep_alive = $ra
313 $ENV{KEEP_ALIVE} = $keep_alive = '';
317 if ($request =~ /^OPTIONS \/?FEX HTTP\/[\d\.]+$/i) {
318 fexlog($connect,@log);
321 "X-Features: $ENV{FEATURES}",
322 "X-Timeout: $timeout",
325 next REQUEST if $keep_alive;
329 if ($request =~ m:^GET /?SID HTTP/[\d\.]+$:i) {
330 if ($ENV{FEATURES} !~ /\bSID\b/) {
331 fexlog($connect,@log);
333 "HTTP/1.1 501 Not Available",
335 "X-Features: ".$ENV{FEATURES},
336 "X-Timeout: ".$timeout,
341 $ENV{SID} = randstring(8);
342 fexlog($connect,@log);
344 "HTTP/1.1 201 ".$ENV{SID},
346 "X-Features: ".$ENV{FEATURES},
348 "X-Timeout: ".$timeout,
353 next REQUEST if $keep_alive;
357 if ($request =~ /^(GET|HEAD|POST)\s+(.+)\s+(HTTP\/[\d\.]+$)/i) {
359 $ENV{REQUEST_METHOD} = uc($1);
360 $ENV{REQUEST_URI} = $uri = $cgi = $2;
361 $ENV{HTTP_VERSION} = $protocol = $3;
362 $ENV{QUERY_STRING} = $1 if $cgi =~ s/\?(.*)//;
363 $ENV{PATH_INFO} = $1 if $cgi =~ m:/.+?(/.+?)(\?|$):;
364 $ENV{KEEP_ALIVE} = $keep_alive = '' if $protocol =~ /1\.0/;
365 $ENV{REQUEST_URL} = "$ENV{PROTO}://$ENV{HTTP_HOST}$ENV{REQUEST_URI}";
366 if ($uri =~ /<|%3c/i) { badchar("<") }
367 if ($uri =~ />|%3e/i) { badchar(">") }
368 if ($uri =~ /\||%7c/i) { badchar("|") }
369 if ($uri =~ /\\|%5c/i) { badchar("\\") }
372 my $fua = join('|',@forbidden_user_agents);
374 while ($_ = shift @header) {
376 # header inquisition!
377 &$header_hook($connect,$_,$ra) if $header_hook;
379 # mega stupid "Download Manager" FlashGet
380 if ($uri =~ m{^/fop/} and m{^Referer: https?://.*\Q$uri$}) {
381 fexlog($connect,@log,"NULL: FlashGet");
382 debuglog("NULL: FlashGet");
383 exec qw'cat /dev/zero' or sleep 30;
387 if ($fua and /^User-Agent: ($fua)/) {
388 disconnect($1,"499 User Agent $1 Not Supported",30);
392 disconnect("Range a,b","416 Requested Range Not Satisfiable",30);
394 if (/^Range:.*(\d+)-(\d+)/) {
396 disconnect("Range a>b","416 Requested Range Not Satisfiable",0);
398 if (($header{'User-Agent'}||'') !~ /$adlm/ ) {
399 disconnect("Range a-b","416 Requested Range Not Satisfiable",30);
403 if (/^Range:.*\d+-$/ and $hid) {
404 my $lock = untaint($lockdir.'/'.md5_hex($hid));
405 if (open $lock,'+>>',$lock) {
406 if (flock($lock,LOCK_EX|LOCK_NB)) {
412 "multiple Range request",
413 "400 Multiple Requests Not Allowed",
420 # client signed int bug
421 if (/^Range:.*-\d+-/) {
422 disconnect("Range -a-","416 Requested Range Not Satisfiable",0);
425 # if (/^Range:/ and $protocol =~ /1\.0/) {
426 # &$header_hook($connect,$_,$ra) while ($header_hook and $_ = shift @header);
427 # fexlog($connect,@log,"DISCONNECT: Range + HTTP/1.0");
428 # debuglog("DISCONNECT: Range + HTTP/1.0");
433 if (/^Connection:\s*close/i) {
434 $ENV{KEEP_ALIVE} = $keep_alive = '';
437 # HTTP header ==> environment variables
438 if (/^([\w\-_]+):\s*(.+)/s) {
441 $http_var =~ s/-/_/g;
442 $http_var = uc($http_var);
443 $http_val =~ s/^\s+//;
444 $http_val =~ s/\s+$//;
445 if ($http_var =~ /^X_(FEX_\w+|CONTENT_LENGTH)$/) {
448 $http_val =~ s/\s+/ /g;
449 if ($http_var =~ /^HTTP_(HOST|VERSION)$/) {
450 $http_var = 'HTTP_X_'.$1;
451 } elsif ($http_var =~ /^PROXY/) {
452 # http://cert.at/warnings/all/20160718.html
453 $http_var = 'HTTP_X_'.$http_var;
454 } elsif ($http_var !~ /^CONTENT_/) {
455 $http_var = 'HTTP_'.$http_var;
458 $ENV{$http_var} = $http_val;
462 # multiline header inquisition
463 &$header_hook($connect,$header,$ra) if $header_hook;
467 # extra download request? (request http://fexserver//xkey)
468 if ($cgi =~ m{^//([^/]+)$}) {
471 if ($xkey =~ /^afex_\d/) {
472 $dkey = readlink "$xkeydir/$xkey" and $dkey =~ s/^\.\.\///;
474 $dkey = readlink "$xkeydir/$xkey/dkey" and $dkey .= "/$xkey";
477 # xkey downloads are only one time possible - besides afex
478 if ($xkey !~ /^afex_\d/) {
479 unlink "$xkeydir/$xkey/xkey";
480 unlink "$xkeydir/$xkey";
483 "HTTP/1.1 301 Moved Permanently",
484 "Location: $ENV{PROTO}://$ENV{HTTP_HOST}/fop/$dkey",
488 fexlog($connect,@log);
491 fexlog($connect,@log);
497 if (($ENV{QUERY_STRING} =~ /.*locale=([\w-]+)/ or
498 $ENV{HTTP_COOKIE} =~ /.*locale=([\w-]+)/)
499 and -d "$FEXHOME/locale/$1") {
500 $ENV{LOCALE} = $locale = $1;
502 $ENV{LOCALE} = $locale = $default_locale;
505 # for dynamic HTML documents
506 if ($ENV{HTTP_COOKIE} =~ /akey=(\w+)/) {
509 if ($user = readlink "$akeydir/$akey") {
511 $user = untaint($user);
512 if ($id = slurp("$spooldir/$user/@")) {
521 # check for name based virtual host
522 $vhost = vhost($ENV{'HTTP_HOST'});
526 foreach $var (sort keys %ENV) {
527 if (defined($ENV{$var})) {
528 debuglog(sprintf " %s = >%s<\n",$var,$ENV{$var});
534 # locale definitions in fex.ph?
537 $locale = $locales[0];
538 } elsif (not grep /^$locale$/,@locales) {
539 $locale = $default_locale;
543 # prepare document file name
544 if ($ENV{REQUEST_METHOD} =~ /^GET|HEAD$/) {
546 foreach my $r (keys %redirect) {
547 if ($uri =~ /^\Q$r/) {
553 $doc = untaint($uri);
554 $doc =~ s/%([\dA-F]{2})/unpack("a",pack("H2",$1))/ge;
555 $doc =~ m:/\.\./: and http_error(403);
558 if ($locale and $locale ne 'english' and -e "$docdir/locale/$locale/$doc") {
559 $doc = "$docdir/locale/$locale/$doc";
561 $doc = "$docdir/$doc";
565 # CGI or document request?
566 if ($cgi =~ s:^/+::) {
569 my $login = "$FEXHOME/cgi-bin/login";
571 $cgi = untaint(readlink($login) || $login);
576 $ENV{SCRIPT_NAME} = $cgi;
578 # locale CGIs? (vhost comes already with own FEXLIB)
579 if ($locale and $locale ne 'english'
580 and -f "$FEXHOME/locale/$locale/cgi-bin/$cgi") {
581 $ENV{SCRIPT_FILENAME} = $cgi = "$FEXHOME/locale/$locale/cgi-bin/$cgi";
582 $ENV{FEXLIB} = $FEXLIB = "$FEXHOME/locale/$locale/lib" unless $vhost;
584 $ENV{SCRIPT_FILENAME} = $cgi = "$FEXHOME/cgi-bin/$cgi";
585 $ENV{FEXLIB} = $FEXLIB = "$FEXHOME/lib" unless $vhost;
589 if (-x $cgi and -f $cgi) {
590 if (@forbidden_hosts and ipin($ra,@forbidden_hosts)) {
591 fexlog($connect,@log,"FORBIDDEN");
594 unlink "$spooldir/.error/$ra";
595 # push @log,"DEBUG: locale=$locale locales=(@locales)";
596 fexlog($connect,@log,"EXEC $cgi");
597 eval { local $^W = 0; exec $cgi };
598 $status = "$! or bad interpreter";
599 fexlog($connect,@log,"FAILED to exec $cgi : $status");
602 if (-f "$doc/.htindex") {
603 require "$FEXLIB/dop";
604 fexlog($connect,@log);
608 next REQUEST if $keep_alive;
611 if (-f "$doc/index.html") {
612 # force redirect if trailing / is missing
613 # this is mandatory for processing further HTTP request!
616 "HTTP/1.1 301 Moved Permanently",
617 "Location: $ENV{REQUEST_URL}/",
621 fexlog($connect,@log);
622 next REQUEST if $keep_alive;
625 $doc .= '/index.html';
628 $doc =~ s/#.*//; # ignore HTML anchors (stupid msnbot)
630 # special request for F*EX UNIX clients
631 if ($ENV{SCRIPT_NAME} eq 'xx.tar') {
632 bintar(qw'fexget fexsend xx zz ezz');
634 if ($ENV{SCRIPT_NAME} eq 'sex.tar') {
635 bintar(qw'sexsend sexget sexxx');
637 if ($ENV{SCRIPT_NAME} eq 'afex.tar') {
638 bintar(qw'afex asex fexget fexsend sexsend sexget');
640 if ($ENV{SCRIPT_NAME} eq 'afs.tar') {
641 bintar(qw'afex asex fexget fexsend xx sexsend sexget sexxx zz ezz');
643 # URL ends with ".html!" or ".html?!"
644 if ($doc =~ s/(\.html)!$/$1/ or
645 $doc =~ /\.html$/ and $ENV{'QUERY_STRING'} eq '!')
646 { $htmlsource = $doc } else { $htmlsource = '' }
649 or $doc =~ /(.+)\.(tar|tgz|zip)$/ and lstat("$1.stream")
650 or $doc =~ /(.+)\.tgz$/ and -f "$1.tar"
651 or $doc =~ /(.+)\.gz$/ and -f $1)
653 unlink "$spooldir/.error/$ra";
654 delete $ENV{SCRIPT_FILENAME};
655 $ENV{DOCUMENT_FILENAME} = $doc;
656 require "$FEXLIB/dop";
657 fexlog($connect,@log);
660 next REQUEST if $keep_alive;
662 } elsif ($uri eq '/bunny') {
663 fexlog($connect,@log);
667 "Content-Type: text/plain",
672 } elsif ($uri eq '/camel') {
673 fexlog($connect,@log);
677 "Content-Type: text/plain",
681 print unpack('u',<DATA>);
684 $status = 'not executable';
691 # neither document nor CGI ==> error
694 fexlog($connect,@log,"FAILED to exec $cgi : $status");
697 fexlog($connect,@log,"UNKNOWN URL");
705 # read one text line unbuffered from STDIN
713 # must use sysread to avoid perl line buffering
714 # (later exec would destroy line buffer)
715 while (sysread STDIN,$c,1) {
720 fexlog($connect,@log,$line,"OVERRUN");
734 foreach my $logdir (@logdir) {
735 if (open $log,'>>',"$logdir/$log") {
737 seek $log,0,SEEK_END;
738 print {$log} "\n",join("\n",@log),"\n";
741 http_die("$0: cannot write to $logdir/$log - $!\n");
750 fexlog($connect,@log,"DISCONNECT: bad characters in URL");
751 debuglog("DISCONNECT: bad characters in URL $uri");
753 http_die("\"$bc\" is not allowed in URL");
758 my $tmpdir = "$FEXHOME/tmp";
759 my $fs = "$ENV{PROTO}://$ENV{HTTP_HOST}";
761 if (chdir "$FEXHOME/bin") {
762 fexlog($connect,@log);
763 chdir $fstb if $fstb;
766 copy($f,"$tmpdir/$f","s#fexserver = ''#fexserver = '$fs'#");
767 chmod 0755,"$tmpdir/$f";
769 chdir $tmpdir or http_die("internal error: $tmpdir - $!");
770 my $tar = `tar cf - @_ 2>/dev/null`;
775 "Content-Length: ".length($tar),
776 "Content-Type: application/x-tar",
787 my $URL = $ENV{REQUEST_URL}||'';
788 my $URI = $ENV{REQUEST_URI}||'';
791 http_error_header("400 Bad Request");
792 nvt_print("Your request $URL is not acceptable.");
793 } elsif ($error eq 403) {
794 http_error_header("403 Forbidden");
795 nvt_print("You have no permission to request $URL");
796 } elsif ($error eq 404) {
797 http_error_header("404 Not Found");
798 nvt_print("The requested URI $URI was not found on this server.");
799 } elsif ($error eq 413) {
800 http_error_header("413 Payload Too Large");
801 nvt_print("Your HTTP header is too large.");
802 } elsif ($error eq 416) {
803 http_error_header("416 Requested Range Not Satisfiable");
804 } elsif ($error eq 503) {
805 http_error_header("503 Service Unavailable");
806 # nvt_print("No Perl ipv6 support on this server.");
808 http_error_header("555 Unknown Error");
809 nvt_print("The requested URL $URL produced an internal error.");
813 "<address>fexsrv at <a href=\"/index.html\">$hostname:$port</a></address>",
825 # &$header_hook($connect,$_,$ra) while ($header_hook and $_ = shift @header);
826 fexlog($connect,@log,"DISCONNECT: $info");
827 debuglog("DISCONNECT: $info");
828 errorlog("$ENV{REQUEST_URI} ==> $error");
829 badlog("$ENV{REQUEST_URI} ==> $error ($info)");
832 nvt_print("HTTP/1.0 $error");
837 sub http_error_header {
839 my $uri = $ENV{REQUEST_URI};
841 errorlog("$uri ==> $error") if $uri;
845 "Content-Type: text/html; charset=iso-8859-1",
847 '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">',
849 "<head><title>$error</title></head>",
859 my $rr = $redirect{$r};
867 "HTTP/1.1 301 Moved Permanently",
873 if ($rr =~ /^http/) {
876 $newurl = "$ENV{PROTO}://$ENV{HTTP_HOST}$rr$uri";
879 http_header("200 OK");
880 print html_header("$hostname page has moved");
882 '<h3>Please use new URL: <a href="$newurl">$newurl</a></h3>'
886 fexlog($connect,@log,"REDIRECT $newurl");
887 if ($rr =~ /^http/) {
898 my $ed = "$spooldir/.error";
902 foreach (@ignore_error) {
903 return if $request =~ /$_/;
907 if ($ra and $max_error and $max_error_handler) {
908 mkdir($ed) unless -d $ed;
910 if (open $ra,"+>>$ed/$ra") {
914 printf {$ra} "%s %s\n",isodate(time),$request;
916 &$max_error_handler($ra,@n) if scalar(@n) > $max_error;
923 M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("PM)R(G+5P*("`@
924 M("`@("`@("`@("`@("`@("`@("`@("]@8&`M+B`@(&!<("`@("Q=+B`@("`@
925 M("`@("`@("`@("`@("`@("`L+BY?"B`@("`@("`@("`@("`@("`@("`@("`@
926 M+&`@("`@(&`B+B`@72X@("`@(&`N("`@("`@("`@("`@("`@7U]?+BY>(&!?
927 M)V`B(B(*("`@("`@("`@("`@("`@("`@("`@("`I("`@("`@("`G+"TG+2T@
928 M+B`@("!@+B`@("`@(%]?7RPN+2<@("XN("T@("`G("TP("Y?"B`@("`@("`@
929 M("`@+"X@("`@("`@("`@?%]?+"Y?("!?("`N+5\@("!<("`\("Y@(B<G)V`L
930 M7R`@("`@("`@+2!?("TN("`@("`@("<M+BX*("`@("`@("`@('P@(&8M+2TM
931 M+2TM+2<O("`@("!@)RTM+BXL("`@("`M("`@("`@("`@("T@("`@("`@("`@
932 M("`@("`G("XL7U\N7%\N)PH@("`@("`@("`@8"TM)R<G)R<G8"TM7"`@("`@
933 M("`@("`@("!@)RTM+B`@("`@("`@("`@("`@("`@("`@7RPN)RTM)R<*("`@
934 M("`@("`@("`@("`@("`@("`@("\@("`@("`@("`@("`@("`@("!@8#T@+2`@
935 M+2`@("!?+2`@7RXM)PH@("`@("`@("`@("`@("`@("`@("!\("`@("`@("`@
936 M("`@("`@("`@("`@("!?)V`@("`G)U\M+2<*("`@("`@("`@("`@("`@("`@
937 M("`@?"`@("`@("`@("`@("`@("`@("`O("`@?"T]+BXM+2<*("`@("`@("`@
938 M("`@("`@(%\@("`@("<N("`@("`@("`N7R`@(%\N+2=@)R`@(&!?7PH@("`@
939 M("`@("`@("`@("PG("`N("PG("TL3%]?+WP@(%P@8"<G+&\N("`@+5\@("`@
940 M)V`M+2X*("`@("`@("`@("`@("`@+BPG(BT@("`O("`@("`@("`@?"`@8'8@
941 M+R`N("PG8"TM+BXG7R`G+@H@("`@("`@("`@("`@("`@("TN+E\L)R`@("`@
942 M('P@("!\("`@)RTM+E]@("!?+R`@('P@("!\"B`@("`@("`@("`@("`@("`@
943 M("`@("`@("`@("=@8"<@6R`@("`@("`@("=@)R`@("`L+RXN+PH@("`@("`@
944 M("`@("`@("`@("`@("`@("`@('P@("`M+B<@("`@("`@("`@("`@("Y;("`@
945 M+PH@("`@("`@("`@("`@("`@("`@(%]?7U]?7RX@("`L.E]?7U]?7U]?7U]?
946 M7U]?7RQ@("PG"B`@("TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2T@+2(M+2TM
947 M+2TM+2TM+2TM+2TM+2T]+0H@+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
948 6+2TM+2TM+2TM+2TM+2TM+2TM+2TM"@``