#!/usr/bin/perl # $Id: service.in,v 1.118 2005/10/19 01:22:14 don Exp $ # # Usage: service .nn # Temps: incoming/P.nn use warnings; use strict; use locale; use POSIX qw(strftime locale_h); setlocale(LC_TIME, "C"); use Debbugs::Config qw(:globals :config); use File::Copy; use MIME::Parser; use Params::Validate qw(:types validate_with); use Debbugs::Common qw(:util :quit :misc :lock); use Debbugs::Status qw(:read :status :write :versions :hook); use Debbugs::Packages qw(binary_to_source); use Debbugs::MIME qw(decode_rfc1522 encode_rfc1522 create_mime_message); use Debbugs::Mail qw(send_mail_message); use Debbugs::User; use Debbugs::Recipients qw(:all); use HTML::Entities qw(encode_entities); use Debbugs::Versions::Dpkg; use Debbugs::Status qw(splitpackages); use Debbugs::CGI qw(html_escape); use Debbugs::Control qw(:all); use Debbugs::Control::Service qw(:all); use Debbugs::Log qw(:misc); use Debbugs::Text qw(:templates); use Scalar::Util qw(looks_like_number); use List::Util qw(first); use Mail::RFC822::Address; use Encode qw(decode encode); chdir($config{spool_dir}) or die "Unable to chdir to spool_dir '$config{spool_dir}': $!"; my $debug = 0; umask(002); my ($nn,$control) = $ARGV[0] =~ m/^(([RC])\.\d+)$/; if (not defined $control or not defined $nn) { die "Bad argument to service.in"; } if (!rename("incoming/G$nn","incoming/P$nn")) { defined $! and $! =~ m/no such file or directory/i and exit 0; die "Failed to rename incoming/G$nn to incoming/P$nn: $!"; } my $log_fh = IO::File->new("incoming/P$nn",'r') or die "Unable to open incoming/P$nn for reading: $!"; my @log=<$log_fh>; my @msg=@log; close($log_fh); chomp @msg; print "###\n",join("##\n",@msg),"\n###\n" if $debug; # Bug numbers to send e-mail to, hash so that we don't send to the # same bug twice. my (%bug_affected); my (@headerlines,@bodylines); my $parse_output = Debbugs::MIME::parse(join('',@log)); @headerlines = @{$parse_output->{header}}; @bodylines = @{$parse_output->{body}}; my %header; for (@headerlines) { $_ = decode_rfc1522($_); s/\n\s/ /g; print ">$_<\n" if $debug; if (s/^(\S+):\s*//) { my $v = lc $1; print ">$v=$_<\n" if $debug; $header{$v} = $_; } else { print "!>$_<\n" if $debug; } } $header{'message-id'} ||= ''; $header{subject} ||= ''; grep(s/\s+$//,@bodylines); print "***\n",join("\n",@bodylines),"\n***\n" if $debug; if (defined $header{'resent-from'} && !defined $header{'from'}) { $header{'from'} = $header{'resent-from'}; } defined($header{'from'}) || die "no From header"; delete $header{'reply-to'} if ( defined($header{'reply-to'}) && $header{'reply-to'} =~ m/^\s*$/ ); my $replyto; if ( defined($header{'reply-to'}) && $header{'reply-to'} ne "" ) { $replyto = $header{'reply-to'}; } else { $replyto = $header{'from'}; } # This is an error counter which should be incremented every time there is an error. my $errors = 0; my $controlrequestaddr= ($control ? 'control' : 'request').'@'.$config{email_domain}; my $transcript_scalar = ''; open my $transcript, ">:scalar:utf8", \$transcript_scalar or die "Unable to create transcript scalar: $!"; print {$transcript} "Processing commands for $controlrequestaddr:\n\n"; my $dl = 0; my %affected_packages; my %recipients; # this is the hashref which is passed to all control calls my %limit = (); my @common_control_options = (transcript => $transcript, requester => $header{from}, request_addr => $controlrequestaddr, request_msgid => $header{'message-id'}, request_subject => $header{subject}, request_nn => $nn, request_replyto => $replyto, message => \@log, affected_bugs => \%bug_affected, affected_packages => \%affected_packages, recipients => \%recipients, limit => \%limit, ); my $state= 'idle'; my $lowstate= 'idle'; my $mergelowstate= 'idle'; my $midix=0; my $user = $replyto; $user =~ s/,.*//; $user =~ s/^.*<(.*)>.*$/$1/; $user =~ s/[(].*[)]//; $user =~ s/^\s*(\S+)\s+.*$/$1/; $user = "" unless (Debbugs::User::is_valid_user($user)); my $indicated_user = 0; my $quickabort = 0; if (@gExcludeFromControl and grep {$replyto =~ m/\Q$_\E/} @gExcludeFromControl) { print {$transcript} fill_template('mail/excluded_from_control'); $quickabort = 1; } my %limit_pkgs = (); my %clonebugs = (); my %bcc = (); our $data; our $message; our $extramessage; our $ref; our $mismatch; our $action; my $ok = 0; my $unknowns = 0; my $procline=0; for ($procline=0; $procline<=$#bodylines; $procline++) { my $noriginator; my $newsubmitter; my $oldsubmitter; my $newowner; $state eq 'idle' || print "state: $state ?\n"; $lowstate eq 'idle' || print "lowstate: $lowstate ?\n"; $mergelowstate eq 'idle' || print "mergelowstate: $mergelowstate ?\n"; if ($quickabort) { print {$transcript} "Stopping processing here.\n\n"; last; } $_= $bodylines[$procline]; s/\s+$//; # Remove BOM markers from UTF-8 strings # Fixes #488554 s/\xef\xbb\xbf//g; next unless m/\S/; eval { my $temp = decode("utf8",$_,Encode::FB_CROAK); $_ = $temp; }; print {$transcript} "> $_\n"; next if m/^\s*\#/; $action= ''; if (m/^(?:stop|quit|--|thank(?:s|\s*you)?|kthxbye)\.*\s*$/i) { print {$transcript} "Stopping processing here.\n\n"; last; } elsif (m/^debug\s+(\d+)$/i && $1 >= 0 && $1 <= 1000) { $dl= $1+0; if ($dl > 0 and not grep /debug/,@common_control_options) { push @common_control_options,(debug => $transcript); } print {$transcript} "Debug level $dl.\n\n"; } elsif (m/^(send|get)\s+\#?(\d{2,})$/i) { $ref= $2+0; &sendlynxdoc("bugreport.cgi?bug=$ref","logs for $gBug#$ref"); } elsif (m/^send-detail\s+\#?(\d{2,})$/i) { $ref= $1+0; &sendlynxdoc("bugreport.cgi?bug=$ref&boring=yes", "detailed logs for $gBug#$ref"); } elsif (m/^index(\s+full)?$/i) { print {$transcript} "This BTS function is currently disabled, sorry.\n\n"; $errors++; $ok++; # well, it's not really ok, but it fixes #81224 :) } elsif (m/^index-summary\s+by-package$/i) { print {$transcript} "This BTS function is currently disabled, sorry.\n\n"; $errors++; $ok++; # well, it's not really ok, but it fixes #81224 :) } elsif (m/^index-summary(\s+by-number)?$/i) { print {$transcript} "This BTS function is currently disabled, sorry.\n\n"; $errors++; $ok++; # well, it's not really ok, but it fixes #81224 :) } elsif (m/^index(\s+|-)pack(age)?s?$/i) { &sendlynxdoc("pkgindex.cgi?indexon=pkg",'index of packages'); } elsif (m/^index(\s+|-)maints?$/i) { &sendlynxdoc("pkgindex.cgi?indexon=maint",'index of maintainers'); } elsif (m/^index(\s+|-)maint\s+(\S+)$/i) { my $maint = $2; &sendlynxdoc("pkgreport.cgi?maint=" . urlsanit($maint), "$gBug list for maintainer \`$maint'"); $ok++; } elsif (m/^index(\s+|-)pack(age)?s?\s+(\S.*\S)$/i) { my $package = $+; &sendlynxdoc("pkgreport.cgi?pkg=" . urlsanit($package), "$gBug list for package $package"); $ok++; } elsif (m/^send-unmatched(\s+this|\s+-?0)?$/i) { print {$transcript} "This BTS function is currently disabled, sorry.\n\n"; $errors++; $ok++; # well, it's not really ok, but it fixes #81224 :) } elsif (m/^send-unmatched\s+(last|-1)$/i) { print {$transcript} "This BTS function is currently disabled, sorry.\n\n"; $errors++; $ok++; # well, it's not really ok, but it fixes #81224 :) } elsif (m/^send-unmatched\s+(old|-2)$/i) { print {$transcript} "This BTS function is currently disabled, sorry.\n\n"; $errors++; $ok++; # well, it's not really ok, but it fixes #81224 :) } elsif (m/^getinfo\s+([\w.-]+)$/i) { # the following is basically a Debian-specific kludge, but who cares my $req = $1; if ($req =~ /^maintainers$/i && -f "$gConfigDir/Maintainers") { &sendinfo("local", "$gConfigDir/Maintainers", "Maintainers file"); } elsif ($req =~ /^override\.(\w+)\.([\w.-]+)$/i) { $req =~ s/.gz$//; &sendinfo("ftp.d.o", "$req", "override file for $2 part of $1 distribution"); } elsif ($req =~ /^pseudo-packages\.(description|maintainers)$/i && -f "$gConfigDir/$req") { &sendinfo("local", "$gConfigDir/$req", "$req file"); } else { print {$transcript} "Info file $req does not exist.\n\n"; } } elsif (m/^help/i) { &sendhelp; print {$transcript} "\n"; $ok++; } elsif (m/^refcard/i) { &sendtxthelp("bug-mailserver-refcard.txt","mail servers' reference card"); } elsif (m/^subscribe/i) { print {$transcript} < $bodylines[$procline]\n"; next if $bad; my ($o, $txt) = ($1, $2); if ($#cats == -1 && $o eq "+") { print {$transcript} "User defined category specification must start with a category name. Skipping.\n\n"; $errors++; $bad = 1; next; } if ($o eq "+") { unless (ref($cats[-1]) eq "HASH") { $cats[-1] = { "nam" => $cats[-1], "pri" => [], "ttl" => [] }; } $catsec++; my ($desc, $ord, $op); if ($txt =~ m/^(.*\S)\s*\[((\d+):\s*)?\]\s*$/) { $desc = $1; $ord = $3; $op = ""; } elsif ($txt =~ m/^(.*\S)\s*\[((\d+):\s*)?(\S+)\]\s*$/) { $desc = $1; $ord = $3; $op = $4; } elsif ($txt =~ m/^([^[\s]+)\s*$/) { $desc = ""; $op = $1; } else { print {$transcript} "Unrecognised syntax for category section. Skipping.\n\n"; $errors++; $bad = 1; next; } $ord = 999 unless defined $ord; if ($op) { push @{$cats[-1]->{"pri"}}, $prefix . $op; push @{$cats[-1]->{"ttl"}}, $desc; push @ords, "$ord $catsec"; } else { $cats[-1]->{"def"} = $desc; push @ords, "$ord DEF"; $catsec--; } @ords = sort { my ($a1, $a2, $b1, $b2) = split / /, "$a $b"; ((looks_like_number($a1) and looks_like_number($a2))?$a1 <=> $b1:$a1 cmp $b1) || ((looks_like_number($a2) and looks_like_number($b2))?$a2 <=> $b2:$a2 cmp $b2); } @ords; $cats[-1]->{"ord"} = [map { m/^.* (\S+)/; $1 eq "DEF" ? $catsec + 1 : $1 } @ords]; } elsif ($o eq "*") { $catsec = 0; my ($name); if ($txt =~ m/^(.*\S)(\s*\[(\S+)\])\s*$/) { $name = $1; $prefix = $3; } else { $name = $txt; $prefix = ""; } push @cats, $name; } } # XXX: got @cats, now do something with it my $u = Debbugs::User::get_user($user); if (@cats) { print {$transcript} "Added usercategory $catname.\n\n"; $u->{"categories"}->{$catname} = [ @cats ]; if (not $hidden) { push @{$u->{visible_cats}},$catname; } } else { print {$transcript} "Removed usercategory $catname.\n\n"; delete $u->{"categories"}->{$catname}; @{$u->{visible_cats}} = grep {$_ ne $catname} @{$u->{visible_cats}}; } $u->write(); } elsif (m/^usertags?\s+\#?(-?\d+)\s+(([=+-])\s*)?(\S.*)?$/i) { $ok++; $ref = $1; my $addsubcode = $3 || "+"; my $tags = $4; if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) { $ref = $clonebugs{$ref}; } if ($user eq "") { print {$transcript} "No valid user selected\n"; $errors++; $indicated_user = 1; } elsif (check_limit(data => read_bug(bug => $ref), limit => \%limit, transcript => $transcript)) { if (not $indicated_user and defined $user) { print {$transcript} "User is $user\n"; $indicated_user = 1; } my %ut; Debbugs::User::read_usertags(\%ut, $user); my @oldtags = (); my @newtags = (); my @badtags = (); my %chtags; if (defined $tags and length $tags) { for my $t (split /[,\s]+/, $tags) { if ($t =~ m/^[a-zA-Z0-9.+\@-]+$/) { $chtags{$t} = 1; } else { push @badtags, $t; } } } if (@badtags) { print {$transcript} "Ignoring illegal tag/s: ".join(', ', @badtags).".\nPlease use only alphanumerics, at, dot, plus and dash.\n"; $errors++; } for my $t (keys %chtags) { $ut{$t} = [] unless defined $ut{$t}; } for my $t (keys %ut) { my %res = map { ($_, 1) } @{$ut{$t}}; push @oldtags, $t if defined $res{$ref}; my $addop = ($addsubcode eq "+" or $addsubcode eq "="); my $del = (defined $chtags{$t} ? $addsubcode eq "-" : $addsubcode eq "="); $res{$ref} = 1 if ($addop && defined $chtags{$t}); delete $res{$ref} if ($del); push @newtags, $t if defined $res{$ref}; $ut{$t} = [ sort { $a <=> $b } (keys %res) ]; } if (@oldtags == 0) { print {$transcript} "There were no usertags set.\n"; } else { print {$transcript} "Usertags were: " . join(" ", @oldtags) . ".\n"; } print {$transcript} "Usertags are now: " . join(" ", @newtags) . ".\n"; Debbugs::User::write_usertags(\%ut, $user); } } elsif (!$control) { print {$transcript} < $_, clonebugs => \%clonebugs, limit => \%limit, common_control_options => \@common_control_options, errors => \$errors, transcript => $transcript, debug => $debug, ok => \$ok, replyto => $replyto, ); if ($terminate_control) { last; } } else { print {$transcript} "Unknown command or malformed arguments to command.\n"; $errors++; if (++$unknowns >= 5) { print {$transcript} "Too many unknown commands, stopping here.\n\n"; last; } } } if ($procline>$#bodylines) { print {$transcript} ">\nEnd of message, stopping processing here.\n\n"; } if (!$ok && !$quickabort) { $errors++; print {$transcript} "No commands successfully parsed; sending the help text(s).\n"; &sendhelp; print {$transcript} "\n"; } my @maintccs = determine_recipients(recipients => \%recipients, address_only => 1, cc => 1, ); if (!defined $header{'subject'} || $header{'subject'} eq "") { $header{'subject'} = "your mail"; } # Error text here advertises how many errors there were my $error_text = $errors > 0 ? " (with $errors error" . ($errors > 1 ? "s" : "") . ")" : ""; my @common_headers; push @common_headers, 'X-Loop',$gMaintainerEmail; my $temp_transcript = $transcript_scalar; eval{ $temp_transcript = decode("utf8",$temp_transcript,Encode::FB_CROAK); }; my $reply = create_mime_message([From => "$gMaintainerEmail ($gProject $gBug Tracking System)", To => $replyto, @maintccs ? (Cc => join(', ',@maintccs)):(), Subject => "Processed${error_text}: $header{subject}", 'Message-ID' => "", 'In-Reply-To' => $header{'message-id'}, References => join(' ',grep {defined $_} $header{'message-id'},$data->{msgid}), Precedence => 'bulk', keys %affected_packages ?("X-${gProject}-PR-Package" => join(' ',keys %affected_packages)):(), keys %affected_packages ?("X-${gProject}-PR-Source" => join(' ', map {defined $_ ?(ref($_)?@{$_}:$_):()} binary_to_source(binary => [keys %affected_packages], source_only => 1))):(), "X-$gProject-PR-Message" => 'transcript', @common_headers, ], fill_template('mail/message_body', {body => "${temp_transcript}Please contact me if you need assistance."}, )); my $repliedshow= join(', ',$replyto, determine_recipients(recipients => \%recipients, cc => 1, address_only => 1, ) ); utime(time,time,"db-h"); &sendmailmessage($reply, exists $header{'x-debbugs-no-ack'}?():$replyto, make_list(values %{{determine_recipients(recipients => \%recipients, address_only => 1, )}} ), ); unlink("incoming/P$nn") || die "unlinking incoming/P$nn: $!"; sub sendmailmessage { my ($message,@recips) = @_; $message = "X-Loop: $gMaintainerEmail\n" . $message; send_mail_message(message => $message, recipients => \@recips, ); $midix++; } sub fill_template{ my ($template,$extra_var) = @_; $extra_var ||={}; my $variables = {config => \%config, defined($ref)?(ref => $ref):(), defined($data)?(data => $data):(), refs => [map {exists $clonebugs{$_}?$clonebugs{$_}:$_} keys %bug_affected], %{$extra_var}, }; my $hole_var = {'&bugurl' => sub{"$_[0]: ". $config{cgi_domain}.'/'. Debbugs::CGI::bug_links(bug=>$_[0], links_only => 1, ); } }; return fill_in_template(template => $template, variables => $variables, hole_var => $hole_var, ); } =head2 message_body_template message_body_template('mail/ack',{ref=>'foo'}); Creates a message body using a template =cut sub message_body_template{ my ($template,$extra_var) = @_; $extra_var ||={}; my $body = fill_template($template,$extra_var); return fill_template('mail/message_body', {%{$extra_var}, body => $body, }, ); } sub sendhelp { if ($control) { &sendtxthelpraw("bug-maint-mailcontrol.txt","instructions for control\@$gEmailDomain") } else { &sendtxthelpraw("bug-log-mailserver.txt","instructions for request\@$gEmailDomain"); } } #sub unimplemented { # print {$transcript} "Sorry, command $_[0] not yet implemented.\n\n"; #} our %checkmatch_values; sub checkmatch { my ($string,$mvarname,$svarvalue,@newmergelist) = @_; my ($mvarvalue); if (@newmergelist) { $mvarvalue = $checkmatch_values{$mvarname}; print {$transcript} "D| checkmatch \`$string' /$mvarname/$mvarvalue/$svarvalue/\n" if $dl; $mismatch .= "Values for \`$string' don't match:\n". " #$newmergelist[0] has \`$mvarvalue';\n". " #$ref has \`$svarvalue'\n" if $mvarvalue ne $svarvalue; } else { print {$transcript} "D| setupmatch \`$string' /$mvarname/$svarvalue/\n" if $dl; $checkmatch_values{$mvarname} = $svarvalue; } } sub checkpkglimit { if (keys %limit_pkgs and not defined $limit_pkgs{$data->{package}}) { print {$transcript} "$gBug number $ref belongs to package $data->{package}, skipping.\n\n"; $errors++; return 0; } return 1; } sub manipset { my $list = shift; my $elt = shift; my $add = shift; my %h = map { $_ => 1 } split ' ', $list; if ($add) { $h{$elt}=1; } else { delete $h{$elt}; } return join ' ', sort keys %h; } # High-level bug manipulation calls # Do announcements themselves # # Possible calling sequences: # setbug (returns 0) # # setbug (returns 1) # &transcript(something) # nochangebug # # setbug (returns 1) # $action= (something) # do { # (modify s_* variables) # } while (getnextbug); our $manybugs; sub nochangebug { &dlen("nochangebug"); $state eq 'single' || $state eq 'multiple' || die "$state ?"; &cancelbug; &endmerge if $manybugs; $state= 'idle'; &dlex("nochangebug"); } our $sref; our @thisbugmergelist; sub setbug { &dlen("setbug $ref"); if ($ref =~ m/^-\d+/) { if (!defined $clonebugs{$ref}) { ¬foundbug; &dlex("setbug => noclone"); return 0; } $ref = $clonebugs{$ref}; } $state eq 'idle' || die "$state ?"; if (!&getbug) { ¬foundbug; &dlex("setbug => 0s"); return 0; } if (!&checkpkglimit) { &cancelbug; return 0; } @thisbugmergelist= split(/ /,$data->{mergedwith}); if (!@thisbugmergelist) { &foundbug; $manybugs= 0; $state= 'single'; $sref=$ref; &dlex("setbug => 1s"); return 1; } &cancelbug; &getmerge; $manybugs= 1; if (!&getbug) { ¬foundbug; &endmerge; &dlex("setbug => 0mc"); return 0; } &foundbug; $state= 'multiple'; $sref=$ref; &dlex("setbug => 1m"); return 1; } sub getnextbug { &dlen("getnextbug"); $state eq 'single' || $state eq 'multiple' || die "$state ?"; &savebug; if (!$manybugs || !@thisbugmergelist) { length($action) || die; print {$transcript} "$action\n$extramessage\n"; &endmerge if $manybugs; $state= 'idle'; &dlex("getnextbug => 0"); return 0; } $ref= shift(@thisbugmergelist); &getbug || die "bug $ref disappeared"; &foundbug; &dlex("getnextbug => 1"); return 1; } # Low-level bug-manipulation calls # Do no announcements # # getbug (returns 0) # # getbug (returns 1) # cancelbug # # getmerge # $action= (something) # getbug (returns 1) # savebug/cancelbug # getbug (returns 1) # savebug/cancelbug # [getbug (returns 0)] # &transcript("$action\n\n") # endmerge sub notfoundbug { print {$transcript} "$gBug number $ref not found. (Is it archived?)\n\n"; } sub foundbug { print {$transcript} "$gBug#$ref: $data->{subject}\n"; } sub getmerge { &dlen("getmerge"); $mergelowstate eq 'idle' || die "$mergelowstate ?"; &filelock('lock/merge'); $mergelowstate='locked'; &dlex("getmerge"); } sub endmerge { &dlen("endmerge"); $mergelowstate eq 'locked' || die "$mergelowstate ?"; &unfilelock; $mergelowstate='idle'; &dlex("endmerge"); } sub getbug { &dlen("getbug $ref"); $lowstate eq 'idle' || die "$state ?"; # Only use unmerged bugs here if (($data = &lockreadbug($ref,'db-h'))) { $sref= $ref; $lowstate= "open"; &dlex("getbug => 1"); $extramessage=''; return 1; } $lowstate= 'idle'; &dlex("getbug => 0"); return 0; } sub cancelbug { &dlen("cancelbug"); $lowstate eq 'open' || die "$state ?"; &unfilelock; $lowstate= 'idle'; &dlex("cancelbug"); } sub savebug { &dlen("savebug $ref"); $lowstate eq 'open' || die "$lowstate ?"; length($action) || die; $ref == $sref || die "read $sref but saving $ref ?"; append_action_to_log(bug => $ref, action => $action, requester => $header{from}, request_addr => $controlrequestaddr, message => \@log, get_lock => 0, ); unlockwritebug($ref, $data); $lowstate= "idle"; &dlex("savebug"); } sub dlen { return if !$dl; print {$transcript} "C> @_ ($state $lowstate $mergelowstate)\n"; } sub dlex { return if !$dl; print {$transcript} "R> @_ ($state $lowstate $mergelowstate)\n"; } sub urlsanit { my $url = shift; $url =~ s/%/%25/g; $url =~ s/\+/%2b/g; my %saniarray = ('<','lt', '>','gt', '&','amp', '"','quot'); $url =~ s/([<>&"])/\&$saniarray{$1};/g; return $url; } sub sendlynxdoc { &sendlynxdocraw; print {$transcript} "\n"; $ok++; } sub sendtxthelp { &sendtxthelpraw; print {$transcript} "\n"; $ok++; } our $doc; sub sendtxthelpraw { my ($relpath,$description) = @_; $doc=''; if (not -e "$gDocDir/$relpath") { print {$transcript} "Unfortunatly, the help text doesn't exist, so it wasn't sent.\n"; warn "Help text $gDocDir/$relpath not found"; return; } open(D,"$gDocDir/$relpath") || die "open doc file $relpath: $!"; while() { $doc.=$_; } close(D); print {$transcript} "Sending $description in separate message.\n"; &sendmailmessage(< Precedence: bulk X-$gProject-PR-Message: doc-text $relpath END $ok++; } sub sendlynxdocraw { my ($relpath,$description) = @_; $doc=''; open(L,"lynx -nolist -dump $gCGIDomain/\Q$relpath\E 2>&1 |") || die "fork for lynx: $!"; while() { $doc.=$_; } $!=0; close(L); if ($? == 255 && $doc =~ m/^\n*lynx: Can\'t access start file/) { print {$transcript} "Information ($description) is not available -\n". "perhaps the $gBug does not exist or is not on the WWW yet.\n"; $ok++; } elsif ($?) { print {$transcript} "Error getting $description (code $? $!):\n$doc\n"; } else { print {$transcript} "Sending $description.\n"; &sendmailmessage(< Precedence: bulk X-$gProject-PR-Message: doc-html $relpath END $ok++; } } sub sendinfo { my ($wherefrom,$path,$description) = @_; if ($wherefrom eq "ftp.d.o") { $doc = `lynx -nolist -dump http://ftp.debian.org/debian/indices/$path.gz 2>&1 | gunzip -cf` or die "fork for lynx/gunzip: $!"; $! = 0; if ($? == 255 && $doc =~ m/^\n*lynx: Can\'t access start file/) { print {$transcript} "$description is not available.\n"; $ok++; return; } elsif ($?) { print {$transcript} "Error getting $description (code $? $!):\n$doc\n"; return; } } elsif ($wherefrom eq "local") { open P, "$path"; $doc = do { local $/;

}; close P; } else { print {$transcript} "internal errror: info files location unknown.\n"; $ok++; return; } print {$transcript} "Sending $description.\n"; &sendmailmessage(< Precedence: bulk X-$gProject-PR-Message: getinfo $description follows: END $ok++; print {$transcript} "\n"; }