home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1586 / unspool
Encoding:
Text File  |  1990-12-28  |  12.8 KB  |  471 lines

  1. #! /usr/local/bin/perl
  2. #
  3. # unspool does all of the actual processing for the ACS.
  4. #
  5. #die "unspool.perl compiled successfully\n";
  6. #
  7. # If the lock file exists, we are already running. Die immediately.
  8. if ( -e "/usr/personals/LCK..SPOOL" ) {
  9.     exit 0;
  10. }
  11. #
  12. # set up the environment for suid operations - no longer needed but...
  13. #
  14. $ENV{"PATH"} = "/bin:/usr/bin:/usr/local/bin:/usr/ucb:/usr/personals";
  15. $ENV{"IFS"} = '' if $ENV{"IFS"} ne '';
  16. $path = $ENV{"PATH"};
  17.  
  18. # set the permissions on all files we create to user only.
  19. umask(0077);
  20.  
  21. # flush on selected channel
  22. $| = 1;
  23. #
  24. # create the lock file
  25. #
  26. open(LCK,">/usr/personals/LCK..SPOOL");
  27. close(LCK);
  28.  
  29. # open the address:alias database
  30. if (! dbmopen(r2a,"/usr/personals/real2alias",0600)) {
  31.     print STDERR ":::can't dbmopen real2alias: $!\n";
  32.     exit(1);
  33. }
  34.  
  35. # open the alias-index file and get the current alias.
  36. if (open(INDEX,'</usr/personals/alias-index')) {
  37.     $alias_index = <INDEX>;
  38.     close(INDEX);
  39. }
  40. else {
  41.     $alias_index = 'a';
  42. }
  43. #
  44. # Process all the replies in the spool directory
  45. #
  46. $seq = $$;
  47. while (</usr/personals/spool/REP*> ) {
  48.     $repfile = $_;
  49.  
  50.     # check to make sure no fast connections are running. unspool
  51.     # kicks off quite a few other processes, which can seriously
  52.     # degrade throughput on a fast modem connected to a slow system.
  53.     &fstchk;
  54.  
  55.     # open the spooled reply file
  56.     open(MSG,"<$repfile");
  57.  
  58.     # read the first line of the message, which contains the recipient's
  59.     # alias.
  60.     $target_alias = <MSG>;
  61.  
  62.     # hack off the terminal newline
  63.     chop $target_alias;
  64.  
  65.     # Load the message on MSG into an array
  66.     @message = <MSG> ;
  67.     close(MSG);
  68.  
  69.     # open the file in the repair directory to store the header and
  70.     # status info in case something goes wrong.
  71.     $replog = 0;
  72.     $repname = "/usr/personals/repair/REP$seq-$target_alias";
  73.     if ( open(REPLOG,">$repname")) {
  74.         $replog = 1;
  75.         $seq++;
  76.     }
  77.  
  78.     # get the sender's address from the From: line
  79.     $address = &getaddr;
  80.  
  81.     # write the header and the extracted address to the repair file
  82.     print REPLOG @header if ( $replog );
  83.     print REPLOG "::: address = $address\n" if ( $replog );
  84.  
  85.     # if we didn't get a usable address, go on to the next message
  86.     goto repfail if ( "$address" eq '' );
  87.  
  88.     # extract the username from the address
  89.     $user = &getuser($address);
  90.  
  91.     # log it in the repair file
  92.     print REPLOG "::: user = $user\n" if ( $replog );
  93.  
  94.     # if there is none, or if it's not acceptable, go to next message
  95.     goto repfail if ( "$user" eq '' );
  96.  
  97.     # extract the subject from the Subject: line
  98.     $subject = &getsubj;
  99.  
  100.     # if the subject is empty, replace it with something clever
  101.     $subject = "(None)" if ( "$subject" eq '' );
  102.     # and log it
  103.     print REPLOG "::: subject = $subject\n" if ( $replog );
  104.  
  105.     # look up the sender's alias in the database. If s/he doesn't
  106.     # have one, give hir one, and log it
  107.     $sender_alias = &getsender($address);
  108.     print REPLOG "::: sender_alias = $sender_alias\n" if ( $replog );
  109.  
  110.     # Lookup target_alias in real2alias db
  111.     # WARNING: you cannot save time by jumping out of this loop
  112.     # after the target_alias has been found. Perl's implementation
  113.     # of the dbm stuff requires that "each" visit every entry in 
  114.     # the data base before it resets. This was the origin of the
  115.     # infamous reply to the wrong alias bug.
  116.     $recip_address = '';
  117.     $found = 0;
  118.     while (($key,$value) = each %r2a) {
  119.         if ( $found == 0 ) {
  120.             if ( "$value" eq "$target_alias" ) {
  121.                 $recip_address = $key;
  122.                 $found = 1;
  123.             }
  124.         }
  125.     }
  126.  
  127.     # non-existent target alias - send a terse message to the sender
  128.     # explaining this.
  129.     if ( "$recip_address" eq '' ) {
  130.         # send a bounce message to sender
  131.         # Using elm here is a horrendous kluge. We should probably
  132.         # use smail instead.
  133.         open(ELM,"|/usr/local/bin/elm -s \"ACS Reply to $target_alias Failed\" $address");
  134.         print ELM "Alias $target_alias not found in database.\nSorry.\nACS\n";
  135.         close(ELM);
  136.         # log the failure
  137.         if ( $replog ) {
  138.             print REPLOG "::: No recip_address for $target_alias\n";
  139.         }
  140.         goto repfail;
  141.     }
  142.  
  143.     # open a pipe into acsmail to send out the anonymous reply
  144.     if (! open(REPLY,"|/usr/personals/acsmail -F $sender_alias@alembic.ACS.COM $recip_address")) {
  145.         goto repfail;
  146.     }
  147.  
  148.     # write the reply into acsmail's stdin. acsmail will add the other
  149.     # header fields.
  150.     print REPLY "Subject: $subject\n";
  151.     print REPLY "To: $recip_address\n\n";
  152.     print REPLY @body;
  153.     close(REPLY);
  154.  
  155.     # since the reply apparently succeeded, unlink the repair file.
  156.     unlink($repname);
  157.     next;
  158.     # something broke. Note it in the repair file and do the next one.
  159. repfail:
  160.     print REPLOG "::: Reply failed\n" if ( $replog );
  161.     next;
  162. } continue {
  163.     # if a repair file is open, close it
  164.     close(REPLOG) if ( $replog );
  165.     # and delete the spooled reply file.
  166.     unlink($repfile);
  167. }
  168. #
  169. # Now do the messages to be posted
  170. #
  171. while (</usr/personals/spool/POST*> ) {
  172.     $postfile = $_;
  173.  
  174.     # check for other things that shouldn't be disturbed
  175.     &fstchk;
  176.  
  177.     # open the spooled POST file
  178.     open(MSG,"<$postfile");
  179.  
  180.     # Load the message on MSG into an array
  181.     @message = <MSG> ;
  182.     close(MSG);
  183.  
  184.     # get the sender's address from the From: line
  185.     $address = &getaddr;
  186.     # if it's empty, forget it and do the next message
  187.     goto postfail if ( "$address" eq '' );
  188.  
  189.     # get the username from the address
  190.     $user = &getuser($address);
  191.  
  192.     # if the username is empty or forbidden, do the next message
  193.     goto postfail if ( "$user" eq '' );
  194.  
  195.     # get the subject from the Subject: line
  196.     $subject = &getsubj;
  197.  
  198.     # trash postings with "test" in the Subject: line
  199.     next if ( $subject =~ /test/io );
  200.  
  201.     # if there is no subject, insert one
  202.     $subject = "(None)" if ( $subject eq '' );
  203.  
  204.     # get the sender's alias. assign one if necessary.
  205.     $alias = &getsender($address);
  206.  
  207.     # open a pipe into inews for the article
  208.     if ( ! open(INEWS,"| /bin/inews -h")) {
  209.         print STDERR "Can\'t pipe into inews\n";
  210.         goto postfail;
  211.     }
  212.  
  213.     # write the article into inews' stdin
  214.     print INEWS "Path: $alias\n";
  215.     print INEWS "From: ",$alias,"@alembic.ACS.COM\n";
  216.     print INEWS "Newsgroups: alt.personals\n";
  217.     print INEWS "Subject: $subject\n";
  218.     print INEWS "Distribution: local\n";
  219.     print INEWS "Reply-To: ",$alias,"@alembic.ACS.COM\n";
  220.     print INEWS "Followup-To: sender\n";
  221.     print INEWS "Organization: Anonymous Contact Service\n";
  222.     print INEWS "\n";
  223.     print INEWS @body;
  224.  
  225.     # add the ACS usage "signature"
  226.     print INEWS "\n-- \n";
  227.     print INEWS <<EOS;
  228. To use this service, send email to:                   | There is a 25 line
  229. Anonymous posting:    acs-post@alembic.ACS.COM      | limit on all posts
  230. Anonymous reply:    <user's alias>@alembic.ACS.COM| and e-mail messages.
  231. Test path/get an alias: acs-ping@alembic.ACS.COM      | Alternate path:
  232. ACS administrator:    acs-admin@alembic.ACS.COM     | uunet!alembic!...
  233. EOS
  234.     close(INEWS);
  235. postfail:
  236.     next;
  237. } continue {
  238.     # delete the spooled POST file
  239.     unlink($postfile);
  240. }
  241.  
  242. #
  243. # Process the acs-ping messages
  244. #
  245. while (</usr/personals/spool/PING*> ) {
  246.     $pingfile = $_;
  247.  
  248.     # don't disturb high-priority processing
  249.     &fstchk;
  250.  
  251.     # open the spooled PING file
  252.     open(MSG,"<$pingfile");
  253.  
  254.     # Load the message on MSG into an array
  255.     @message = <MSG> ;
  256.     close(MSG);
  257.  
  258.     # get the sender's address from the From: line
  259.     $address = &getaddr;
  260.  
  261.     # forget it and do the next one if no address
  262.     goto pingfail if ( "$address" eq '' );
  263.  
  264.     # extract the username from the address
  265.     $user = &getuser($address);
  266.  
  267.     # skip to next message if the username is empty or forbidden
  268.     goto pingfail if ( "$user" eq '' );
  269.  
  270.     # get the sender's alias, assigning one if necessary
  271.     $sender_alias = &getsender($address);    
  272.     $| = 1;
  273.  
  274.     # open a pipe into smail to send the echo back
  275.     open(REPLY,"|smail -F acs-ping@alembic.ACS.COM $address")
  276.             || die "Can't pipe into smail\n";
  277.  
  278.     # write the ping message into smail's stdin
  279.     print REPLY "Subject: Message RCVD\n";
  280.     print REPLY "To: $address\n\n";
  281.     print REPLY "Your ping request has been received by acs-ping@alembic.ACS.COM\n";
  282.     print REPLY "Your alias will be $sender_alias@alembic.ACS.COM\n";
  283.     print REPLY "The header of your message as it arrived here follows:\n\n";
  284.  
  285.     # send them a copy of their message header. Who knows why?
  286.     print REPLY @header;
  287.     close(REPLY);
  288. pingfail:
  289.     next;
  290. } continue {
  291.     # delete the spooled PING file
  292.     unlink($pingfile);
  293. }
  294.  
  295. # cleanup: close the real2alias database, delete the lock file, delete
  296. # and rewrite the alias-index file, and exit.
  297. dbmclose(r2a);
  298. unlink("/usr/personals/LCK..SPOOL");
  299. unlink('/usr/personals/alias-index');
  300. open(INDEX,'>/usr/personals/alias-index') ||
  301.     die "Can't open alias-index: $!\n";
  302. print INDEX $alias_index;
  303. close(INDEX);
  304. exit(0);
  305.  
  306. # subroutine fstchk checks to see if there are any conditions on the
  307. # system which unspool would interfere with. Mostly, this consists of
  308. # high-speed data transfers and high-priority processes running. If
  309. # fstchk finds such a condition, it sleeps for 30 seconds, then checks
  310. # again to see if the condition still exists. It continues this loop
  311. # forever.
  312. sub fstchk {
  313.     #
  314.     # If we're using a high-speed line, sleep until the call ends
  315.     # (otherwise the transfer rate drops through the floor)
  316.     #
  317.     while ( -e "/usr/spool/uucp/LCK/LCK..uunet" ) {
  318.         sleep 30;
  319.     }
  320. } # end subroutine fstchk
  321.  
  322. #
  323. # Subroutine getaddr splits the message in global array @message
  324. # into global arrays @header and @body, truncates @body to 25
  325. # lines, tries to find a signature in @body and deletes it if it
  326. # finds one, the searches @header for a From: line and extracts
  327. # the actual address from it if it can.
  328. # Returns $address.
  329. #
  330. sub getaddr {
  331.     #
  332.     # split the message into body and header
  333. hb:    for ( $line = 0 ; $line <= $#message; $line++ ) {
  334.         if ( $message[$line] eq "\n" ) {
  335.             # store the header
  336.             @header = @message[ 0 .. $line-1 ];
  337.             # store the first 25 lines of the body
  338.             @body = @message [ $line+1 .. $line+25 ];
  339.             last hb;
  340.         }
  341.     }
  342.     #
  343.     # Trash the signature if present 
  344. sig:    for ($line = 0; $line <= $#body; $line++ ) {
  345.         if ( $body[$line] eq "-- \n" || $body[$line] =~ /---/ ) {
  346.             $#body = $line - 1;
  347.             last sig;
  348.         }
  349.     }
  350.     #
  351.     # Get From: line from header
  352.     $from = '';
  353. from:    for ($line = 0; $line <= $#header; $line++ ) {
  354.         if ( $header[$line] =~ /^From: (.*)/ ) {
  355.             $from = $1;
  356.             last from;
  357.         }
  358.     }
  359.     #
  360.     # No From: line
  361.     #
  362.     #
  363.     if ( $line > $#header) {
  364.         return '';
  365.     }
  366.     # Try to extract actual address from $from line
  367.     # look for <bangpath> form first, since that's what uunet
  368.     # put's into the From: line
  369.     if ( $from =~ /<(.*)>/ ) {
  370.         $Address = $1;
  371.     }
  372.     else {
  373.         # try From: address ( comment )
  374.         if ( $from =~ /(.*) \(.*\)/ ) {
  375.             $Address = $1;
  376.         }
  377.         else {
  378.             # just use whatever's there
  379.             $Address = $from;
  380.         }
  381.     }
  382.     # get rid of any whitespace following the address
  383.     ($Address,$junk) = split(/[ \t]/,$Address);
  384.     # return the address
  385.     $Address;
  386. } # end subroutine getaddr
  387.  
  388. #
  389. # subroutine getuser($address) -
  390. # extract the username from an address and check to make sure it isn't
  391. # one of the "forbidden" usernames. Returns either null or the username.
  392. #
  393.  
  394. sub getuser {
  395.     local($addr) = pop(@_);
  396.     #
  397.     # if sender is uucp, news, mailer-daemon, etc., junk the message
  398.     # get the last ! component
  399.     @phase1 = split(/!/,$addr);
  400.     $usr = $phase1[$#phase1];
  401.     # get whatever sits in front of an "@".
  402.     @phase2 = split(/@/,$usr);
  403.     $usr = $phase2[0];
  404.     # get whatever precedes a "%"
  405.     @phase3 = split(/%/,$usr);
  406.     $usr = $phase3[0];
  407.  
  408.     # check for anything that might conceivably be the username
  409.     # of something that bounces mail, rather than a person. We
  410.     # also exclude root, simply because there are too many root
  411.     # users doing system administration at some sites.
  412.     study $usr;
  413.     if ( $usr =~ /MAILER/i) { return(''); }
  414.     if ( $usr =~ /DAEMON/i)    { return(''); }
  415.     if ( $usr =~ /uucp/i)    { return(''); }
  416.     if ( $usr =~ /POSTMASTER/i)    { return(''); }
  417.     if ( $usr =~ /DELIVER/i) { return(''); }
  418.     if ( $usr =~ /news/i)    { return(''); }
  419.     if ( $usr =~ /root/)    { return(''); }
  420.     $usr;
  421. } # end subroutine getuser
  422.  
  423. # subroutine getsubj - search through the global array @header until
  424. # we find a Subject: line. Extract and return the subject.
  425.  
  426. sub getsubj {
  427.     #
  428.     # Get Subject: line from header
  429.     $subj = '';
  430.     for ($line = 0; $line <= $#header; $line++ ) {
  431.         if ( $header[$line] =~ /^Subject: (.*)$/ ) {
  432.             $subj = $1;
  433.             last ;
  434.         }
  435.     }
  436.     $subj;
  437. } # end subroutine getsubj
  438.  
  439. #
  440. # subroutine getsender($address) - given the address of the sender of
  441. # a message, find hir alias in the real2alias database and return the
  442. # alias. If the sender is not in the database, add them. Returns the
  443. # sender's alias.
  444.  
  445. sub getsender {
  446.     local($addr) = pop(@_);
  447.     #
  448.     # Lookup sender in real2alias db
  449.     $Salias = $r2a{$addr};
  450.     if ( ! defined($Salias)) {
  451.         # create alias for sender
  452.         $alias_index++;
  453.         $Salias = "acs-".$alias_index;
  454.         # add the newbie to the database
  455.         $r2a{"$addr"} = $Salias;
  456.         # Add alias to /usr/lib/aliases
  457.         open(SYSALIAS,">>/usr/lib/aliases") || 
  458.             die "Can't write to aliases file: $!\n";
  459.         print SYSALIAS "$Salias: \"|/usr/personals/anon-reply $Salias\"\n";
  460.         close(SYSALIAS);
  461.         #
  462.         # need to execute newaliases here if sendmail doesn't
  463.         # support OD flag to automatically update dbm database
  464.         # of course, this is irrelevant if the MTA doesn't
  465.         # use dbm database.
  466.         # system("/usr/ucb/newaliases");
  467.     }
  468.     # return the alias
  469.     $Salias;
  470. } # end subroutine getsender
  471.