#!/opt/local/bin/perl -wT ############################################################################### # # generate new document ID number and send info to archive librarian # # Greg Fruth, Flight Mechanics Department # # v0.5, 22 May 1995 # v0.6, 30 Jan 1996 : rewritten # v0.7, 2 Mar 2000 : use flock for file locking, Y2K fixes # v1.1, 5 Apr 2001 : rewrite; "use CGI", new look and feel, # keyword field added, filter "^" in output # v1.2, 22 Jun 2001 : support multiple orgs; CCC field added; "use strict"; # UseSection field added # v1.3, 31 Aug 2001 : taint mode, adapt for web.aero.org # v1.4, 5 Apr 2002 : re-adapt for web.aero.org # ############################################################################### # fixme: trap signals to ensure sequence file integrity use CGI; use CGI::Carp qw(fatalsToBrowser); use Time::Local; use Fcntl ':flock'; use strict; # global variables use vars qw($Query $Debug $Now $MyCCC %UserDir); use vars qw($ConfFile $Style $LibrarianDesc $LibrarianAddr $UseSection); use vars qw($SeqDir); use vars qw($Sendmail $CountMax $SleepTime); use vars qw(@Errors); use vars qw($Day $Mon $Year $Sect $Subj $Auth $Dist $Clas $Keyw $Url); use vars qw($Host $DocId); use vars qw(%Label); require('./doc_util.pl'); $Query = new CGI; $Debug = 0; $Now = 0; $MyCCC = '????'; $ConfFile = 'config.txt'; $Style = ''; $LibrarianDesc = 'the Archive Librarian'; $LibrarianAddr = 'root'; $UseSection = 0; $SeqDir = 'sequence'; $Sendmail = '/usr/lib/sendmail'; $CountMax = 5; $SleepTime = 1; @Errors = (); $Day = ''; $Mon = ''; $Year = ''; $Sect = ''; $Subj = ''; $Auth = ''; $Dist = ''; $Clas = ''; $Keyw = ''; $Url = ''; $Host = ''; $DocId = ''; %Label = ( 'DocId' => 'Document ID', 'Date' => 'Publication date', 'Sect' => 'Section', 'Subj' => 'Subject', 'Auth' => 'Author', 'Dist' => 'Distribution', 'Clas' => 'Classification', 'Keyw' => 'Keywords', 'Url' => 'URL', ); ############################################################################### # main body ############################################################################### main(); exit(0); ############################################################################### # main body ############################################################################### sub main { my ($key, $value); my ($Errors, $error); $Errors = ''; $error = ''; # get inputs foreach $key ($Query->param) { $value = $Query->param($key); next unless (defined($value) && $value); # skip empty params $Day = $value, next if ($key eq 'Day'); $Mon = $value, next if ($key eq 'Mon'); $Year = $value, next if ($key eq 'Year'); $Sect = $value, next if ($key eq 'Sect'); $Subj = $value, next if ($key eq 'Subj'); $Auth = $value, next if ($key eq 'Auth'); $Dist = $value, next if ($key eq 'Dist'); $Clas = $value, next if ($key eq 'Clas'); $Keyw = $value, next if ($key eq 'Keyw'); $Url = $value, next if ($key eq 'Url'); $MyCCC = $value, next if ($key eq 'MyCCC'); $Debug = $value, next if ($key eq 'Debug'); } $Host = $Query->remote_host(); # untaint if ($MyCCC =~ /^(\d+)$/) {$MyCCC = $1} else {$MyCCC = ''} if ($Year =~ /^(\d+)$/) {$Year = $1} else {$Year = ''} if ($Sect =~ /^([\-\w]+)$/) {$Sect = $1} else {$Sect = ''} $ENV{PATH} = '/bin:/usr/bin'; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; # make sure we deny write permission to group, other umask(022); $Errors = ReadConf($ConfFile); Header('Register New Document', $Style); if ($Errors) { htmlError('cannot read config file', $Errors); } else { if (InputsAreValid()) { $DocId = GetDocId(); SendMail() if ($DocId); } else { $Errors = ''; htmlWarning('invalid inputs', "Some of the inputs were invalid or incomplete. " . "Use your browser's \"Back\" button to return to " . "the form and resubmit your request.
$Errors"); } } Tailer(); } ############################################################################### # validate form inputs # return 1 if they're OK, otherwise print an error message and return 0 ############################################################################### sub InputsAreValid { my $retval = 1; @Errors = (); if (! isValidDate(\$Year, \$Mon, \$Day)) { push(@Errors, "$Label{Date} (invalid date)"); $retval = 0; } if ($UseSection) { if ($Sect !~ /\w/) { push(@Errors, "$Label{Sect} (must not be empty)"); $retval = 0; } elsif ($Sect !~ /^[\-\w]+$/) { push(@Errors, "$Label{Sect} (valid characters: " . "0-9, a-z, A-Z, underscore (\"_\"), minus (\"-\"))"); $retval = 0; } } if ($Subj !~ /\w/) { push(@Errors, "$Label{Subj} (must not be empty)"); $retval = 0; } if ($Auth !~ /\w/) { push(@Errors, "$Label{Auth} (must not be empty)"); $retval = 0; } if ($Dist !~ /\w/) { push(@Errors, "$Label{Dist} (must not be empty)"); $retval = 0; } if ($Clas !~ /\w/) { push(@Errors, "$Label{Clas} (must not be empty)"); $retval = 0; } if ($Keyw !~ /\w/) { push(@Errors, "$Label{Keyw} (must not be empty)"); $retval = 0; } $Url = '(TBD)' unless ($Url =~ /\w/); if ($MyCCC !~ /\w/) { push(@Errors, "MyCCC (must not be empty)"); $retval = 0; } elsif ($MyCCC !~ /^\d+$/) { push(@Errors, "MyCCC (valid characters: 0-9)"); $retval = 0; } if (!$Host) { htmlError('Unknown host name!', 'Your host name could not be determined'); $retval = 0; } return($retval); } ############################################################################### # construct new document ID # if successful, return the ID, else print an error message and return # the null string ############################################################################### sub GetDocId { my ($seqFile, $seqNo, $count); my $retval = ''; if (!defined($UserDir{$MyCCC})) { htmlError("No UserDir defined for CCC $MyCCC"); return($retval); } if ($UseSection) { $seqFile = "$UserDir{$MyCCC}/$SeqDir/$Year" . "_" . "$Sect"; } else { $seqFile = "$UserDir{$MyCCC}/$SeqDir/$Year"; } # open appropriate sequence file, or create one if it doesn't exist if (!open(SEQFILE, '+>>' . $seqFile)) { htmlError("Cannot open file \"$seqFile\"!", "open() failed: $!"); return($retval); } # if file is locked by someone else, wait until it is unlocked $count = 0; while (! flock(SEQFILE, LOCK_EX | LOCK_NB)) { $count++; if ($count > $CountMax) { htmlError("Cannot lock file \"$seqFile\"!", "flock() failed: too many lock attempts"); return($retval); } # file is locked, sleep a while then try again sleep($SleepTime); } # get sequence number $seqNo = 1; while() { # if the file is empty, this leaves seqNo = 1 chomp; $seqNo = $_; } # detect corrupted sequence data if ($seqNo !~ /\d+/) { htmlError('Sequence file corrupted!' , "Sequence file \"$seqFile\" corrupted at line $.: " . "\"$seqNo\"!"); return($retval); } # construct document ID if ($UseSection) { $retval = sprintf("A%04d-%4d.%s-%03d", $Year, $MyCCC, $Sect, $seqNo); } else { $retval = sprintf("A%04d-%4d-%03d", $Year, $MyCCC, $seqNo); } # update sequence $seqNo++; truncate(SEQFILE, 0); print(SEQFILE "$seqNo\n"); close(SEQFILE); return($retval); } ############################################################################### # send E-mail to log librarian ############################################################################### sub SendMail { my $date = $Year . '/' . $Mon . '/' . $Day; my $value; if (open(MAIL, "| $Sendmail $LibrarianAddr")) { select(MAIL); # filter out carets (replace with the HTML escape sequence) foreach $value ($DocId, $Url, $date, $Auth, $Clas, $Dist, $Subj, $Keyw) { $value =~ s/\^/\&\#710;/g; } print <The document identification number is " . "$DocId"); beginPanel('header'); print('Note'); midPanel('data'); print("The document will be added to the log by $LibrarianDesc " . "after it has been verified. Remember to give an electronic " . "copy of your document to " . "$LibrarianDesc " . "so it can be placed on line."); endPanel(); print('
'); beginPanel('header'); print('Document Information'); midPanel('data'); print < $Label{'Date'}: $date $Label{'Subj'}: $Subj $Label{'Auth'}: $Auth $Label{'Dist'}: $Dist $Label{'Clas'}: $Clas $Label{'Keyw'}: $Keyw $Label{'Url'}: $Url EOF endPanel(); } else { htmlError("E-mail message could not be sent to $LibrarianAddr!", "sendmail failed: $!"); } }