home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume34 / mailagent / patch13 next >
Text File  |  1992-12-12  |  47KB  |  1,375 lines

  1. Newsgroups: comp.sources.misc
  2. From: ram@eiffel.com (Raphael Manfredi)
  3. Subject:  v34i025:  mailagent - Rule Based Mail Filtering, Patch13
  4. Message-ID: <1992Dec13.022249.29882@sparky.imd.sterling.com>
  5. X-Md4-Signature: a3c760916049925ca96b1720928a195c
  6. Date: Sun, 13 Dec 1992 02:22:49 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: ram@eiffel.com (Raphael Manfredi)
  10. Posting-number: Volume 34, Issue 25
  11. Archive-name: mailagent/patch13
  12. Environment: Perl, Sendmail, UNIX
  13. Patch-To: mailagent: Volume 33, Issue 93-109
  14.  
  15. [Please note that mailagent was initially posted to comp.sources.misc]
  16. [at patchlevel 12. This is the first of two patches (13 and 14) being]
  17. [posted, bringing mailagent to version 2.9 patchlevel 14.    -Kent+  ]
  18.  
  19. System: mailagent version 2.9
  20. Patch #: 13
  21. Priority: MEDIUM
  22. Subject: changed Configure test for 'union wait'
  23. Subject: chkagent could report errors due to spurious matches
  24. Subject: added extra checking for writes to soft NFS-mounted disks
  25. Subject: filter now also complains when using -t in setgid mode
  26. Subject: removed spurious inclusion of <sys/types.h>
  27. Subject: hostname is now computed once and cached
  28. Subject: fixed various typos on the word "Precedence"
  29. Subject: new paragraph about file inclusion
  30. Subject: allowed file inclusion for KEEP and STRIP
  31. Subject: new macros %A, %C, %I and %O
  32. Subject: remove context file lock when excessively old
  33. Subject: action parsing rewritten to handle nested braces
  34. Subject: forgot to handle the %H macro
  35. Subject: (reported by David Giddy <d.giddy@trl.oz.au>)
  36. Subject: now also understands multiple To and Cc lines in headers
  37. Subject: added internet info extraction out of e-mail address
  38. Subject: now takes care of escaped ';' for layout purposes
  39. Subject: read statistics lines one at a time to limit memory usage
  40. Subject: added new tests for file inclusion with KEEP and STRIP
  41. Date: Tue Dec  1 09:48:46 PST 1992
  42. From: Raphael Manfredi <ram@eiffel.com>
  43.  
  44. Description:
  45.     Changed Configure test for 'union wait'. A lot of platforms had
  46.     problems with that and had to manually undefine UNION_WAIT from
  47.     config.h. Configure now looks for 'union.*wait.*{' in <sys/wait.h>
  48.     to see whether your system wants a plain int pointer or a union
  49.     wait pointer.
  50.  
  51.     chkagent could report errors due to spurious matches. This script
  52.     (intended to be run through cron) gave false alarms when a message
  53.     subject contained the word 'ERROR' for instance, and was logged.
  54.     The script now makes sure such a word is preceded by ': ' in the
  55.     logfile. This should reduce the chance of getting an error report
  56.     whereas nothing went wrong.
  57.  
  58.     Added extra checking for writes to soft NFS-mounted disks. The filter
  59.     program makes all the necessary system call status checks when queuing
  60.     a message. However, when writing on a soft NFS partition, I once got
  61.     an empty message with no error report from write. So the filter now
  62.     stats the queued file to make sure its size matches the size of the
  63.     mail read from sendmail.
  64.  
  65.     The filter now also complains when using -t in setgid mode. It already
  66.     complained when used in setuid mode, but I discovered a way to breach
  67.     through security by using only the setgid bit, so...
  68.  
  69.     Removed spurious inclusion of <sys/types.h> in parser.c. This could
  70.     prevent the parser from actually compiling.
  71.  
  72.     Fixed various typos on the word "Precedence" throughout the manual page.
  73.  
  74.     There is a new paragraph about file inclusion in the manual page,
  75.     explaining what it is and how it works.
  76.  
  77.     Allowed file inclusion for KEEP and STRIP. I've also made sure that
  78.     those worked even when mail headers are not normalized. For instance,
  79.     'STRIP Cc' should strip a 'cc:' line in the message header.
  80.  
  81.     New macros %A, %C, %I and %O. Refer to the manual page for details.
  82.  
  83.     Action parsing was rewritten to handle nested braces, in anticipation
  84.     for other features I'd like to add.
  85.  
  86.     Forgot to handle the %H macro (reported by David Giddy
  87.     <d.giddy@trl.oz.au>).
  88.  
  89.     Now understands multiple To and Cc lines in headers. The fields
  90.     are correctly concatenated, for filtering purposes, into a long list
  91.     of comma separated addresses.
  92.  
  93.     Now takes care of escaped ';' for layout purposes (when dumping rules).
  94.  
  95.     Read statistics lines one at a time to limit memory usage. If you are
  96.     collecting statistics and have changed your rule file so often that
  97.     your statistics file is huge (say 400 Kb), then you may have noticed
  98.     excessive memory consumptions, since the mailagent was trying to load
  99.     that file into memory without any pre-extension, thus causing the
  100.     process to grow rapidly as numerous realloc() occured.
  101.  
  102.     Added new tests for file inclusion with KEEP and STRIP and make sure
  103.     they behave in a case insensitive manner.
  104.  
  105.     Three new files were added in agent/pl/.
  106.  
  107.  
  108. Fix:    From rn, say "| patch -p -N -d DIR", where DIR is your mailagent source
  109.     directory.  Outside of rn, say "cd DIR; patch -p -N <thisarticle".
  110.     If you don't have the patch program, apply the following by hand,
  111.     or get patch (version 2.0, latest patchlevel).
  112.  
  113.     After patching:
  114.         *** DO NOTHING--INSTALL ALL PATCHES UP THROUGH #14 FIRST ***
  115.  
  116.     If patch indicates that patchlevel is the wrong version, you may need
  117.     to apply one or more previous patches, or the patch may already
  118.     have been applied.  See the patchlevel.h file to find out what has or
  119.     has not been applied.  In any event, don't continue with the patch.
  120.  
  121.     If you are missing previous patches they can be obtained from me:
  122.  
  123.         Raphael Manfredi <ram@eiffel.com>
  124.  
  125.     If you send a mail message of the following form it will greatly speed
  126.     processing:
  127.  
  128.         Subject: Command
  129.         @SH mailpatch PATH mailagent 2.9 LIST
  130.                ^ note the c
  131.  
  132.     where PATH is a return path FROM ME TO YOU either in Internet notation,
  133.     or in bang notation from some well-known host, and LIST is the number
  134.     of one or more patches you need, separated by spaces, commas, and/or
  135.     hyphens.  Saying 35- says everything from 35 to the end.
  136.  
  137.     To get some more detailed instructions, send me the following mail:
  138.  
  139.         Subject: Command
  140.         @SH mailhelp PATH
  141.  
  142.  
  143. Index: patchlevel.h
  144. Prereq: 12
  145. 4c4
  146. < #define PATCHLEVEL 12
  147. ---
  148. > #define PATCHLEVEL 13
  149.  
  150. Index: agent/man/mailagent.SH
  151. Prereq: 2.9.1.6
  152. *** agent/man/mailagent.SH.old    Tue Dec  1 09:47:54 1992
  153. --- agent/man/mailagent.SH    Tue Dec  1 09:47:56 1992
  154. ***************
  155. *** 18,24 ****
  156.   .TH MAILAGENT $manext "Version $VERSION PL$PATCHLEVEL"
  157.   ''' @(#) Manual page for mailagent's filter -- (c) ram February 1991
  158.   '''
  159. ! ''' $Id: mailagent.SH,v 2.9.1.6 92/11/10 10:12:13 ram Exp $
  160.   '''
  161.   '''  Copyright (c) 1991, 1992, Raphael Manfredi
  162.   '''
  163. --- 18,24 ----
  164.   .TH MAILAGENT $manext "Version $VERSION PL$PATCHLEVEL"
  165.   ''' @(#) Manual page for mailagent's filter -- (c) ram February 1991
  166.   '''
  167. ! ''' $Id: mailagent.SH,v 2.9.1.7 92/12/01 09:16:23 ram Exp $
  168.   '''
  169.   '''  Copyright (c) 1991, 1992, Raphael Manfredi
  170.   '''
  171. ***************
  172. *** 26,31 ****
  173. --- 26,37 ----
  174.   '''  License as specified in the README file that comes with dist.
  175.   '''
  176.   ''' $Log:    mailagent.SH,v $
  177. + ''' Revision 2.9.1.7  92/12/01  09:16:23  ram
  178. + ''' patch13: fixed various typos on the word "Precedence"
  179. + ''' patch13: new paragraph about file inclusion
  180. + ''' patch13: allowed file inclusion for KEEP and STRIP
  181. + ''' patch13: new macros %A, %C, %I and %O
  182. + ''' 
  183.   ''' Revision 2.9.1.6  92/11/10  10:12:13  ram
  184.   ''' patch12: perl interface functions now return 1 for success
  185.   ''' 
  186. ***************
  187. *** 54,60 ****
  188.   ''' 
  189.   ''' Revision 2.9.1.1  92/07/25  12:35:51  ram
  190.   ''' patch1: now respects English uppercased title conventions
  191. ! ''' patch1: a bulk or junk Precendence header voids vacation message
  192.   ''' patch1: documents the minimal set of header selectors available
  193.   ''' patch1: host name in p_host config variable cannot have domain name
  194.   ''' 
  195. --- 60,66 ----
  196.   ''' 
  197.   ''' Revision 2.9.1.1  92/07/25  12:35:51  ram
  198.   ''' patch1: now respects English uppercased title conventions
  199. ! ''' patch1: a bulk or junk Precedence header voids vacation message
  200.   ''' patch1: documents the minimal set of header selectors available
  201.   ''' patch1: host name in p_host config variable cannot have domain name
  202.   ''' 
  203. ***************
  204. *** 1267,1273 ****
  205.   a "KEEP From To Cc Subject" will keep only the principal fields from the
  206.   mail message. This is suitable for archving mailing lists messages.
  207.   You may add a ':' after each header field name if you wish, but that is not
  208. ! strictly necessary.
  209.   (Does not modify existing status)
  210.   .TP
  211.   LEAVE
  212. --- 1273,1280 ----
  213.   a "KEEP From To Cc Subject" will keep only the principal fields from the
  214.   mail message. This is suitable for archving mailing lists messages.
  215.   You may add a ':' after each header field name if you wish, but that is not
  216. ! strictly necessary. Headers may be specified using shell-style regular
  217. ! expressions, and file inclusion is allowed to get headers from a file.
  218.   (Does not modify existing status)
  219.   .TP
  220.   LEAVE
  221. ***************
  222. *** 1431,1437 ****
  223.   Remove the corresponding lines in the header of the mail. For instance,
  224.   a "STRIP Newsgroups Apparently-To" will remove the appropriate lines to wipe
  225.   out any Newsgroups: or Apparently-To: header. You may add a ':' after each
  226. ! header field name if you wish, but that is not strictly necessary.
  227.   (Does not alter execution status)
  228.   .TP
  229.   SUBST \fIvar expression\fR
  230. --- 1438,1445 ----
  231.   Remove the corresponding lines in the header of the mail. For instance,
  232.   a "STRIP Newsgroups Apparently-To" will remove the appropriate lines to wipe
  233.   out any Newsgroups: or Apparently-To: header. You may add a ':' after each
  234. ! header field name if you wish, but that is not strictly necessary. Headers
  235. ! may be specified via shell-style regular expressions or via "file" inclusion.
  236.   (Does not alter execution status)
  237.   .TP
  238.   SUBST \fIvar expression\fR
  239. ***************
  240. *** 1616,1622 ****
  241.   The login name of the address on the From: line.
  242.   .TP
  243.   .I \$precedence
  244. ! The content of the Precendence: line, if any at all.
  245.   .TP
  246.   .I \$sender
  247.   The sender of the message (may have a comment), derived in the same way the
  248. --- 1624,1630 ----
  249.   The login name of the address on the From: line.
  250.   .TP
  251.   .I \$precedence
  252. ! The content of the Precedence: line, if any at all.
  253.   .TP
  254.   .I \$sender
  255.   The sender of the message (may have a comment), derived in the same way the
  256. ***************
  257. *** 1666,1671 ****
  258. --- 1674,1693 ----
  259.   includes scripts started via the PERL command and mail hooks. The latter will
  260.   be described in detail further down.
  261.   '''
  262. + .SS "File inclusion"
  263. + .PP
  264. + Some commands like FORWARD or KEEP allow you to specify a file name between
  265. + double quotes to actually load parameters from this file. Unless a full path
  266. + is given, the following method is used to locate the file: first in the location
  267. + pointed to by the \fImailfilter\fR variable if set, otherwise in \fImaildir\fR
  268. + and finally in the home directory. Note that this is not a search path in the
  269. + sense that if \fImailfilter\fR is defined and the file is not there, an error
  270. + will be reported.
  271. + .PP
  272. + The file should list each parameter (be it an address, a header or a pattern)
  273. + on a line by itself. Shell-style comments (#) are allowed within that file and
  274. + leading white spaces are trimmed (but not trailing spaces).
  275. + '''
  276.   .SS "Macros Substitutions"
  277.   .PP
  278.   All the commands go through a macro substitution mechanism before being
  279. ***************
  280. *** 1676,1686 ****
  281.   %%
  282.   A real percent sign
  283.   .TP
  284.   %D
  285.   Day of the week (0-6)
  286.   .TP
  287.   %H
  288. ! Host name (name of the machine on which the \fImailagent\fR runs)
  289.   .TP
  290.   %L
  291.   Length of the body part, in bytes
  292. --- 1698,1721 ----
  293.   %%
  294.   A real percent sign
  295.   .TP
  296. + %A
  297. + The internet address extracted out of the \fIFrom:\fR field (\fIa.b.c\fR
  298. + in \fIu@a.b.c\fR), converted to lower-case.
  299. + .TP
  300. + %C
  301. + CPU name on which the mailagent runs. That is a fully qualified hostname
  302. + with the domain name, e.g. \fIlyon.eiffel.com\fR.
  303. + .TP
  304.   %D
  305.   Day of the week (0-6)
  306.   .TP
  307.   %H
  308. ! Host name (name of the machine on which the \fImailagent\fR runs), without
  309. ! any domain name. Always in lower-case, regardless of the machine name.
  310. ! .TP
  311. ! %I
  312. ! The internet domain name extracted out of the \fIFrom:\fR field (\fIb.c\fR
  313. ! in \fIu@a.b.c\fR), converted to lower-case.
  314.   .TP
  315.   %L
  316.   Length of the body part, in bytes
  317. ***************
  318. *** 1688,1693 ****
  319. --- 1723,1732 ----
  320.   %N
  321.   Full name of the sender (login name if none)
  322.   .TP
  323. + %O
  324. + The organization name extracted out of the \fIFrom:\fR field (\fIb\fR in
  325. + \fIu@a.b.c\fR), converted to lower-case.
  326. + .TP
  327.   %R
  328.   Subject of the original message with leading Re: suppressed
  329.   .TP
  330. ***************
  331. *** 1891,1897 ****
  332.   
  333.   Sincerely,
  334.   --
  335. ! %U <%u@%H>
  336.   .fi
  337.   .in -5
  338.   .sp
  339. --- 1930,1936 ----
  340.   
  341.   Sincerely,
  342.   --
  343. ! %U <%u@%C>
  344.   .fi
  345.   .in -5
  346.   .sp
  347. ***************
  348. *** 1905,1911 ****
  349.   \fInewsmaster\fR, \fIusenet\fR, \fIMAILER-DAEMON\fR or \fInobody\fR).
  350.   Matches are done in a case insentive manner, so \fIMailer-Daemon\fR will also
  351.   be recognized as a special user.
  352. ! Furthermore, any message tagged with a \fIPrecendence:\fR field set to
  353.   \fIbulk\fR or \fIjunk\fR will not trigger a vacation message. This built-in
  354.   behaviour can of course be overloaded by suitable rules (by testing and
  355.   issuing the vacation message yourself via MESSAGE).
  356. --- 1944,1950 ----
  357.   \fInewsmaster\fR, \fIusenet\fR, \fIMAILER-DAEMON\fR or \fInobody\fR).
  358.   Matches are done in a case insentive manner, so \fIMailer-Daemon\fR will also
  359.   be recognized as a special user.
  360. ! Furthermore, any message tagged with a \fIPrecedence:\fR field set to
  361.   \fIbulk\fR or \fIjunk\fR will not trigger a vacation message. This built-in
  362.   behaviour can of course be overloaded by suitable rules (by testing and
  363.   issuing the vacation message yourself via MESSAGE).
  364.  
  365. Index: agent/pl/actions.pl
  366. Prereq: 2.9.1.3
  367. *** agent/pl/actions.pl.old    Tue Dec  1 09:48:00 1992
  368. --- agent/pl/actions.pl    Tue Dec  1 09:48:01 1992
  369. ***************
  370. *** 1,4 ****
  371. ! ;# $Id: actions.pl,v 2.9.1.3 92/11/01 15:44:28 ram Exp $
  372.   ;#
  373.   ;#  Copyright (c) 1992, Raphael Manfredi
  374.   ;#
  375. --- 1,4 ----
  376. ! ;# $Id: actions.pl,v 2.9.1.4 92/12/01 09:18:05 ram Exp $
  377.   ;#
  378.   ;#  Copyright (c) 1992, Raphael Manfredi
  379.   ;#
  380. ***************
  381. *** 6,11 ****
  382. --- 6,15 ----
  383.   ;#  Licence as specified in the README file that comes with dist.
  384.   ;#
  385.   ;# $Log:    actions.pl,v $
  386. + ;# Revision 2.9.1.4  92/12/01  09:18:05  ram
  387. + ;# patch13: allowed file inclusion for KEEP and STRIP
  388. + ;# patch13: file inclusion processing now handled by &include_file
  389. + ;# 
  390.   ;# Revision 2.9.1.3  92/11/01  15:44:28  ram
  391.   ;# patch11: the PERL command now sets up @ARGV as if invoked from shell
  392.   ;# patch11: fixed message substitution bug (for MESSAGE and NOTIFY)
  393. ***************
  394. *** 407,413 ****
  395.       local($address) = &email_addr;    # Address of user
  396.       # Any address included withing "" is in fact a file name where actual
  397.       # forwarding addresses are found.
  398. !     $addresses = &complete_addr($addresses);    # Process "include-requests"
  399.       unless (open(MAILER,"|/usr/lib/sendmail -odq $addresses")) {
  400.           do add_log("cannot run sendmail to forward message") if $loglvl > 0;
  401.           return 1;
  402. --- 411,418 ----
  403.       local($address) = &email_addr;    # Address of user
  404.       # Any address included withing "" is in fact a file name where actual
  405.       # forwarding addresses are found.
  406. !     $addresses =
  407. !         &complete_list($addresses, 'address');    # Process "include-requests"
  408.       unless (open(MAILER,"|/usr/lib/sendmail -odq $addresses")) {
  409.           do add_log("cannot run sendmail to forward message") if $loglvl > 0;
  410.           return 1;
  411. ***************
  412. *** 439,445 ****
  413.       local($addresses) = @_;            # Address(es) mail should be bounced to
  414.       # Any address included withing "" is in fact a file name where actual
  415.       # bouncing addresses are found.
  416. !     $addresses = &complete_addr($addresses);    # Process "include-requests"
  417.       unless (open(MAILER,"|/usr/lib/sendmail -odq $addresses")) {
  418.           do add_log("cannot run sendmail to bounce message") if $loglvl > 0;
  419.           return 1;
  420. --- 444,451 ----
  421.       local($addresses) = @_;            # Address(es) mail should be bounced to
  422.       # Any address included withing "" is in fact a file name where actual
  423.       # bouncing addresses are found.
  424. !     $addresses =
  425. !         &complete_list($addresses, 'address');    # Process "include-requests"
  426.       unless (open(MAILER,"|/usr/lib/sendmail -odq $addresses")) {
  427.           do add_log("cannot run sendmail to bounce message") if $loglvl > 0;
  428.           return 1;
  429. ***************
  430. *** 955,975 ****
  431.   # Removes or keeps some headers and update the Header structure
  432.   sub alter_header {
  433.       local($headers, $action) = @_;
  434.       local(@list) = split(/\s/, $headers);
  435.       local(@head) = split(/\n/, $Header{'Head'});
  436.       local(@newhead);                # The constructed header
  437.       local($last_was_altered) = 0;    # Set to true when header is altered
  438.       local($matched);                # Did any header matched ?
  439.       foreach (@head) {
  440.           if (/^From\s/) {            # First From line...
  441.               push(@newhead, $_);        # Keep it anyway
  442.               next;
  443.           }
  444.           unless (/^\s/) {            # If not a continuation line
  445.               $last_was_altered = 0;    # Reset header alteration flag
  446.               $matched = 0;            # Assume no match
  447.               foreach $h (@list) {    # Loop over to-be-altered lines
  448. -                 $h =~ s/:$//;        # Remove trailing ':' if any
  449.                   if (/^$h:/i) {        # We found a line to be removed/kept
  450.                       $matched = 1;
  451.                       last;
  452. --- 961,992 ----
  453.   # Removes or keeps some headers and update the Header structure
  454.   sub alter_header {
  455.       local($headers, $action) = @_;
  456. +     $headers =
  457. +         &complete_list($headers, 'header');    # Process "file-inclusion"
  458.       local(@list) = split(/\s/, $headers);
  459.       local(@head) = split(/\n/, $Header{'Head'});
  460.       local(@newhead);                # The constructed header
  461.       local($last_was_altered) = 0;    # Set to true when header is altered
  462.       local($matched);                # Did any header matched ?
  463. +     local($line);                    # Original header line
  464. +     foreach $h (@list) {            # Prepare patterns
  465. +         $h =~ s/:$//;                # Remove trailing ':' if any
  466. +         $h = &perl_pattern($h);        # Headers specified by shell patterns
  467. +     }
  468.       foreach (@head) {
  469.           if (/^From\s/) {            # First From line...
  470.               push(@newhead, $_);        # Keep it anyway
  471.               next;
  472.           }
  473. +         $line = $_;                    # Save original
  474. +         # Make sure header field name is normalized before attempting a match
  475. +         s/^([\w-]+):/&header'normalize($1).':'/e;
  476.           unless (/^\s/) {            # If not a continuation line
  477.               $last_was_altered = 0;    # Reset header alteration flag
  478.               $matched = 0;            # Assume no match
  479.               foreach $h (@list) {    # Loop over to-be-altered lines
  480.                   if (/^$h:/i) {        # We found a line to be removed/kept
  481.                       $matched = 1;
  482.                       last;
  483. ***************
  484. *** 984,990 ****
  485.           } else {                                    # Action is $HD_KEEP
  486.               next if /^\s/ && !$last_was_altered;    # Header was not kept
  487.           }
  488. !         push(@newhead, $_);                        # Add line to the new header
  489.       }
  490.       $Header{'Head'} = join("\n", @newhead) . "\n";
  491.   }
  492. --- 1001,1007 ----
  493.           } else {                                    # Action is $HD_KEEP
  494.               next if /^\s/ && !$last_was_altered;    # Header was not kept
  495.           }
  496. !         push(@newhead, $line);        # Add line to the new header
  497.       }
  498.       $Header{'Head'} = join("\n", @newhead) . "\n";
  499.   }
  500. ***************
  501. *** 1158,1194 ****
  502.       0;
  503.   }
  504.   
  505. ! # Given a list of addresses separated by white spaces, return a new list of
  506. ! # addresses, but with "include-request" processed.
  507. ! sub complete_addr {
  508.       local(@addr) = split(' ', $_[0]);    # Original list
  509.       local(@result);                        # Where result list is built
  510.       local($filename);                    # Name of include file
  511.       local($_);
  512.       foreach $addr (@addr) {
  513. !         if ($addr !~ /^"/) {            # Address not enclosed within ""
  514.               push(@result, $addr);        # Kept as-is
  515.           } else {
  516. !             ($filename) = $addr =~ /^"(.*)"$/;
  517. !             $filename = &locate_file($filename);
  518. !             if ($filename && open(ADDRESSES, "$filename")) {
  519. !                 while (<ADDRESSES>) {
  520. !                     next if /^\s*#/;    # Skip shell comments
  521. !                     chop;
  522. !                     s/^\s+//;            # Remove leading spaces
  523. !                     push(@result, $_);
  524. !                 }
  525. !                 close ADDRESSES;
  526. !             } elsif ($filename) {        # Could not open file
  527. !                 &add_log("WARNING couldn't open $filename for addresses: $!")
  528. !                     if $loglvl > 4;
  529. !             } else {
  530. !                 &add_log("WARNING incorrect file inclusion request")
  531. !                     if $loglvl > 4;
  532. !             }
  533.           }
  534.       }
  535. !     join(' ', @result);        # Return space separated addresses
  536.   }
  537.   
  538.   # Save digest mail into a folder, or queue it if no folder is provided
  539. --- 1175,1197 ----
  540.       0;
  541.   }
  542.   
  543. ! # Given a list of items separated by white spaces, return a new list of
  544. ! # items, but with "include-request" processed.
  545. ! sub complete_list {
  546.       local(@addr) = split(' ', $_[0]);    # Original list
  547. +     local($type) = $_[1];                # Type of item (header, address, ...)
  548.       local(@result);                        # Where result list is built
  549.       local($filename);                    # Name of include file
  550.       local($_);
  551.       foreach $addr (@addr) {
  552. !         if ($addr !~ /^"/) {            # Item not enclosed within ""
  553.               push(@result, $addr);        # Kept as-is
  554.           } else {
  555. !             # Load items from file whose name is given between "quotes"
  556. !             push(@result, &include_file($addr, $type));
  557.           }
  558.       }
  559. !     join(' ', @result);        # Return space separated items
  560.   }
  561.   
  562.   # Save digest mail into a folder, or queue it if no folder is provided
  563.  
  564. Index: agent/pl/lexical.pl
  565. Prereq: 2.9.1.2
  566. *** agent/pl/lexical.pl.old    Tue Dec  1 09:48:13 1992
  567. --- agent/pl/lexical.pl    Tue Dec  1 09:48:13 1992
  568. ***************
  569. *** 1,4 ****
  570. ! ;# $Id: lexical.pl,v 2.9.1.2 92/11/01 15:50:52 ram Exp $
  571.   ;#
  572.   ;#  Copyright (c) 1992, Raphael Manfredi
  573.   ;#
  574. --- 1,4 ----
  575. ! ;# $Id: lexical.pl,v 2.9.1.3 92/12/01 09:22:16 ram Exp $
  576.   ;#
  577.   ;#  Copyright (c) 1992, Raphael Manfredi
  578.   ;#
  579. ***************
  580. *** 6,11 ****
  581. --- 6,15 ----
  582.   ;#  Licence as specified in the README file that comes with dist.
  583.   ;#
  584.   ;# $Log:    lexical.pl,v $
  585. + ;# Revision 2.9.1.3  92/12/01  09:22:16  ram
  586. + ;# patch13: now counts lines even when reading rules from memory
  587. + ;# patch13: action parsing rewritten to handle nested braces
  588. + ;# 
  589.   ;# Revision 2.9.1.2  92/11/01  15:50:52  ram
  590.   ;# patch11: fixed English typo
  591.   ;# 
  592. ***************
  593. *** 29,34 ****
  594. --- 33,39 ----
  595.   # The following subroutine is called in place of read_rule when rules are
  596.   # coming from the command line via @Linerules.
  597.   sub read_linerule {
  598. +     $.++;                        # One more line
  599.       shift(@Linerules);            # Read a new line from array
  600.   }
  601.   
  602. ***************
  603. *** 116,135 ****
  604.       $pattern;
  605.   }
  606.   
  607.   sub get_action {
  608.       local(*line) = shift(@_);    # edited in place
  609.       local($_) = $line;            # make a copy of original
  610. !     local($action) = "";
  611. !     if (s/^\s*{([^}]*)}//) {
  612. !         $action = $1;
  613. !     } else {
  614. !         unless (/\{.*\}/) {        # trash line if no { action } is present
  615. !             &add_log("ERROR expected action, discarded '$_'") if $loglvl;
  616. !             $_ = '';
  617.           }
  618.       }
  619. -     $line = $_;                    # eventually updates the line
  620. -     $action =~ s/\s+$//;        # remove trailing spaces
  621. -     $action;
  622.   }
  623.   
  624. --- 121,182 ----
  625.       $pattern;
  626.   }
  627.   
  628. + # Extract the action part from the line (by editing it in place) and return
  629. + # the first action encountered. Nesting of {...} blocks may occur.
  630.   sub get_action {
  631.       local(*line) = shift(@_);    # edited in place
  632.       local($_) = $line;            # make a copy of original
  633. !     return '' unless s/^\s*\{/{/;
  634. !     local($action) = &action_parse(*_, 0);
  635. !     &add_log("ERROR no action, discarding '$_'") if $loglvl && $action eq '';
  636. !     $line = $_;                    # eventually update the line
  637. !     $action =~ s/^\{\s*//;        # remove leading and trailing braces
  638. !     $action =~ s/\s*\}$//;
  639. !     $action;                    # return new action block
  640. ! }
  641. ! # Recursively parse the action string and return the parsed portion of the text
  642. ! # with proper nesting wherever necessary. The string given as parameter is
  643. ! # edited in place and the remaining is the unparsed part.
  644. ! sub action_parse {
  645. !     local(*_) = shift(@_);        # edited in place
  646. !     local($level) = shift(@_);    # recursion level
  647. !     local($parsed) = '';        # the part we parsed so far
  648. !     local($block);                # block recognized
  649. !     local($follow);                # recursion string returned
  650. !     for (;;) {
  651. !         # Go to first un-escaped '{', if possible and save leading string
  652. !         # up-to first '{'. Note that any '}' immediately stops scanning.
  653. !         s/^(([^\\{}]|\\.)*{)// && ($parsed .= $1);
  654. !         # Go to first un-escaped '}', with any '{' stopping scan.
  655. !         $block = '';
  656. !         s/^(([^\\{}]|\\.)*\})// && ($block = $1);
  657. !         $parsed .= $block;        # block may be empty, or has trailing '}'
  658. !         if ($parsed =~ s/\{$//) {    # recursion if '{' found
  659. !             $follow = &action_parse(*_, $level + 1);
  660. !             # If a null string is returned, then no matching '}' was found
  661. !             &add_log("WARNING no closing brace (added for you)")
  662. !                 if $follow eq '' && $loglvl > 5;
  663. !             $parsed .= '{' . $follow . '}';
  664. !         } elsif (s/^\}//) {            # reached end of a block
  665. !             &add_log("WARNING extra closing brace ignored")
  666. !                 if $level == 0 && $loglvl > 5;
  667. !             return $parsed;
  668. !         } else {
  669. !             # Get the whole string until the next '}' and return. If a '{'
  670. !             # interposes, the first match will return an empty string. In that
  671. !             # case, we continue if we are not at level #0. Otherwise we got the
  672. !             # whole action and may return now.
  673. !             $block = '';
  674. !             s/^(([^\\{}]|\\.)*\})// && ($block = $1);
  675. !             if ($block eq '' && $level) {        # Advance until '{'
  676. !                 s/^(([^\\}]|\\.)*\{)// && ($block = $1);
  677. !                 $parsed .= $block;
  678. !                 next;
  679. !             }
  680. !             $block =~ s/\}//;
  681. !             return $parsed . $block;
  682.           }
  683.       }
  684.   }
  685.   
  686.  
  687. Index: agent/pl/macros.pl
  688. Prereq: 2.9.1.2
  689. *** agent/pl/macros.pl.old    Tue Dec  1 09:48:16 1992
  690. --- agent/pl/macros.pl    Tue Dec  1 09:48:16 1992
  691. ***************
  692. *** 1,4 ****
  693. ! ;# $Id: macros.pl,v 2.9.1.2 92/08/26 13:16:14 ram Exp $
  694.   ;#
  695.   ;#  Copyright (c) 1992, Raphael Manfredi
  696.   ;#
  697. --- 1,4 ----
  698. ! ;# $Id: macros.pl,v 2.9.1.3 92/12/01 09:24:09 ram Exp $
  699.   ;#
  700.   ;#  Copyright (c) 1992, Raphael Manfredi
  701.   ;#
  702. ***************
  703. *** 6,11 ****
  704. --- 6,16 ----
  705.   ;#  Licence as specified in the README file that comes with dist.
  706.   ;#
  707.   ;# $Log:    macros.pl,v $
  708. + ;# Revision 2.9.1.3  92/12/01  09:24:09  ram
  709. + ;# patch13: new macros %A, %C, %I and %O
  710. + ;# patch13: forgot to handle the %H macro
  711. + ;# patch13: (reported by David Giddy <d.giddy@trl.oz.au>)
  712. + ;# 
  713.   ;# Revision 2.9.1.2  92/08/26  13:16:14  ram
  714.   ;# patch8: added support for external variables (persistent)
  715.   ;# 
  716. ***************
  717. *** 18,27 ****
  718. --- 23,36 ----
  719.   ;# 
  720.   # Macros:
  721.   # %%     A real percent sign
  722. + # %A     Sender's main address (host.domain.ct in user@loc.host.domain.ct)
  723. + # %C     CPU name, fully qualified with domain name
  724.   # %D     Day of the week (0-6)
  725.   # %H     Host name (name of the machine on which the mailagent runs)
  726. + # %I     Internet domain from sender (domain.ct in user@host.domain.ct)
  727.   # %L     Length of the message in bytes (without header)
  728.   # %N     Full name of sender (login name if none)
  729. + # %O     Organization name from sender address (domain in user@host.domain.ct)
  730.   # %R     Subject of orginal message with leading Re: suppressed
  731.   # %S     Re: subject of original message
  732.   # %T     Time of last modification on mailed file (value taken from $macro_T)
  733. ***************
  734. *** 83,93 ****
  735. --- 92,106 ----
  736.   
  737.       s/%%/##pr##/g;                        # Protect double percent signs
  738.       s/%/#%%!/g;                            # Make sure substitutions do not add %
  739. +     s/#%%!A/¯o'internet/eg;            # Main internet address of sender
  740.       s/#%%!d/$mday/g;                    # Day of the month (01-31)
  741. +     s/#%%!C/&domain_addr/eg;            # CPU name, fully qualified with domain
  742.       s/#%%!D/$wday/g;                    # Day of the week (0-6)
  743.       s/#%%!f/$Header{'From'}/g;            # The "From:" line
  744.       s/#%%!h/$hour/g;                    # Hour of the day (00-23)
  745. +     s/#%%!H/&myhostname/eg;                # Hostname on which mailagent runs
  746.       s/#%%!i/$Header{'Message-Id'}/g;    # Message-Id (null string if none)
  747. +     s/#%%!I/¯o'domain/eg;            # Internet domain name of sender
  748.       s/#%%!l/$Header{'Lines'}/g;            # Number if lines in message
  749.       s/#%%!L/$Header{'Length'}/g;        # Length of message, in bytes
  750.       s/#%%!m/$mon/g;                        # Month of the year
  751. ***************
  752. *** 94,99 ****
  753. --- 107,113 ----
  754.       s/#%%!n/$login/g;                    # Lower-cased login name of sender
  755.       s/#%%!N/$fullname/g;                # Full name of sender (login if none)
  756.       s/#%%!o/$orgname/g;                    # Organization name
  757. +     s/#%%!O/¯o'org/eg;                # Organization part of sender's address
  758.       s/#%%!r/$reply_to/g;                # Return path of message
  759.       s/#%%!R/$subject/g;                    # Subject with leading Re: suppressed
  760.       s/#%%!s/$Header{'Subject'}/g;        # Subject of message
  761. ***************
  762. *** 114,117 ****
  763. --- 128,159 ----
  764.       s/##pr##/%/g;                        # A double percent expands to %
  765.       $str = $_;                            # Update string in-place
  766.   }
  767. + package macro;
  768. + # Return the internet information of the From address
  769. + sub info {
  770. +     local($addr) = (&'parse_address($'Header{'From'}))[0];
  771. +     &'internet_info($addr);
  772. + }
  773. + # Return the organization name
  774. + sub org {
  775. +     local($host, $domain, $country) = &info;
  776. +     $domain;
  777. + }
  778. + # Return the domain name
  779. + sub domain {
  780. +     local($host, $domain, $country) = &info;
  781. +     $domain .'.'. $country;
  782. + }
  783. + # Return the qualified internet address
  784. + sub internet {
  785. +     local($host, $domain, $country) = &info;
  786. +     $host ne '' ? $host .'.'. $domain .'.'. $country : $domain .'.'. $country;
  787. + }
  788. + package main;
  789.   
  790.  
  791. Index: agent/pl/matching.pl
  792. Prereq: 2.9.1.1
  793. *** agent/pl/matching.pl.old    Tue Dec  1 09:48:18 1992
  794. --- agent/pl/matching.pl    Tue Dec  1 09:48:19 1992
  795. ***************
  796. *** 1,4 ****
  797. ! ;# $Id: matching.pl,v 2.9.1.1 92/08/02 16:11:54 ram Exp $
  798.   ;#
  799.   ;#  Copyright (c) 1992, Raphael Manfredi
  800.   ;#
  801. --- 1,4 ----
  802. ! ;# $Id: matching.pl,v 2.9.1.2 92/12/01 09:25:48 ram Exp $
  803.   ;#
  804.   ;#  Copyright (c) 1992, Raphael Manfredi
  805.   ;#
  806. ***************
  807. *** 6,11 ****
  808. --- 6,15 ----
  809.   ;#  Licence as specified in the README file that comes with dist.
  810.   ;#
  811.   ;# $Log:    matching.pl,v $
  812. + ;# Revision 2.9.1.2  92/12/01  09:25:48  ram
  813. + ;# patch13: new perl_pattern function to transform shell-style patterns
  814. + ;# patch13: file inclusion now handled by include_file
  815. + ;# 
  816.   ;# Revision 2.9.1.1  92/08/02  16:11:54  ram
  817.   ;# patch2: added support for negated selectors
  818.   ;# 
  819. ***************
  820. *** 39,44 ****
  821. --- 43,57 ----
  822.       );
  823.   }
  824.   
  825. + # Transform a shell-style pattern into a perl pattern
  826. + sub perl_pattern {
  827. +     local($_) = @_;        # The shell pattern
  828. +     s/\./\\./g;            # Escape .
  829. +     s/\*/.*/g;            # Transform * into .*
  830. +     s/\?/./g;            # Transform ? into .
  831. +     $_;                    # Perl pattern
  832. + }
  833.   # Take a pattern as written in the rule file and make it suitable for
  834.   # pattern matching as understood by perl. If the pattern starts with a
  835.   # leading /, nothing is done. Otherwise, a set of / are added.
  836. ***************
  837. *** 46,56 ****
  838.   sub make_pattern {
  839.       local($_) = shift(@_);
  840.       unless (m|^/|) {                # Pattern does not start with a /
  841. !         # With simple words, patterns have the same form as shell ones
  842. !         s/\./\\./g;            # Escape .
  843. !         s/\*/.*/g;            # Transform * into .*
  844. !         s/\?/./g;            # Transform ? into .
  845. !         $_ = "/^$_\$/";        # Anchor pattern
  846.       }
  847.       # The whole pattern is inserted within () to make at least one
  848.       # backreference. Otherwise, the following could happen:
  849. --- 59,66 ----
  850.   sub make_pattern {
  851.       local($_) = shift(@_);
  852.       unless (m|^/|) {                # Pattern does not start with a /
  853. !         $_ = &perl_pattern($_);        # Simple words specified via shell patterns
  854. !         $_ = "/^$_\$/";                # Anchor pattern
  855.       }
  856.       # The whole pattern is inserted within () to make at least one
  857.       # backreference. Otherwise, the following could happen:
  858. ***************
  859. *** 76,106 ****
  860.       if ($pattern !~ /^"/) {
  861.           $matched = do apply_match($selector, $pattern);
  862.       } else {
  863. !         local(@filepat) = ();            # File pattern
  864. !         local($filename);                # Where pattern should be read from
  865. !         ($filename) =
  866. !             $pattern =~ /^"(.*)"$/;        # The filename is held within ""
  867. !         $filename =
  868. !             &locate_file($filename);    # Path may not be absolute
  869. !         if ($filename) {
  870. !             if (open(PATTERN, "$filename")) {
  871. !                 while (<PATTERN>) {
  872. !                     next if /^\s*#/;    # Skip shell comments
  873. !                     chop;
  874. !                     s/^\s*//;            # Remove leading spaces
  875. !                     push(@filepat, $_);
  876. !                     do add_log ("loading pattern $_") if $loglvl > 19;
  877. !                 }
  878. !                 close PATTERN;
  879. !             } else {
  880. !                 do add_log("WARNING couldn't open $filename for patterns")
  881. !                     if $loglvl > 4;
  882. !                 push(@filepat, "*");    # Ensure anything matches
  883. !             }
  884. !         } else {
  885. !             do add_log("WARNING incorrect file name $pattern") if $loglvl > 4;
  886. !             push(@filepat, "*");    # Ensure anything matches
  887. !         }
  888.           # Now do the match for all the patterns. Stop as soon as one matches.
  889.           foreach (@filepat) {
  890.               $matched = do apply_match($selector, $_);
  891. --- 86,93 ----
  892.       if ($pattern !~ /^"/) {
  893.           $matched = do apply_match($selector, $pattern);
  894.       } else {
  895. !         # Load patterns from file whose name is given between "quotes"
  896. !         local(@filepat) = &include_file($pattern, 'pattern');
  897.           # Now do the match for all the patterns. Stop as soon as one matches.
  898.           foreach (@filepat) {
  899.               $matched = do apply_match($selector, $_);
  900.  
  901. Index: agent/filter/io.c
  902. Prereq: 2.9
  903. *** agent/filter/io.c.old    Tue Dec  1 09:47:40 1992
  904. --- agent/filter/io.c    Tue Dec  1 09:47:41 1992
  905. ***************
  906. *** 11,17 ****
  907.   */
  908.   
  909.   /*
  910. !  * $Id: io.c,v 2.9 92/07/14 16:48:13 ram Exp $
  911.    *
  912.    *  Copyright (c) 1992, Raphael Manfredi
  913.    *
  914. --- 11,17 ----
  915.   */
  916.   
  917.   /*
  918. !  * $Id: io.c,v 2.9.1.1 92/12/01 09:11:51 ram Exp $
  919.    *
  920.    *  Copyright (c) 1992, Raphael Manfredi
  921.    *
  922. ***************
  923. *** 19,24 ****
  924. --- 19,27 ----
  925.    *  Licence as specified in the README file that comes with dist.
  926.    *
  927.    * $Log:    io.c,v $
  928. +  * Revision 2.9.1.1  92/12/01  09:11:51  ram
  929. +  * patch13: added extra checking for writes to soft NFS-mounted disks
  930. +  * 
  931.    * Revision 2.9  92/07/14  16:48:13  ram
  932.    * 3.0 beta baseline.
  933.    * 
  934. ***************
  935. *** 497,502 ****
  936. --- 500,506 ----
  937.       register1 char *mailptr;        /* Pointer into mail buffer */
  938.       register2 int length;            /* Number of bytes already written */
  939.       register3 int amount;            /* Amount of bytes written by last call */
  940. +     struct stat buf;                /* Stat buffer */
  941.   
  942.       sprintf(path, "%s/%s.%d", dir, template, progpid);
  943.   
  944. ***************
  945. *** 506,512 ****
  946.           return (char *) 0;
  947.       }
  948.   
  949. !     /* Write the mail on disc. We do not call a single write on the mail buffer
  950.        * as in "write(fd, mail, len)" in case the mail length exceeds the maximum
  951.        * amount of bytes the system can atomically write.
  952.        */
  953. --- 510,516 ----
  954.           return (char *) 0;
  955.       }
  956.   
  957. !     /* Write the mail on disk. We do not call a single write on the mail buffer
  958.        * as in "write(fd, mail, len)" in case the mail length exceeds the maximum
  959.        * amount of bytes the system can atomically write.
  960.        */
  961. ***************
  962. *** 524,535 ****
  963.               if (n == -1)
  964.                   add_log(1, "SYSERR write: %m (%e)");
  965.               add_log(2, "ERROR cannot write to file %s", path);
  966. -             if (-1 == unlink(path)) {
  967. -                 add_log(1, "SYSERR unlink: %m (%e)");
  968. -                 add_log(4, "WARNING leaving %s around", path);
  969. -             }
  970.               close(fd);
  971. !             return (char *) 0;
  972.           }
  973.       }
  974.   
  975. --- 528,535 ----
  976.               if (n == -1)
  977.                   add_log(1, "SYSERR write: %m (%e)");
  978.               add_log(2, "ERROR cannot write to file %s", path);
  979.               close(fd);
  980. !             goto error;                /* Remove file and report error */
  981.           }
  982.       }
  983.   
  984. ***************
  985. *** 536,542 ****
  986. --- 536,568 ----
  987.       close(fd);
  988.       add_log(19, "mail in %s", path);
  989.   
  990. +     /* I don't really trust writes through NFS soft-mounted partitions, and I
  991. +      * am also suspicious about hard-mounted ones. I could have opened the file
  992. +      * with the O_SYNC flag, but the effect on NFS is not well defined either.
  993. +      * So, let's just make sure the mail has been correctly written on the disk
  994. +      * by comparing the file size and the orginal message size. If they differ,
  995. +      * complain and return an error.
  996. +      */
  997. +     if (-1 == stat(path, &buf))        /* No entry in file system, probably */
  998. +         return (char *) 0;            /* Saving failed */
  999. +     if (buf.st_size != len) {        /* Not written entirely */
  1000. +         add_log(2, "ERROR mail truncated to %d bytes (had %d)",
  1001. +             buf.st_size, len);
  1002. +         goto error;                    /* Remove file and report error */
  1003. +     }
  1004.       return path;            /* Where mail was writen (static data) */
  1005. + error:        /* Come here when a write error has been detected */
  1006. +     if (-1 == unlink(path)) {
  1007. +         add_log(1, "SYSERR unlink: %m (%e)");
  1008. +         add_log(4, "WARNING leaving %s around", path);
  1009. +     }
  1010. +     return (char *) 0;
  1011.   }
  1012.   
  1013.   #ifndef RENAME
  1014.  
  1015. Index: agent/pl/rules.pl
  1016. Prereq: 2.9.1.2
  1017. *** agent/pl/rules.pl.old    Tue Dec  1 09:48:28 1992
  1018. --- agent/pl/rules.pl    Tue Dec  1 09:48:29 1992
  1019. ***************
  1020. *** 1,4 ****
  1021. ! ;# $Id: rules.pl,v 2.9.1.2 92/11/01 15:52:24 ram Exp $
  1022.   ;#
  1023.   ;#  Copyright (c) 1992, Raphael Manfredi
  1024.   ;#
  1025. --- 1,4 ----
  1026. ! ;# $Id: rules.pl,v 2.9.1.3 92/12/01 09:30:01 ram Exp $
  1027.   ;#
  1028.   ;#  Copyright (c) 1992, Raphael Manfredi
  1029.   ;#
  1030. ***************
  1031. *** 6,11 ****
  1032. --- 6,15 ----
  1033.   ;#  Licence as specified in the README file that comes with dist.
  1034.   ;#
  1035.   ;# $Log:    rules.pl,v $
  1036. + ;# Revision 2.9.1.3  92/12/01  09:30:01  ram
  1037. + ;# patch13: fixed mode selection pattern (no brace allowed)
  1038. + ;# patch13: now takes care of escaped ';' for layout purposes
  1039. + ;# 
  1040.   ;# Revision 2.9.1.2  92/11/01  15:52:24  ram
  1041.   ;# patch11: fixed English typo
  1042.   ;# patch11: makes sure default rules apply if no valid rules are present
  1043. ***************
  1044. *** 180,186 ****
  1045.           next unless &before($rulenum);                # Call 'before' hook
  1046.           $selnum = 0;
  1047.           $rules = $_;        # Work on a copy
  1048. !         $rules =~ s/^(.*){// && ($mode = $1);        # First "word" is the mode
  1049.           $rules =~ s/\s*(.*)}// && ($action = $1);    # Then action within {}
  1050.           $mode =~ s/\s*$//;                            # Remove trailing spaces
  1051.           print "<$mode> ";                            # Mode in which it applies
  1052. --- 184,190 ----
  1053.           next unless &before($rulenum);                # Call 'before' hook
  1054.           $selnum = 0;
  1055.           $rules = $_;        # Work on a copy
  1056. !         $rules =~ s/^([^{]*){// && ($mode = $1);    # First "word" is the mode
  1057.           $rules =~ s/\s*(.*)}// && ($action = $1);    # Then action within {}
  1058.           $mode =~ s/\s*$//;                            # Remove trailing spaces
  1059.           print "<$mode> ";                            # Mode in which it applies
  1060. ***************
  1061. *** 213,219 ****
  1062. --- 217,232 ----
  1063.               }
  1064.           }
  1065.           print "  " if $lines == 1;
  1066. +         # Split actions, but take care of escaped \; (layout purposes)
  1067. +         $action =~ s/\\\\/\02/g;            # \\ -> ^B
  1068. +         $action =~ s/\\;/\01/g;                # \; -> ^A
  1069.           @action = split(/;/, $action);
  1070. +         foreach (@action) {                    # Restore escapes by in-place edit
  1071. +             s/\01/\\;/g;                    # ^A -> \;
  1072. +             s/\02/\\\\/g;                    # ^B -> \\
  1073. +         }
  1074.           # If action is large enough, format differently (one action/line)
  1075.           if (length($action) > 60 && @action > 1) {
  1076.               print "\n\t{\n";
  1077.  
  1078. Index: agent/pl/rfc822.pl
  1079. Prereq: 2.9.1.1
  1080. *** agent/pl/rfc822.pl.old    Tue Dec  1 09:48:26 1992
  1081. --- agent/pl/rfc822.pl    Tue Dec  1 09:48:26 1992
  1082. ***************
  1083. *** 1,4 ****
  1084. ! ;# $Id: rfc822.pl,v 2.9.1.1 92/11/01 15:51:46 ram Exp $
  1085.   ;#
  1086.   ;#  Copyright (c) 1992, Raphael Manfredi
  1087.   ;#
  1088. --- 1,4 ----
  1089. ! ;# $Id: rfc822.pl,v 2.9.1.2 92/12/01 09:27:19 ram Exp $
  1090.   ;#
  1091.   ;#  Copyright (c) 1992, Raphael Manfredi
  1092.   ;#
  1093. ***************
  1094. *** 6,11 ****
  1095. --- 6,14 ----
  1096.   ;#  Licence as specified in the README file that comes with dist.
  1097.   ;#
  1098.   ;# $Log:    rfc822.pl,v $
  1099. + ;# Revision 2.9.1.2  92/12/01  09:27:19  ram
  1100. + ;# patch13: added internet info extraction out of e-mail address
  1101. + ;# 
  1102.   ;# Revision 2.9.1.1  92/11/01  15:51:46  ram
  1103.   ;# patch11: allows _ as separator in names (as in First_Last)
  1104.   ;# 
  1105. ***************
  1106. *** 63,67 ****
  1107. --- 66,93 ----
  1108.       s/.*_(\w+)/$1/;                    # Same as above (_ separation)
  1109.       tr/A-Z/a-z/;                    # And lowercase it
  1110.       $_;
  1111. + }
  1112. + # Parse an e-mail address and return a three element array:
  1113. + #   ($host, $domain, $country)
  1114. + sub internet_info {
  1115. +     local($_) = shift(@_);                # The internet address
  1116. +     local($login) = &login_name($_);    # Get the address login name
  1117. +     local($internet);                    # The internet part of the address
  1118. +     # Try with uucp form first, to detect things like eiffel!ram@inria.fr
  1119. +     # We use the login name to anchor the last '!' or the first '@' or '%'
  1120. +     ($internet) = /([^!]*)!$login/i;
  1121. +     ($internet) = /$login[@%]([\w.-]*)/i unless $internet;
  1122. +     $internet =~ tr/A-Z/a-z/;                # Always lower-cased
  1123. +     local(@parts) = split(/\./, $internet);    # Break on dots
  1124. +     if (@parts == 1) {                        # Only a host name
  1125. +         # Maybe this is a local address, maybe this is a uucp name. Assume that
  1126. +         # it is local if there is an '@' sign, as in 'ram@lyon'. Otherwise, it
  1127. +         # is a uucp name, as in 'eiffel!ram'.
  1128. +         push(@parts, 'uucp') if /!$login/;    # UUCP name
  1129. +         push(@parts, split(/\./, $mydomain)) if @parts == 1;
  1130. +     }
  1131. +     unshift(@parts, '') if @parts == 2;        # No host name
  1132. +     @parts[($#parts - 2) .. $#parts];        # ($host, $domain, $country)
  1133.   }
  1134.   
  1135.  
  1136. Index: agent/magent.SH
  1137. Prereq: 2.9.1.2
  1138. *** agent/magent.SH.old    Tue Dec  1 09:47:48 1992
  1139. --- agent/magent.SH    Tue Dec  1 09:47:49 1992
  1140. ***************
  1141. *** 22,28 ****
  1142.   # via the filter. Mine looks like this:
  1143.   #   "|exec /users/ram/mail/filter >>/users/ram/.bak 2>&1"
  1144.   
  1145. ! # $Id: magent.SH,v 2.9.1.2 92/08/26 12:41:27 ram Exp $
  1146.   #
  1147.   #  Copyright (c) 1991, 1992, Raphael Manfredi
  1148.   #
  1149. --- 22,28 ----
  1150.   # via the filter. Mine looks like this:
  1151.   #   "|exec /users/ram/mail/filter >>/users/ram/.bak 2>&1"
  1152.   
  1153. ! # $Id: magent.SH,v 2.9.1.3 92/12/01 09:14:07 ram Exp $
  1154.   #
  1155.   #  Copyright (c) 1991, 1992, Raphael Manfredi
  1156.   #
  1157. ***************
  1158. *** 30,35 ****
  1159. --- 30,39 ----
  1160.   #  Licence as specified in the README file that comes with dist.
  1161.   #
  1162.   # $Log:    magent.SH,v $
  1163. + # Revision 2.9.1.3  92/12/01  09:14:07  ram
  1164. + # patch13: hostname is now computed once and cached
  1165. + # patch13: three new .pl files are now appended
  1166. + # 
  1167.   # Revision 2.9.1.2  92/08/26  12:41:27  ram
  1168.   # patch8: better no-lock handling
  1169.   # patch8: now maintains the notion of private library directory
  1170. ***************
  1171. *** 459,465 ****
  1172.       local($_);                            # Our host name
  1173.       $_ = $hiddennet if $hiddennet ne '';
  1174.       if ($_ eq '') {
  1175. !         chop($_ = `$phostname`);        # Must fork to get hostname, grr...
  1176.           $_ .= $mydomain unless /\./;    # We want something fully qualified
  1177.       }
  1178.       $_;
  1179. --- 463,469 ----
  1180.       local($_);                            # Our host name
  1181.       $_ = $hiddennet if $hiddennet ne '';
  1182.       if ($_ eq '') {
  1183. !         $_ = &hostname;                    # Must fork to get hostname, grr...
  1184.           $_ .= $mydomain unless /\./;    # We want something fully qualified
  1185.       }
  1186.       $_;
  1187. ***************
  1188. *** 568,572 ****
  1189. --- 572,579 ----
  1190.   $grep -v '^;#' pl/mailhook.pl >>magent
  1191.   $grep -v '^;#' pl/interface.pl >>magent
  1192.   $grep -v '^;#' pl/getdate.pl >>magent
  1193. + $grep -v '^;#' pl/include.pl >>magent
  1194. + $grep -v '^;#' pl/plural.pl >>magent
  1195. + $grep -v '^;#' pl/hostname.pl >>magent
  1196.   chmod 755 magent
  1197.   $eunicefix magent
  1198.  
  1199. Index: agent/pl/parse.pl
  1200. Prereq: 2.9.1.1
  1201. *** agent/pl/parse.pl.old    Tue Dec  1 09:48:21 1992
  1202. --- agent/pl/parse.pl    Tue Dec  1 09:48:21 1992
  1203. ***************
  1204. *** 1,4 ****
  1205. ! ;# $Id: parse.pl,v 2.9.1.1 92/08/26 13:17:47 ram Exp $
  1206.   ;#
  1207.   ;#  Copyright (c) 1992, Raphael Manfredi
  1208.   ;#
  1209. --- 1,4 ----
  1210. ! ;# $Id: parse.pl,v 2.9.1.2 92/12/01 09:26:19 ram Exp $
  1211.   ;#
  1212.   ;#  Copyright (c) 1992, Raphael Manfredi
  1213.   ;#
  1214. ***************
  1215. *** 6,11 ****
  1216. --- 6,14 ----
  1217.   ;#  Licence as specified in the README file that comes with dist.
  1218.   ;#
  1219.   ;# $Log:    parse.pl,v $
  1220. + ;# Revision 2.9.1.2  92/12/01  09:26:19  ram
  1221. + ;# patch13: now also understands multiple To and Cc lines in headers
  1222. + ;# 
  1223.   ;# Revision 2.9.1.1  92/08/26  13:17:47  ram
  1224.   ;# patch8: created by extraction from analyze.pl
  1225.   ;# patch8: parsing can now be done on header only
  1226. ***************
  1227. *** 122,132 ****
  1228.       }
  1229.   
  1230.       # There is usually one Apparently-To line per address. Remove all new lines
  1231. !     # in the header line and replace them with ','.
  1232. !     $* = 1;
  1233. !     $Header{'Apparently-To'} =~ s/\n/,/g;    # Remove new-lines
  1234.       $* = 0;
  1235. -     $Header{'Apparently-To'} =~ s/,$/\n/;    # Restore last new-line
  1236.   
  1237.       # If no To: field, then maybe there is an Apparently-To: instead. If so,
  1238.       # make them identical. Otherwise, assume the mail was directed to the user.
  1239. --- 125,138 ----
  1240.       }
  1241.   
  1242.       # There is usually one Apparently-To line per address. Remove all new lines
  1243. !     # in the header line and replace them with ','. Likewise for To: and Cc:.
  1244. !     # although it is far less likely to occur.
  1245. !     local($*) = 1;
  1246. !     foreach $field ('Apparently-To', 'To', 'Cc') {
  1247. !         $Header{$field} =~ s/\n/,/g;    # Remove new-lines
  1248. !         $Header{$field} =~ s/,$/\n/;    # Restore last new-line
  1249. !     }
  1250.       $* = 0;
  1251.   
  1252.       # If no To: field, then maybe there is an Apparently-To: instead. If so,
  1253.       # make them identical. Otherwise, assume the mail was directed to the user.
  1254.  
  1255. Index: agent/pl/include.pl
  1256. *** agent/pl/include.pl.old    Tue Dec  1 09:48:11 1992
  1257. --- agent/pl/include.pl    Tue Dec  1 09:48:11 1992
  1258. ***************
  1259. *** 0 ****
  1260. --- 1,45 ----
  1261. + ;# $Id: include.pl,v 2.9.1.1 92/12/01 09:21:10 ram Exp $
  1262. + ;#
  1263. + ;#  Copyright (c) 1992, Raphael Manfredi
  1264. + ;#
  1265. + ;#  You may redistribute only under the terms of the GNU General Public
  1266. + ;#  Licence as specified in the README file that comes with dist.
  1267. + ;#
  1268. + ;# $Log:    include.pl,v $
  1269. + ;# Revision 2.9.1.1  92/12/01  09:21:10  ram
  1270. + ;# patch13: created
  1271. + ;# 
  1272. + ;# 
  1273. + # Process "include-file" requests. The file is allowed to have shell comments
  1274. + # and leading spaces are trimmed. The function returns an array, each item
  1275. + # being one of the non-comment lines found in the file.
  1276. + sub include_file {
  1277. +     local($inc) = shift(@_);    # Include request "file-name"
  1278. +     local($what) = shift(@_);    # What we are looking for (singular)
  1279. +     local(*INCLUDE);            # Local file handle
  1280. +     local($filename) = $inc =~ /^"(.*)"$/;
  1281. +     local(@result);
  1282. +     local($_);
  1283. +     # Find file using mailfilter, maildir variables if not specified with an
  1284. +     # absolute pathname (starting iwht a '/').
  1285. +     $filename = &locate_file($filename);
  1286. +     &add_log("loading ".&plural($what)." from $filename") if $loglvl > 18;
  1287. +     if ($filename ne '' && open(INCLUDE, "$filename")) {
  1288. +         while (<INCLUDE>) {
  1289. +             next if /^\s*#/;    # Skip shell comments
  1290. +             chop;
  1291. +             s/^\s+//;            # Remove leading spaces
  1292. +             push(@result, $_);
  1293. +             &add_log("loaded $what '$_'") if $loglvl > 19;
  1294. +         }
  1295. +         close INCLUDE;
  1296. +     } elsif ($filename ne '') {        # Could not open file
  1297. +         &add_log("WARNING couldn't open $filename for ".&plural($what).": $!")
  1298. +             if $loglvl > 4;
  1299. +     } else {
  1300. +         &add_log("WARNING incorrect file inclusion request: $inc")
  1301. +             if $loglvl > 4;
  1302. +     }
  1303. +     @result;        # List of non-comment lines held in file
  1304. + }
  1305.  
  1306. Index: agent/files/chkagent.sh
  1307. Prereq: 2.9
  1308. *** agent/files/chkagent.sh.old    Tue Dec  1 09:47:38 1992
  1309. --- agent/files/chkagent.sh    Tue Dec  1 09:47:38 1992
  1310. ***************
  1311. *** 5,13 ****
  1312.   #  You may redistribute only under the terms of the GNU General Public
  1313.   #  Licence as specified in the README file that comes with dist.
  1314.   #
  1315. ! # $Id: chkagent.sh,v 2.9 92/07/14 16:47:41 ram Exp $
  1316.   #
  1317.   # $Log:    chkagent.sh,v $
  1318.   # Revision 2.9  92/07/14  16:47:41  ram
  1319.   # 3.0 beta baseline.
  1320.   # 
  1321. --- 5,16 ----
  1322.   #  You may redistribute only under the terms of the GNU General Public
  1323.   #  Licence as specified in the README file that comes with dist.
  1324.   #
  1325. ! # $Id: chkagent.sh,v 2.9.1.1 92/12/01 09:10:33 ram Exp $
  1326.   #
  1327.   # $Log:    chkagent.sh,v $
  1328. + # Revision 2.9.1.1  92/12/01  09:10:33  ram
  1329. + # patch13: chkagent could report errors due to spurious matches
  1330. + # 
  1331.   # Revision 2.9  92/07/14  16:47:41  ram
  1332.   # 3.0 beta baseline.
  1333.   # 
  1334. ***************
  1335. *** 42,48 ****
  1336.   
  1337.   if test -f "$logfile"; then
  1338.       grep "$today" $logfile > $todaylog
  1339. !     egrep "$lookat" $todaylog > $output
  1340.       if test -s "$output"; then
  1341.           echo "*** Errors from logfile ($logfile):" > $report
  1342.           echo " " >> $report
  1343. --- 45,51 ----
  1344.   
  1345.   if test -f "$logfile"; then
  1346.       grep "$today" $logfile > $todaylog
  1347. !     egrep ": ($lookat)" $todaylog > $output
  1348.       if test -s "$output"; then
  1349.           echo "*** Errors from logfile ($logfile):" > $report
  1350.           echo " " >> $report
  1351.  
  1352. *** End of Patch 13 ***
  1353.  
  1354. exit 0 # Just in case...
  1355.