home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume44 / mailagent / patch13 < prev    next >
Encoding:
Internet Message Format  |  1994-09-22  |  45.4 KB

  1. From: Raphael Manfredi <ram@acri.fr>
  2. Newsgroups: comp.sources.misc
  3. Subject: v44i087:  mailagent - Flexible mail filtering and processing package, v3.0, Patch13
  4. Date: 22 Sep 1994 12:12:49 -0500
  5. Organization: Advanced Computer Research Institute, Lyon, France
  6. Sender: kent@sparky.sterling.com
  7. Approved: kent@sparky.sterling.com
  8. Message-ID: <35sduh$r4q@sparky.sterling.com>
  9. X-Md4-Signature: 54a601f5e0d0ff502086866c67008bb6
  10.  
  11. Submitted-by: Raphael Manfredi <ram@acri.fr>
  12. Posting-number: Volume 44, Issue 87
  13. Archive-name: mailagent/patch13
  14. Environment: UNIX, Perl
  15. Patch-To: mailagent: Volume 41, Issue 1-26
  16.  
  17. [The latest patch for mailagent version 3.0 is #16.]
  18.  
  19. System: mailagent version 3.0
  20. Patch #: 13
  21. Priority: MEDIUM
  22. Subject: patch #12, continued
  23. Date: Thu Sep 22 17:04:30 MET DST 1994
  24. From: Raphael Manfredi <ram@acri.fr>
  25.  
  26. Description:
  27.     See patch #12.
  28.  
  29.  
  30. Fix:    From rn, say "| patch -p -N -d DIR", where DIR is your mailagent source
  31.     directory.  Outside of rn, say "cd DIR; patch -p -N <thisarticle".
  32.     If you don't have the patch program, apply the following by hand,
  33.     or get patch (version 2.0, latest patchlevel).
  34.  
  35.     After patching:
  36.         *** DO NOTHING--INSTALL ALL PATCHES UP THROUGH #16 FIRST ***
  37.  
  38.     If patch indicates that patchlevel is the wrong version, you may need
  39.     to apply one or more previous patches, or the patch may already
  40.     have been applied.  See the patchlevel.h file to find out what has or
  41.     has not been applied.  In any event, don't continue with the patch.
  42.  
  43.     If you are missing previous patches they can be obtained from me:
  44.  
  45.         Raphael Manfredi <ram@acri.fr>
  46.  
  47.     If you send a mail message of the following form it will greatly speed
  48.     processing:
  49.  
  50.         Subject: Command
  51.         @SH mailpatch PATH mailagent 3.0 LIST
  52.                ^ note the c
  53.  
  54.     where PATH is a return path FROM ME TO YOU either in Internet notation,
  55.     or in bang notation from some well-known host, and LIST is the number
  56.     of one or more patches you need, separated by spaces, commas, and/or
  57.     hyphens.  Saying 35- says everything from 35 to the end.
  58.  
  59.     To get some more detailed instructions, send me the following mail:
  60.  
  61.         Subject: Command
  62.         @SH mailhelp PATH
  63.  
  64.  
  65. Index: patchlevel.h
  66. Prereq: 12
  67. 4c4
  68. < #define PATCHLEVEL 12
  69. ---
  70. > #define PATCHLEVEL 13
  71.  
  72. Index: agent/pl/callout.pl
  73. *** agent/pl/callout.pl.old    Thu Sep 22 16:43:06 1994
  74. --- agent/pl/callout.pl    Thu Sep 22 16:43:06 1994
  75. ***************
  76. *** 0 ****
  77. --- 1,286 ----
  78. + ;# $Id: callout.pl,v 3.0.1.1 1994/09/22 14:13:07 ram Exp $
  79. + ;#
  80. + ;#  Copyright (c) 1990-1993, Raphael Manfredi
  81. + ;#  
  82. + ;#  You may redistribute only under the terms of the Artistic License,
  83. + ;#  as specified in the README file that comes with the distribution.
  84. + ;#  You may reuse parts of this distribution only within the terms of
  85. + ;#  that same Artistic License; a copy of which may be found at the root
  86. + ;#  of the source tree for mailagent 3.0.
  87. + ;#
  88. + ;# $Log: callout.pl,v $
  89. + ;# Revision 3.0.1.1  1994/09/22  14:13:07  ram
  90. + ;# patch12: created
  91. + ;#
  92. + ;#
  93. + ;# This package implements a callout queue for a limited "at" support in
  94. + ;# mailagent commands. Since items in the callout queue can only be dispatched
  95. + ;# by another call to the mailagent command, execution at an exact time cannot
  96. + ;# be guaranteed; instead we say the action will be launched "after" a certain
  97. + ;# date.
  98. + ;#
  99. + ;# It is quite admissible, however, to schedule a periodic cron job launching
  100. + ;# a 'mailagent -q' command to actually force processing of the queue and also
  101. + ;# of the callout queue, as a side effect... This is up to you, the user, to
  102. + ;# ensure that, depending on the required accuracy for your AFTER commands.
  103. + ;#
  104. + ;# The callout queue is handled as a sorted list with a single text file:
  105. + ;#
  106. + ;#   <timestamp> <type> <filename> <command>
  107. + ;#
  108. + ;# where:
  109. + ;#  - timestamp is the time in seconds elapsed since the Epoch, after which
  110. + ;#    the job should be launched.
  111. + ;#  - type is either 'agent' or 'shell' depending on whether the command is
  112. + ;#    a shell command or a mailagent filtering command (which can be a possible
  113. + ;#    mailagent call back to a perl routine via DO).
  114. + ;#  - command is the command to be run. Everything up to the new line.
  115. + ;#
  116. + ;# When loaded in memory, the callout queue is held in three hash tables:
  117. + ;#  %Calltype: associates a timestamp to a list of ^@-separated types
  118. + ;#  %Callout: associates a timestamp to a list of ^@-separated actions
  119. + ;#  %Callfile: associates a timestamp with a list of file names
  120. + ;# This separation by means of ^@ is necessary since more than one event may
  121. + ;# be associated to a single point in time.
  122. + ;#
  123. + package callout;
  124. + #
  125. + # Callout queue handling
  126. + #
  127. + # Init constants -- must be called after mailagent context was loaded
  128. + sub init {
  129. +     $AGENT = 'agent';        # Action is a mailagent command
  130. +     $SHELL = 'shell';        # Action is a standalone shell command
  131. +     $CMD = 'cmd';            # Action is a shell command on a mail message
  132. +     $first_callout = &context'get('next-callout');    # undef if not there
  133. +     $callout_changed = 0;    # Records changes in callout queue
  134. + }
  135. + # Load callout queue file into memory. Before exiting, mailagent will flush
  136. + # it again to the disk if it has been modified in some way. It is not an error
  137. + # for the file not to exist: it means the callout queue has been emptied.
  138. + sub load {
  139. +     unless (open(CALLOUT, $cf'callout)) {
  140. +         &'add_log("WARNING unable to open callout queue file: $!")
  141. +             if -f $cf'callout && $'loglvl > 5;
  142. +         return;
  143. +     }
  144. +     &'add_log("loading mailagent callout queue") if $'loglvl > 15;
  145. +     local($_, $.);
  146. +     while (<CALLOUT>) {
  147. +         next if /^\s*#/;
  148. +         if (/^(\d+)\s+(\w+)\s+(\S+)\s+(.*)/) {
  149. +             $Calltype{$1} .= "$2\0";
  150. +             $Callfile{$1} .= "$3\0";
  151. +             $Callout{$1} .= "$4\0";
  152. +             next;
  153. +         }
  154. +         &'add_log("WARNING callout queue corrupted, line $.") if $'loglvl > 5;
  155. +         last;
  156. +     }
  157. +     close CALLOUT;
  158. +     return unless defined %Callout;        # Nothing loaded, empty file...
  159. +     local($next_callout) = (sort keys %Callout)[0];
  160. +     if ($next_callout != $first_callout) {
  161. +         &'add_log(
  162. +             "NOTICE next-callout is $first_callout, should be $next_callout"
  163. +         ) if $'loglvl > 6;
  164. +         &'add_log("WARNING inconsistency in mailagent context (next-callout)")
  165. +             if $'loglvl > 5;
  166. +     }
  167. +     $first_callout = $next_callout;        # Trust callout queue over context
  168. + }
  169. + # Enqueue a new job to be performed after a certain time. If the job is to be
  170. + # launched before the first one in the queue, the next-callout value in the
  171. + # mailagent context is updated.
  172. + # Return the queued file name, or '-' if none, undef on errors.
  173. + sub queue {
  174. +     local($time, $action, $type, $no_input) = @_;
  175. +     $callout_changed++;
  176. +     &load unless defined %Callout;
  177. +     local($qname) = '-';            # File not queued by default
  178. +     if ($type ne $SHELL && !$no_input) {
  179. +         # 'agent' or 'cmd' callouts have input by default, unless $no_input
  180. +         # is specified in the arguments.
  181. +         local(@mail);                # Temporary mail storage
  182. +         @mail = split(/\n/, $'Header{'All'});
  183. +         $qname = &'qmail(*mail, 'cm');
  184. +         unless (defined $qname) {
  185. +             &'add_log("ERROR cannot record $type callout $action for $time")
  186. +                 if $'loglvl > 1;
  187. +             return undef;
  188. +         }
  189. +     }
  190. +     $Callfile{$time} .= "$qname\0";    # Add queue name to the list
  191. +     $Calltype{$time} .= "$type\0";    # Add type to the list
  192. +     $Callout{$time} .= "$action\0";    # Add action at this time stamp
  193. +     $first_callout = $time if $time < $first_callout;
  194. +     return $qname;
  195. + }
  196. + # Return trigger time for a callout, based on its file name. This is primarily
  197. + # used to list the callout queue. If no callout is found, returns 0.
  198. + sub trigger {
  199. +     local($file) = @_;
  200. +     local($directory, $base) = $file =~ m|(.*)/(.*)|;
  201. +     $file = $directory eq $cf'queue ? $base : $file;
  202. +     &load unless defined %Callout;
  203. +     local($time, $files);
  204. +     foreach $time (keys %Callfile) {
  205. +         $files = $Callfile{$time};
  206. +         next unless "\0$files" =~ /\0$file\0/;
  207. +         return $time;
  208. +     }
  209. +     return 0;
  210. + }
  211. + # Run the queue, by poping off the first set in the queue, and executing
  212. + # it. If by that time another timeout expires, loop again.
  213. + sub run {
  214. +     &'add_log("running callout queue") if $'loglvl > 15;
  215. +     $callout_changed++;
  216. +     &load unless defined %Callout;
  217. +     local(@type, @action, @file);
  218. +     local($type, $action, $file);
  219. +     do {
  220. +         chop($type = $Calltype{$first_callout});    # Remove trailing \0
  221. +         chop($action = $Callout{$first_callout});
  222. +         chop($file = $Callfile{$first_callout});
  223. +         @type = split(/\0/, $type);
  224. +         @action = split(/\0/, $action);
  225. +         @file = split(/\0/, $file);
  226. +         while ($type = shift(@type)) {
  227. +             $action = shift(@action);
  228. +             $file = shift(@file);
  229. +             &spawn($type, $file, $action);        # Spawn callout action
  230. +         }
  231. +         delete $Calltype{$first_callout};
  232. +         delete $Callout{$first_callout};
  233. +         delete $Callfile{$first_callout};
  234. +         $first_callout = (sort keys %Callout)[0];
  235. +     } while (time >= $first_callout);
  236. + }
  237. + # Flush the callout queue to the disk. This operation launches the commands
  238. + # that have expired, then rewrites a new callout queue file to the disk if
  239. + # required. When all the jobs from the queue have been run, the callout file
  240. + # is removed and the next-callout value is deleted from the context.
  241. + # NOTE: this is called by &main'contextual_operations in pl/context.pl, before
  242. + # the new mailagent context is actually saved to the disk. Therefore, we are
  243. + # able to update next-callout for the next mailagent run.
  244. + sub flush {
  245. +     return unless defined $first_callout;
  246. +     &run if time >= $first_callout;        # Run queue if time reached
  247. +     return unless $callout_changed;        # Done if no change since &init
  248. +     &save;
  249. +     &context'set('next-callout', $first_callout);
  250. + }
  251. + # Save the callout queue on disk. If the %Callout table is empty, the
  252. + # callout file is removed.
  253. + sub save {
  254. +     local($count) = scalar(keys %Callout);
  255. +     unless ($count) {
  256. +         &'add_log("removing mailagent callout queue") if $'loglvl > 15;
  257. +         unlink($cf'callout);
  258. +         return;
  259. +     }
  260. +     &'add_log("saving $count entries in callout queue") if $'loglvl > 15;
  261. +     local($existed) = -f $cf'callout;
  262. +     &'acs_rqst($cf'callout) if $existed;    # Lock existing file
  263. +     unless (open(CALLOUT, ">$cf'callout")) {
  264. +         &'add_log("ERROR cannot overwrite callout queue $cf'callout: $!")
  265. +             if $'loglvl > 1;
  266. +         &'free_file($cf'callout) if $existed;
  267. +         return;
  268. +     }
  269. +     require 'ctime.pl';
  270. +     print CALLOUT "# Mailagent callout queue, last updated " . &'ctime(time);
  271. +     local(@type, @action, @file);
  272. +     local($type, $action, $file);
  273. +     # De-compile callout data structure back into a human-readable table
  274. +     foreach $time (sort keys %Callout) {
  275. +         chop($type = $Calltype{$time});        # Remove trailing \0
  276. +         chop($action = $Callout{$time});
  277. +         chop($file = $Callfile{$time});
  278. +         @type = split(/\0/, $type);            # Type and action lists per time
  279. +         @action = split(/\0/, $action);
  280. +         @file = split(/\0/, $file);
  281. +         while ($type = shift(@type)) {
  282. +             $action = shift(@action);
  283. +             $file = shift(@file);
  284. +             print CALLOUT "$time\t$type\t$file\t$action\n";
  285. +         }
  286. +     }
  287. +     close CALLOUT;
  288. +     &'free_file($cf'callout) if $existed;
  289. + }
  290. + #
  291. + # Spawning engine
  292. + #
  293. + # Spawn callout action given its type, and the mail file on which the action
  294. + # takes place. If the file name is '-', then no input, but only for shell
  295. + # commands.
  296. + sub spawn {
  297. +     local($type, $action, $file) = @_;
  298. +     local($sub) = 'spawn_' . $type;
  299. +     local($file_name) = $file;        # Where mail is held (within queue usually)
  300. +     local(%Header);                    # Where filtering information is stored
  301. +     # File name is absolute if not within mailagent's queue, otherwise it
  302. +     # is only a relative path name, as returned by &qmail. Shell commands
  303. +     # specify '-', meaning no input is to be taken.
  304. +     $file_name = $cf'queue . '/' . $file_name unless $file_name =~ m|^/|;
  305. +     if (defined &$sub) {
  306. +         &'add_log("setting up mailagent data structures for $file")
  307. +             if $'loglvl > 15;
  308. +         &'parse_mail($file_name) if $file ne '-';    # Fill in %Header
  309. +         &'add_log("spawning callout $type type on $file: $action")
  310. +             if $'loglvl > 15;
  311. +         local($failed);
  312. +         $failed = &$sub($action);        # Invoke call-out action
  313. +         $failed = 'FAILED' if $failed;
  314. +         &'add_log("${failed}CALLOUT ($type) [$file] $action") if $'loglvl > 7;
  315. +     } else {
  316. +         &'add_log("ERROR unknown callout type $type -- skipping $action")
  317. +             if $'loglvl;
  318. +     }
  319. +     unlink $file_name unless $file eq '-';
  320. + }
  321. + # Spawn filtering command
  322. + sub spawn_agent {
  323. +     local($action) = @_;
  324. +     local($mode) = '_CALLOUT_';    # Initial working mode
  325. +     local($wmode) = $mode;        # Needed for statistics routines
  326. +     umask($cf'umask);            # Reset default umask
  327. +     &'xeqte($action);            # Run action
  328. +     umask($cf'umask);            # Reset umask anyway
  329. +     return 0;
  330. + }
  331. + # Spawn command-on-mail, i.e. shell command with mail on stdin
  332. + sub spawn_cmd {
  333. +     local($action) = @_;
  334. +     return &'shell_command($action, $'MAIL_INPUT, $'NO_FEEDBACK);
  335. + }
  336. + # Spawn shell command
  337. + sub spawn_shell {
  338. +     local($action) = @_;
  339. +     return &'shell_command($action, $'NO_INPUT, $'NO_FEEDBACK);
  340. + }
  341. + package main;
  342.  
  343. Index: agent/pl/actions.pl
  344. Prereq: 3.0.1.3
  345. *** agent/pl/actions.pl.old    Thu Sep 22 16:43:02 1994
  346. --- agent/pl/actions.pl    Thu Sep 22 16:43:03 1994
  347. ***************
  348. *** 1,4 ****
  349. ! ;# $Id: actions.pl,v 3.0.1.3 1994/07/01 14:57:49 ram Exp $
  350.   ;#
  351.   ;#  Copyright (c) 1990-1993, Raphael Manfredi
  352.   ;#  
  353. --- 1,4 ----
  354. ! ;# $Id: actions.pl,v 3.0.1.4 1994/09/22 14:07:26 ram Exp $
  355.   ;#
  356.   ;#  Copyright (c) 1990-1993, Raphael Manfredi
  357.   ;#  
  358. ***************
  359. *** 9,14 ****
  360. --- 9,21 ----
  361.   ;#  of the source tree for mailagent 3.0.
  362.   ;#
  363.   ;# $Log: actions.pl,v $
  364. + ;# Revision 3.0.1.4  1994/09/22  14:07:26  ram
  365. + ;# patch12: now updates new variable folder_saved with folder path
  366. + ;# patch12: added various escapes in strings for perl5 support
  367. + ;# patch12: create ~/agent.trace if unable to mail command trace back
  368. + ;# patch12: interface change for &qmail allows for better log messages
  369. + ;# patch12: implements new AFTER and DO filtering commands
  370. + ;#
  371.   ;# Revision 3.0.1.3  1994/07/01  14:57:49  ram
  372.   ;# patch8: timeout for RUN commands now defined by runmax config variable
  373.   ;# patch8: now systematically escape leading From if fromall is ON
  374. ***************
  375. *** 116,125 ****
  376.           }
  377.           $failed = 1;
  378.       }
  379. !     $failed;        # Propagate failure status
  380.   }
  381.   
  382.   # Called by &save when folder is a hook.
  383.   # Return command failure status.
  384.   sub save_hook {
  385.       local($failed) = &hook'process($mailbox);
  386. --- 123,137 ----
  387.           }
  388.           $failed = 1;
  389.       }
  390. !     $folder_saved = $mailbox;    # Keep track of last folder we save into
  391. !     $failed;                    # Propagate failure status
  392.   }
  393.   
  394.   # Called by &save when folder is a hook.
  395. + # Note that as opposed to other folder saving routines, we do not update the
  396. + # $folder_saved variable when saving into a hook. This is because the hook
  397. + # might be another set of filtering rules or a perl escape taking care of its
  398. + # own saving, in which case we do not want to corrupt the saved location.
  399.   # Return command failure status.
  400.   sub save_hook {
  401.       local($failed) = &hook'process($mailbox);
  402. ***************
  403. *** 152,158 ****
  404.       $dest =~ /<(.*)>/ && ($dest = $1);
  405.   
  406.       # Debugging purposes
  407. !     &add_log("@PATH was '$Userpath' and sender was '$sender'") if $loglvl > 18;
  408.       &add_log("computed destination: $dest") if $loglvl > 15;
  409.   
  410.       # Copy body of message in an array, one line per entry
  411. --- 164,171 ----
  412.       $dest =~ /<(.*)>/ && ($dest = $1);
  413.   
  414.       # Debugging purposes
  415. !     &add_log("\@PATH was '$Userpath' and sender was '$sender'")
  416. !         if $loglvl > 18;
  417.       &add_log("computed destination: $dest") if $loglvl > 15;
  418.   
  419.       # Copy body of message in an array, one line per entry
  420. ***************
  421. *** 170,191 ****
  422.   
  423.       line: foreach (@body) {
  424.           # Built-in commands
  425. !         if (/^@PACK\s*(.*)/) {        # Pack mode
  426.               $pack = $1 if $1 ne '';
  427.               $pack = "" if ($pack =~ /[=$^&*([{}`\\|;><?]/);
  428.           }
  429. !         s/^[ \t]@SH/@SH/;    # allow one blank only
  430. !         if (/^@SH/) {
  431.               s/\\!/!/g;        # if uucp address, un-escape `!'
  432.               if (/[=\$^&*([{}`\\|;><?]/) {
  433. !                 s/^@SH/bad command:/;    # space after ":" will be added
  434.                   $bad .= $_ . "\n";
  435.                   next line;
  436.               }
  437.               # Some useful substitutions
  438. !             s/@SH[ \t]*//;                # Allow leading blanks
  439.               s/ PATH/ $dest/;             # PATH is a macro
  440. !             s/^mial(\w*)/mail\1/;        # Common mis-spellings
  441.               s/^mailpath/mailpatch/;
  442.               s/^mailist/maillist/;
  443.               # Now fetch command's name (first symbol)
  444. --- 183,204 ----
  445.   
  446.       line: foreach (@body) {
  447.           # Built-in commands
  448. !         if (/^\@PACK\s*(.*)/) {        # Pack mode
  449.               $pack = $1 if $1 ne '';
  450.               $pack = "" if ($pack =~ /[=$^&*([{}`\\|;><?]/);
  451.           }
  452. !         s/^[ \t]\@SH/\@SH/;    # allow one blank only
  453. !         if (/^\@SH/) {
  454.               s/\\!/!/g;        # if uucp address, un-escape `!'
  455.               if (/[=\$^&*([{}`\\|;><?]/) {
  456. !                 s/^\@SH/bad command:/;    # space after ":" will be added
  457.                   $bad .= $_ . "\n";
  458.                   next line;
  459.               }
  460.               # Some useful substitutions
  461. !             s/\@SH[ \t]*//;                # Allow leading blanks
  462.               s/ PATH/ $dest/;             # PATH is a macro
  463. !             s/^mial(\w*)/mail$1/;        # Common mis-spellings
  464.               s/^mailpath/mailpatch/;
  465.               s/^mailist/maillist/;
  466.               # Now fetch command's name (first symbol)
  467. ***************
  468. *** 282,288 ****
  469.   It produced the following output and failed:
  470.   
  471.   EOM
  472. !                 if (open(TRACE, "$trace")) {
  473.                       while (<TRACE>) {
  474.                           print MAILER;
  475.                       }
  476. --- 295,301 ----
  477.   It produced the following output and failed:
  478.   
  479.   EOM
  480. !                 if (open(TRACE, $trace)) {
  481.                       while (<TRACE>) {
  482.                           print MAILER;
  483.                       }
  484. ***************
  485. *** 295,300 ****
  486. --- 308,324 ----
  487.                   close MAILER;
  488.                   if ($?) {
  489.                       &add_log("ERROR cannot report failure") if $loglvl;
  490. +                     # Dump trace in ~/agent.trace
  491. +                     local($ok) = 1;
  492. +                     open(DUMP, ">>$cf'home/agent.trace") || ($ok = 0);
  493. +                     print DUMP "--- Trace for failed $fullcmd ---\n";
  494. +                     print DUMP "--- (was unable to mail it back) ---\n";
  495. +                     open(TRACE, $trace) || ($ok = 0);
  496. +                     while (<TRACE>) { print DUMP; }
  497. +                     print DUMP "--- End of trace for $fullcmd ---\n";
  498. +                     close DUMP;
  499. +                     &add_log("DUMPED trace in ~/agent.trace")
  500. +                         if $ok && $loglvl > 2;
  501.                   }
  502.                   &add_log("FAILED $fullcmd") if $loglvl > 1;
  503.               } else {
  504. ***************
  505. *** 1299,1304 ****
  506. --- 1323,1397 ----
  507.       0;        # Success
  508.   }
  509.   
  510. + # The "DO" command
  511. + # The routine name can be one of pack'routine, COMMAND:pack'routine or
  512. + # /some/path:pack'routine. The following parsing duplicates the one done
  513. + # in &dynload'do, so beware, should the interface change.
  514. + sub do {
  515. +     local($something, $routine, $args) = @_;
  516. +     $routine = $what if $something eq '';
  517. +     unless (&dynload'do($what)) {
  518. +         local($under);
  519. +         $under = " under $something" if $something ne '';
  520. +         &add_log("ERROR couldn't locate routine $routine$under") if $loglvl > 1;
  521. +         return 1;    # Failed
  522. +     }
  523. +     $args = '()' unless $args;
  524. +     &add_log("calling routine $routine$args") if $loglvl > 15;
  525. +     eval "package main; &$routine$args;";
  526. +     # I want to allow people to call mailhook commands from a DO routine call.
  527. +     # However, commands modifying the filtering control flow are performing a
  528. +     # die() with 'Status x' as the error message where 'x' defines the new
  529. +     # continuation value for run_command. This is trapped specially here.
  530. +     # Note however that convenience variables typically set for PERL escapes
  531. +     # are not available via a DO.
  532. +     if (chop($@)) {
  533. +         local($_) = $@;
  534. +         $@ = '';                # Avoid cascades: we're within an eval already
  535. +         if (/^Status (\d+)$/) {    # Filter automaton continuation status
  536. +             $cont = $1;            # Propagate status ($cont from &run_command)
  537. +             &add_log("NOTICE $routine shifted automaton to status $cont")
  538. +                 if $loglvl > 1;
  539. +         } else {
  540. +             &add_log("ERROR cannot call $routine$args: $_") if $loglvl > 1;
  541. +             return 1;
  542. +         }
  543. +     }
  544. +     0;        # Success
  545. + }
  546. + # The "AFTER" command
  547. + sub after {
  548. +     local($time, $action, $type) = @_;
  549. +     local($no_input) = $type =~ /n/;
  550. +     local($agent_cmd) = $type =~ /a/ || $type eq '';
  551. +     local($shell_cmd) = $type =~ /s/;
  552. +     local($cmd_cmd) = $type =~ /c/;
  553. +     local($now) = time;                    # Current time
  554. +     local($start);                        # Action's starting time
  555. +     $start = &getdate($time, $now);
  556. +     if ($start == -1) {
  557. +         &add_log("ERROR in AFTER: time '$time' is incorrect") if $loglvl > 1;
  558. +         return (1,undef);
  559. +     }
  560. +     if ($start < $now) {
  561. +         &add_log("NOTICE time '$time' ($start) is before now ($now)")
  562. +             if $loglvl > 5;
  563. +         &add_log("ERROR in AFTER: command should have run already!")
  564. +             if $loglvl > 1;
  565. +         return (1,undef);
  566. +     }
  567. +     local($atype) = $agent_cmd ? $callout'AGENT :
  568. +         ($shell_cmd ? $callout'SHELL : $callout'CMD);
  569. +     local($qfile) = &callout'queue($start, $action, $atype, $no_input);
  570. +     unless (defined $qfile) {
  571. +         &add_log("ERROR in AFTER: cannot queue action $action") if $loglvl > 1;
  572. +         return (1,undef);
  573. +     }
  574. +     (0, $qfile);        # Success
  575. + }
  576.   
  577.   # Modify control flow within automaton by calling a non-existant function
  578.   # &perform, which has been dynamically bound to one of the do_* functions.
  579. ***************
  580. *** 1393,1400 ****
  581.       }
  582.       # Now @array holds the whole digest item
  583.       if ($folder =~ /^\s*$/) {        # No folder means we have to queue message
  584. !         $failed = &qmail(*array);
  585. !         $log_message = 'mailagent\'s queue';
  586.           foreach (@array) {
  587.               $length += length($_) + 1;    # No trailing new-lines
  588.           }
  589. --- 1486,1494 ----
  590.       }
  591.       # Now @array holds the whole digest item
  592.       if ($folder =~ /^\s*$/) {        # No folder means we have to queue message
  593. !         local($name) = &qmail(*array);
  594. !         $failed = defined $name ? 0 : 1;
  595. !         $log_message = $name =~ m|/| ? "file [$name]" : "queue [$name]";
  596.           foreach (@array) {
  597.               $length += length($_) + 1;    # No trailing new-lines
  598.           }
  599.  
  600. Index: agent/pl/lexical.pl
  601. Prereq: 3.0
  602. *** agent/pl/lexical.pl.old    Thu Sep 22 16:43:14 1994
  603. --- agent/pl/lexical.pl    Thu Sep 22 16:43:14 1994
  604. ***************
  605. *** 1,4 ****
  606. ! ;# $Id: lexical.pl,v 3.0 1993/11/29 13:48:55 ram Exp $
  607.   ;#
  608.   ;#  Copyright (c) 1990-1993, Raphael Manfredi
  609.   ;#  
  610. --- 1,4 ----
  611. ! ;# $Id: lexical.pl,v 3.0.1.1 1994/09/22 14:24:44 ram Exp $
  612.   ;#
  613.   ;#  Copyright (c) 1990-1993, Raphael Manfredi
  614.   ;#  
  615. ***************
  616. *** 9,14 ****
  617. --- 9,18 ----
  618.   ;#  of the source tree for mailagent 3.0.
  619.   ;#
  620.   ;# $Log: lexical.pl,v $
  621. + ;# Revision 3.0.1.1  1994/09/22  14:24:44  ram
  622. + ;# patch12: added logging at level 25 to debug lexer
  623. + ;# patch12: better mismatched braces handling
  624. + ;#
  625.   ;# Revision 3.0  1993/11/29  13:48:55  ram
  626.   ;# Baseline for mailagent 3.0 netwide release.
  627.   ;#
  628. ***************
  629. *** 33,42 ****
  630. --- 37,48 ----
  631.   # Assemble a whole rule in one line and return it. The end of a line is
  632.   # marked by a ';' at the end of an input line.
  633.   sub get_line {
  634. +     &add_log("IN get_line") if $loglvl > 24;
  635.       local($result) = "";        # what will be returned
  636.       local($in_braces) = 0;        # are we inside braces ?
  637.       for (;;) {
  638.           $_ = &read_rule;        # new rule line (pseudo from compile_rules)
  639. +         &add_log("READ <<$_>>") if $loglvl > 24;
  640.           last if $_ eq '';        # end of file reached
  641.           s/\n$//;                # don't use chop in case we read from array
  642.           next if /^\s*#/;        # skip comments
  643. ***************
  644. *** 44,72 ****
  645.           s/\s\s+/ /;                # reduce white spaces
  646.           $result .= $_;
  647.           # Very simple braces handling
  648. !         /.*{/ && ($in_braces = 1);
  649. !         if ($in_braces) {
  650. !             /.*}/ && ($in_braces = 0);
  651. !         }
  652. !         last if !$in_braces && /;\s*$/;
  653.       }
  654.       $result;
  655.   }
  656.   
  657.   # Get optional mode (e.g. <TEST>) at the beginning of the line and return
  658.   # it, or ALL if none was present. A mode can be negated by pre-pending a '!'.
  659.   sub get_mode {
  660.       local(*line) = shift(@_);    # edited in place
  661.       local($_) = $line;            # make a copy of original
  662.       local($mode) = "ALL";        # default mode
  663.       s/^\s*<([\s\w,!]+)>// && ($mode = $1);
  664.       $mode =~ s/\s//g;            # no spaces in returned mode
  665.       $line = $_;                    # eventually updates the line
  666.       $mode;
  667.   }
  668.   
  669.   # A selector is either a script or a list of header fields ending with a ':'.
  670.   sub get_selector {
  671.       local(*line) = shift(@_);    # edited in place
  672.       local($_) = $line;            # make a copy of original
  673.       local($selector) = "";
  674. --- 50,79 ----
  675.           s/\s\s+/ /;                # reduce white spaces
  676.           $result .= $_;
  677.           # Very simple braces handling
  678. !         $in_braces += tr/{/{/ - tr/}/}/;
  679. !         last if $in_braces <= 0 && /;\s*$/;
  680.       }
  681. +     &add_log("OUT get_line: $result") if $loglvl > 24;
  682.       $result;
  683.   }
  684.   
  685.   # Get optional mode (e.g. <TEST>) at the beginning of the line and return
  686.   # it, or ALL if none was present. A mode can be negated by pre-pending a '!'.
  687.   sub get_mode {
  688. +     &add_log("IN get_mode") if $loglvl > 24;
  689.       local(*line) = shift(@_);    # edited in place
  690.       local($_) = $line;            # make a copy of original
  691.       local($mode) = "ALL";        # default mode
  692.       s/^\s*<([\s\w,!]+)>// && ($mode = $1);
  693.       $mode =~ s/\s//g;            # no spaces in returned mode
  694.       $line = $_;                    # eventually updates the line
  695. +     &add_log("OUT get_mode: $mode") if $loglvl > 24;
  696.       $mode;
  697.   }
  698.   
  699.   # A selector is either a script or a list of header fields ending with a ':'.
  700.   sub get_selector {
  701. +     &add_log("IN get_selector") if $loglvl > 24;
  702.       local(*line) = shift(@_);    # edited in place
  703.       local($_) = $line;            # make a copy of original
  704.       local($selector) = "";
  705. ***************
  706. *** 77,82 ****
  707. --- 84,90 ----
  708.           s/^\s*([^\/,{\n]*(<[\d\s,-]+>)?\s*:)// && ($selector = $1);
  709.       }
  710.       $line = $_;                    # eventually updates the line
  711. +     &add_log("OUT get_selector: $selector") if $loglvl > 24;
  712.       $selector;
  713.   }
  714.   
  715. ***************
  716. *** 85,90 ****
  717. --- 93,99 ----
  718.   # modifiers.
  719.   # Patterns may be preceded by a single '!' to negate the matching value.
  720.   sub get_pattern {
  721. +     &add_log("IN get_pattern") if $loglvl > 24;
  722.       local(*line) = shift(@_);        # edited in place
  723.       local($_) = $line;                # make a copy of original
  724.       local($pattern) = "";            # the recognized pattern
  725. ***************
  726. *** 112,131 ****
  727.       } else {
  728.           $pattern = $not . $pattern;
  729.       }
  730.       $pattern;
  731.   }
  732.   
  733.   # Extract the action part from the line (by editing it in place) and return
  734.   # the first action encountered. Nesting of {...} blocks may occur.
  735.   sub get_action {
  736.       local(*line) = shift(@_);    # edited in place
  737.       local($_) = $line;            # make a copy of original
  738. !     return '' unless s/^\s*\{/{/;
  739.       local($action) = &action_parse(*_, 0);
  740.       &add_log("ERROR no action, discarding '$_'") if $loglvl && $action eq '';
  741.       $line = $_;                    # eventually update the line
  742.       $action =~ s/^\{\s*//;        # remove leading and trailing braces
  743.       $action =~ s/\s*\}$//;
  744.       $action;                    # return new action block
  745.   }
  746.   
  747. --- 121,146 ----
  748.       } else {
  749.           $pattern = $not . $pattern;
  750.       }
  751. +     &add_log("OUT get_pattern: $pattern") if $loglvl > 24;
  752.       $pattern;
  753.   }
  754.   
  755.   # Extract the action part from the line (by editing it in place) and return
  756.   # the first action encountered. Nesting of {...} blocks may occur.
  757.   sub get_action {
  758. +     &add_log("IN get_action") if $loglvl > 24;
  759.       local(*line) = shift(@_);    # edited in place
  760.       local($_) = $line;            # make a copy of original
  761. !     unless (s/^\s*\{/{/) {
  762. !         &add_log("OUT get_action (none)") if $loglvl > 24;
  763. !         return '';
  764. !     }
  765.       local($action) = &action_parse(*_, 0);
  766.       &add_log("ERROR no action, discarding '$_'") if $loglvl && $action eq '';
  767.       $line = $_;                    # eventually update the line
  768.       $action =~ s/^\{\s*//;        # remove leading and trailing braces
  769.       $action =~ s/\s*\}$//;
  770. +     &add_log("OUT get_action: $action") if $loglvl > 24;
  771.       $action;                    # return new action block
  772.   }
  773.   
  774. ***************
  775. *** 135,143 ****
  776. --- 150,160 ----
  777.   sub action_parse {
  778.       local(*_) = shift(@_);        # edited in place
  779.       local($level) = shift(@_);    # recursion level
  780. +     &add_log("IN action_parse $level: $_") if $loglvl > 24;
  781.       local($parsed) = '';        # the part we parsed so far
  782.       local($block);                # block recognized
  783.       local($follow);                # recursion string returned
  784.       for (;;) {
  785.           # Go to first un-escaped '{', if possible and save leading string
  786.           # up-to first '{'. Note that any '}' immediately stops scanning.
  787. ***************
  788. *** 146,160 ****
  789.           $block = '';
  790.           s/^(([^\\{}]|\\.)*\})// && ($block = $1);
  791.           $parsed .= $block;        # block may be empty, or has trailing '}'
  792.           if ($parsed =~ s/\{$//) {    # recursion if '{' found
  793.               $follow = &action_parse(*_, $level + 1);
  794.               # If a null string is returned, then no matching '}' was found
  795.               &add_log("WARNING no closing brace (added for you)")
  796.                   if $follow eq '' && $loglvl > 5;
  797.               $parsed .= '{' . $follow . '}';
  798. !         } elsif (s/^\}//) {            # reached end of a block
  799.               &add_log("WARNING extra closing brace ignored")
  800.                   if $level == 0 && $loglvl > 5;
  801.               return $parsed;
  802.           } else {
  803.               # Get the whole string until the next '}' and return. If a '{'
  804. --- 163,179 ----
  805.           $block = '';
  806.           s/^(([^\\{}]|\\.)*\})// && ($block = $1);
  807.           $parsed .= $block;        # block may be empty, or has trailing '}'
  808. +         &add_log("action_parse $level: $parsed") if $loglvl > 24;
  809.           if ($parsed =~ s/\{$//) {    # recursion if '{' found
  810.               $follow = &action_parse(*_, $level + 1);
  811.               # If a null string is returned, then no matching '}' was found
  812.               &add_log("WARNING no closing brace (added for you)")
  813.                   if $follow eq '' && $loglvl > 5;
  814.               $parsed .= '{' . $follow . '}';
  815. !         } elsif (s/^\}//) {        # reached end of a block
  816.               &add_log("WARNING extra closing brace ignored")
  817.                   if $level == 0 && $loglvl > 5;
  818. +             &add_log("OUT action_parse $level: $parsed") if $loglvl > 24;
  819.               return $parsed;
  820.           } else {
  821.               # Get the whole string until the next '}' and return. If a '{'
  822. ***************
  823. *** 166,176 ****
  824. --- 185,201 ----
  825.               if ($block eq '' && $level) {        # Advance until '{'
  826.                   s/^(([^\\}]|\\.)*\{)// && ($block = $1);
  827.                   $parsed .= $block;
  828. +                 last if $block eq '';    # Reached the end... prematurely!
  829.                   next;
  830.               }
  831.               $block =~ s/\}//;
  832. +             &add_log("OUT action_parse $level: $parsed$block") if $loglvl > 24;
  833.               return $parsed . $block;
  834.           }
  835.       }
  836. +     &add_log("WARNING mismatched braces in rule file") if $loglvl > 5;
  837. +     &add_log("OUT action_parse $level: $parsed <EOF>") if $loglvl > 24;
  838. +     return $parsed;
  839.   }
  840.   
  841.  
  842. Index: agent/pl/queue_mail.pl
  843. Prereq: 3.0
  844. *** agent/pl/queue_mail.pl.old    Thu Sep 22 16:43:20 1994
  845. --- agent/pl/queue_mail.pl    Thu Sep 22 16:43:20 1994
  846. ***************
  847. *** 1,4 ****
  848. ! ;# $Id: queue_mail.pl,v 3.0 1993/11/29 13:49:11 ram Exp $
  849.   ;#
  850.   ;#  Copyright (c) 1990-1993, Raphael Manfredi
  851.   ;#  
  852. --- 1,4 ----
  853. ! ;# $Id: queue_mail.pl,v 3.0.1.1 1994/09/22 14:34:16 ram Exp $
  854.   ;#
  855.   ;#  Copyright (c) 1990-1993, Raphael Manfredi
  856.   ;#  
  857. ***************
  858. *** 9,14 ****
  859. --- 9,17 ----
  860.   ;#  of the source tree for mailagent 3.0.
  861.   ;#
  862.   ;# $Log: queue_mail.pl,v $
  863. + ;# Revision 3.0.1.1  1994/09/22  14:34:16  ram
  864. + ;# patch12: changed interface of &qmail and &queue_mail for wider usage
  865. + ;#
  866.   ;# Revision 3.0  1993/11/29  13:49:11  ram
  867.   ;# Baseline for mailagent 3.0 netwide release.
  868.   ;#
  869. ***************
  870. *** 16,29 ****
  871.   ;# Queue a mail file. Needs add_log(). Calls fatal() in emergency situations.
  872.   ;# Requires a parsed config file.
  873.   ;# 
  874. ! # Queue mail in a 'fm' file. The mail is held in memory. It returns 0 if the
  875. ! # mail was queued, 1 otherwise.
  876.   sub qmail {
  877. !     local(*array) = @_;            # In which array mail is located.
  878.       local($queue_file);            # Where we attempt to save the mail
  879.       local($failed) = 0;            # Be positive and look forward :-)
  880. !     $queue_file = "$cf'queue/Tqm$$";
  881. !     $queue_file = "$cf'queue/Tqmb$$" if -f "$queue_file";    # Paranoid
  882.       unless (open(QUEUE, ">$queue_file")) {
  883.           &add_log("ERROR unable to create $queue_file: $!") if $loglvl > 1;
  884.           return 1;        # Failed
  885. --- 19,35 ----
  886.   ;# Queue a mail file. Needs add_log(). Calls fatal() in emergency situations.
  887.   ;# Requires a parsed config file.
  888.   ;# 
  889. ! # Queue mail in a 'fm' file (or whatever is specified for type). The mail is
  890. ! # held in memory, within an array passed via a type-glob.
  891. ! # Returns the name of queued file if success, undef if failed. File name will
  892. ! # be absolute only when queued outside of the regular queue.
  893.   sub qmail {
  894. !     local(*array, $type) = @_;    # In which array mail is located.
  895.       local($queue_file);            # Where we attempt to save the mail
  896.       local($failed) = 0;            # Be positive and look forward :-)
  897. !     local($name);                # Name of queued file
  898. !     $queue_file = "$cf'queue/Mqm$$";
  899. !     $queue_file = "$cf'queue/Mqmb$$" if -f "$queue_file";    # Paranoid
  900.       unless (open(QUEUE, ">$queue_file")) {
  901.           &add_log("ERROR unable to create $queue_file: $!") if $loglvl > 1;
  902.           return 1;        # Failed
  903. ***************
  904. *** 43,79 ****
  905.       }
  906.       close QUEUE;
  907.       unlink "$queue_file" if $failed;
  908. !     $failed = &queue_mail($queue_file) unless $failed;
  909. !     $failed;            # 0 means success
  910.   }
  911.   
  912. ! # Queue mail in a 'fm' file. The mail is supposed to be either on disk or
  913. ! # is expected from standard input. Returns 0 for success, 1 if failed.
  914.   # In case mail comes from stdin, may not return at all but raise a fatal error.
  915.   sub queue_mail {
  916.       local($file_name) = shift(@_);        # Where mail to-be-queued is
  917. !     local($deferred) = shift(@_);        # True when 'qm' mail wanted instead
  918.       local($dirname);                    # Directory name of processed file
  919.       local($tmp_queue);                    # Tempoorary storing of queued file
  920.       local($queue_file);                    # Final name of queue file
  921.       local($ok) = 1;                        # Print status
  922.       local($_);
  923. !     &add_log("queuing mail for delayed processing") if $loglvl > 18;
  924.       chdir $cf'queue || &fatal("cannot chdir to $cf'queue");
  925.   
  926.       # The following ensures unique queue mails. As the mailagent itself may
  927.       # queue intensively throughout the SPLIT command, a queue counter is kept
  928.       # and is incremented each time a mail is successfully queued.
  929. !     local($base) = $deferred ? 'qm' : 'fm';
  930. !     $queue_file = "$base$$";        # 'fm' stands for Full Mail
  931. !     $queue_file = "$base$$x" . $queue_count if -f "$queue_file";
  932.       $queue_file = "${queue_file}x" if -f "$queue_file";    # Paranoid
  933.       ++$queue_count;                    # Counts amount of queued mails
  934.       &add_log("queue file is $queue_file") if $loglvl > 19;
  935.   
  936.       # Do not write directly in the fm file, otherwise the main
  937.       # mailagent process could start its processing on it...
  938. !     $tmp_queue = "Tfm$$";
  939.       local($sender) = "<someone>";    # Attempt to report the sender of message
  940.       if ($file_name) {                # Mail is already on file system
  941.           # Mail already in a file
  942. --- 49,103 ----
  943.       }
  944.       close QUEUE;
  945.       unlink "$queue_file" if $failed;
  946. !     unless ($failed) {
  947. !         $type = 'fm' unless defined $type;    # Defaults to a 'fm' file
  948. !         $name = &queue_mail($queue_file, $type);
  949. !         $failed = defined $name ? 0 : 1;
  950. !     }
  951. !     $failed ? undef : $name;    # File path name, undef if failed
  952.   }
  953.   
  954. ! # Queue mail in a queue file. There are three types of queued mails:
  955. ! #   . qm: messages whose handling will be delayed by at most cf'queuehold secs
  956. ! #   . fm: messages queued for immediate processing by next 'mailagent -q'
  957. ! #   . cm: callout queue messages, meant for input by callout command
  958. ! # The mail is supposed to be either on disk or is expected from standard input.
  959.   # In case mail comes from stdin, may not return at all but raise a fatal error.
  960. + # Returns the name of queued file if success, undef if failed. File name will
  961. + # be absolute only when queued outside of the regular queue.
  962.   sub queue_mail {
  963.       local($file_name) = shift(@_);        # Where mail to-be-queued is
  964. !     local($type) = shift(@_);            # Type of mail message, must be known
  965.       local($dirname);                    # Directory name of processed file
  966.       local($tmp_queue);                    # Tempoorary storing of queued file
  967.       local($queue_file);                    # Final name of queue file
  968.       local($ok) = 1;                        # Print status
  969.       local($_);
  970. !     &add_log("queuing mail ($type) for delayed processing") if $loglvl > 18;
  971.       chdir $cf'queue || &fatal("cannot chdir to $cf'queue");
  972.   
  973. +     local(%known_type) = (                # Known queue message types
  974. +         'qm', 1,
  975. +         'fm', 1,
  976. +         'cm', 1,
  977. +     );
  978. +     unless ($known_type{$type}) {
  979. +         &add_log("ERROR unknown type $type, defaulting to qm") if $loglvl > 1;
  980. +         $type = 'qm';
  981. +     }
  982.       # The following ensures unique queue mails. As the mailagent itself may
  983.       # queue intensively throughout the SPLIT command, a queue counter is kept
  984.       # and is incremented each time a mail is successfully queued.
  985. !     $queue_file = "$type$$";        # Append PID for uniqueness
  986. !     $queue_file = "$type$$x" . $queue_count if -f "$queue_file";
  987.       $queue_file = "${queue_file}x" if -f "$queue_file";    # Paranoid
  988.       ++$queue_count;                    # Counts amount of queued mails
  989.       &add_log("queue file is $queue_file") if $loglvl > 19;
  990.   
  991.       # Do not write directly in the fm file, otherwise the main
  992.       # mailagent process could start its processing on it...
  993. !     $tmp_queue = "T$type$$";
  994.       local($sender) = "<someone>";    # Attempt to report the sender of message
  995.       if ($file_name) {                # Mail is already on file system
  996.           # Mail already in a file
  997. ***************
  998. *** 141,148 ****
  999.           # in the queue directory. This file contains the names of the mails
  1000.           # stored outside of the mailagent's queue and waiting to be processed.
  1001.           $ok = &waiting_mail($tmp_queue);
  1002. !         return 1 unless $ok;    # Queuing failed if not ok
  1003. !         return 0;
  1004.       }
  1005.   
  1006.       # We succeeded in writing the temporary queue mail. Now rename it so that
  1007. --- 165,172 ----
  1008.           # in the queue directory. This file contains the names of the mails
  1009.           # stored outside of the mailagent's queue and waiting to be processed.
  1010.           $ok = &waiting_mail($tmp_queue);
  1011. !         return undef unless $ok;        # Queuing failed if not ok
  1012. !         return $tmp_queue;
  1013.       }
  1014.   
  1015.       # We succeeded in writing the temporary queue mail. Now rename it so that
  1016. ***************
  1017. *** 155,163 ****
  1018.       } else {
  1019.           &add_log("ERROR cannot rename $tmp_queue to $queue_file") if $loglvl;
  1020.           $ok = &waiting_mail($tmp_queue);
  1021.       }
  1022. !     return 1 unless $ok;        # Queuing failed if not ok
  1023. !     0;
  1024.   }
  1025.   
  1026.   # Adds mail into the agent.wait file, if possible. This file records all the
  1027. --- 179,188 ----
  1028.       } else {
  1029.           &add_log("ERROR cannot rename $tmp_queue to $queue_file") if $loglvl;
  1030.           $ok = &waiting_mail($tmp_queue);
  1031. +         $queue_file = $tmp_queue;
  1032.       }
  1033. !     return undef unless $ok;            # Queuing failed if not ok
  1034. !     $queue_file;                        # Return file name for success
  1035.   }
  1036.   
  1037.   # Adds mail into the agent.wait file, if possible. This file records all the
  1038.  
  1039. Index: agent/man/mailagent.SH
  1040. Prereq: 3.0.1.4
  1041. *** agent/man/mailagent.SH.old    Thu Sep 22 16:42:58 1994
  1042. --- agent/man/mailagent.SH    Thu Sep 22 16:42:59 1994
  1043. ***************
  1044. *** 20,26 ****
  1045.   .TH MAILAGENT $manext "Version $VERSION PL$PATCHLEVEL"
  1046.   ''' @(#) Manual page for mailagent's filter -- (c) ram February 1991
  1047.   '''
  1048. ! ''' $Id: mailagent.SH,v 3.0.1.4 1994/07/01 14:56:20 ram Exp $
  1049.   '''
  1050.   '''  Copyright (c) 1990-1993, Raphael Manfredi
  1051.   '''  
  1052. --- 20,26 ----
  1053.   .TH MAILAGENT $manext "Version $VERSION PL$PATCHLEVEL"
  1054.   ''' @(#) Manual page for mailagent's filter -- (c) ram February 1991
  1055.   '''
  1056. ! ''' $Id: mailagent.SH,v 3.0.1.5 1994/09/22 13:57:09 ram Exp $
  1057.   '''
  1058.   '''  Copyright (c) 1990-1993, Raphael Manfredi
  1059.   '''  
  1060. ***************
  1061. *** 31,36 ****
  1062. --- 31,42 ----
  1063.   '''  of the source tree for mailagent 3.0.
  1064.   '''
  1065.   ''' $Log: mailagent.SH,v $
  1066. + ''' Revision 3.0.1.5  1994/09/22  13:57:09  ram
  1067. + ''' patch12: documents new config parameters callout and linkdirs
  1068. + ''' patch12: new filtering actions AFTER and DO
  1069. + ''' patch12: variable msgpath is now defined within a PERL escape
  1070. + ''' patch12: mention that PERL escape variables are available to new commands
  1071. + '''
  1072.   ''' Revision 3.0.1.4  1994/07/01  14:56:20  ram
  1073.   ''' patch8: documents new eleven configuration variables
  1074.   ''' patch8: sub-section on timeouts has been expanded
  1075. ***************
  1076. *** 204,209 ****
  1077. --- 210,220 ----
  1078.   (suggested: OFF, unless you use ONCE, UNIQUE or RECORD commands, or activate
  1079.   the vacation mode.)
  1080.   .TP
  1081. + .I callout
  1082. + The name of the callout queue file where batched jobs are kept. This
  1083. + parameter must be defined when using the AFTER command.
  1084. + (suggested: $spool/callout)
  1085. + .TP
  1086.   .I cleanlaps
  1087.   Cleaning period for database entries. The value of the last clean up is saved
  1088.   into the context file. This is optional, but needed if \fIautoclean\fR is on.
  1089. ***************
  1090. *** 266,277 ****
  1091.   .I level
  1092.   Log level, see below for a definition of available levels (suggested: 9).
  1093.   .TP
  1094.   .I lockdekay
  1095.   The delay in seconds between two locking attempts. (optional, defaults to: 2).
  1096.   .TP
  1097.   .I lockhold
  1098.   The maximum delay in seconds for holding a lock. After that time, the lock
  1099. ! will be broken. (optional, defaults to: 3600)
  1100.   .TP
  1101.   .I lockmax
  1102.   Maximum number of locking attempts before giving up. (optional,
  1103. --- 277,296 ----
  1104.   .I level
  1105.   Log level, see below for a definition of available levels (suggested: 9).
  1106.   .TP
  1107. + .I linkdirs
  1108. + When set to ON, carefully checks symbolic links to directories when performing
  1109. + security checks on sensitive files. This will (recursively) check for each
  1110. + symbolic link level that the target directory is not world writable or group
  1111. + writable and that the parent directory of each target link is not world
  1112. + writable. If the \fIsecure\fR option is OFF, this parameter is ignored.
  1113. + (optional, defaults to: ON, suggested: ON when secure is also ON).
  1114. + .TP
  1115.   .I lockdekay
  1116.   The delay in seconds between two locking attempts. (optional, defaults to: 2).
  1117.   .TP
  1118.   .I lockhold
  1119.   The maximum delay in seconds for holding a lock. After that time, the lock
  1120. ! will be broken. (optional, defaults to: 3600).
  1121.   .TP
  1122.   .I lockmax
  1123.   Maximum number of locking attempts before giving up. (optional,
  1124. ***************
  1125. *** 1362,1367 ****
  1126. --- 1381,1404 ----
  1127.   Abort application of filtering rules immediately. See REJECT for the meaning
  1128.   of the optional parameters. (Does not modify existing status)
  1129.   .TP
  1130. + AFTER [-sanc] \fI(time) action\fR
  1131. + Records a callback for after the specified \fItime\fR, where \fIaction\fR will
  1132. + be performed. By default, a mailagent filtering action is assumed (\fB\-a\fR
  1133. + option), on the current mail message. A shell command (\fB\-c\fR) may be
  1134. + given instead, receiving the current mail message as standard input. Finally,
  1135. + a plain shell command may be run (with no input) using the \fB\-s\fR option.
  1136. + The option \fB-n\fR may be used when the current mail message does not need
  1137. + to be kept for input. For instance:
  1138. + .Ex
  1139. + AFTER \fB-an\fR (1 day) DO ~/process:proc'run(%u)
  1140. + .Ef
  1141. + would call \fIproc'run\fR defined in the \fI~/process\fR file in one day
  1142. + from now, without giving any input (the action here does not require any).
  1143. + Note that the command is not called \fIAT\fR because the call will only
  1144. + be performed at the next mailagent invocation after the specified time has
  1145. + elapsed. Dates are specified using the same format as in SELECT.
  1146. + (Fails if the action cannot be recorded in the callout queue).
  1147. + .TP
  1148.   ANNOTATE [-d] \fIfield value\fR
  1149.   Annotate message by adding \fIfield\fR into the mail header, with the
  1150.   supplied \fIvalue\fR. This is like the MH command \fIanno\fR, but the
  1151. ***************
  1152. *** 1422,1427 ****
  1153. --- 1459,1482 ----
  1154.   for pattern loading.
  1155.   (Fails if mail cannot be resent)
  1156.   .TP
  1157. + DO \fIroutine\fR [\fI(arg1, arg2, ... , argn)\fR]
  1158. + Calls the perl \fIroutine\fR, with the supplied arguments if any. This is a
  1159. + very low level hook into \fImailagent's\fR internal. The routine can be
  1160. + specified by itself (\fIpackage'name\fR, \fIpackage\fR being \fImain\fR by
  1161. + default), or identified by a leading \fItag\fR, followed by a ':', then the
  1162. + routine name as before. The \fItag\fR can be a path to a file where the routine
  1163. + is defined, or a command name (for user-defined commands which are loaded
  1164. + dynamically). For instance
  1165. + .Ex
  1166. + DO UNKIT:newcmd'unkit('true')
  1167. + .Ef
  1168. + would lookup
  1169. + the user-defined \fIUNKIT\fR command, load the file where it is defined (in the
  1170. + \fInewcmd\fR package), then call the routine with \fI'true'\fR as argument.
  1171. + The \fIpackage\fR specified determines where the loading is done, so be sure
  1172. + it is consistent with the definition in the file where the routine is defined.
  1173. + (Fails if the routine cannot be located and executed)
  1174. + .TP
  1175.   DELETE
  1176.   Delete the current message. Actually, this does not do anything, it just marks
  1177.   the mail as saved. If no further action involving saving is done, then the
  1178. ***************
  1179. *** 1850,1855 ****
  1180. --- 1905,1915 ----
  1181.   This table, indexed by field name, returns the raw content on the
  1182.   corresponding header line. See below.
  1183.   .TP
  1184. + .I \$msgpath
  1185. + The full path name of the folder (or message within an MH folder) where
  1186. + the last saving operation has occurred. This is intended to be used if
  1187. + you wish to construct your own mail reception notification.
  1188. + .TP
  1189.   .I \$login
  1190.   The login name of the address on the From: line.
  1191.   .TP
  1192. ***************
  1193. *** 2771,2776 ****
  1194. --- 2831,2841 ----
  1195.   saving operation has been completed. If at the end of the filtering, this
  1196.   variable is still \fB0\fR, then the default LEAVE will be executed.
  1197.   .TP
  1198. + .I \$folder_saved
  1199. + The value of that variable governs the \fI\$msgpath\fR convenience variable
  1200. + set for PERL escapes. It is updated whenever a message is written to a file,
  1201. + to hold the path of the written file.
  1202. + .TP
  1203.   .I \$vacation
  1204.   This is a boolean, which when \fIset\fR to \fB1\fR will allow vacation messages.
  1205.   It is mainly used by the VACATION command, but if you wish to re-implement that
  1206. ***************
  1207. *** 2795,2800 ****
  1208. --- 2860,2869 ----
  1209.   This records the current state of the filter automaton (working mode), in a
  1210.   literal string form, typically modified by the BEGIN command or as a side
  1211.   effect, as in REJECT for instance.
  1212. + .PP
  1213. + All the special variables set-up for PERL escapes are also installed within
  1214. + the \fInewcmd\fR package. Those are \fI\$login\fR, \fI%header\fR, etc... You
  1215. + may peruse them at will.
  1216.   .PP
  1217.   Other variables you might have a need for are configuration parameters, held
  1218.   in the \fI~/.mailagent\fR configuration file. Well, the rule is simple. The
  1219.  
  1220. Index: agent/test/cmd/after.t
  1221. *** agent/test/cmd/after.t.old    Thu Sep 22 16:43:28 1994
  1222. --- agent/test/cmd/after.t    Thu Sep 22 16:43:28 1994
  1223. ***************
  1224. *** 0 ****
  1225. --- 1,18 ----
  1226. + # The AFTER command
  1227. + # $Id: after.t,v 3.0.1.1 1994/09/22 14:40:57 ram Exp $
  1228. + #
  1229. + #  Copyright (c) 1990-1993, Raphael Manfredi
  1230. + #  
  1231. + #  You may redistribute only under the terms of the Artistic License,
  1232. + #  as specified in the README file that comes with the distribution.
  1233. + #  You may reuse parts of this distribution only within the terms of
  1234. + #  that same Artistic License; a copy of which may be found at the root
  1235. + #  of the source tree for mailagent 3.0.
  1236. + #
  1237. + # $Log: after.t,v $
  1238. + # Revision 3.0.1.1  1994/09/22  14:40:57  ram
  1239. + # patch12: created
  1240. + #
  1241. + print "-1\n";    # Automatic testing difficult
  1242.  
  1243. *** End of Patch 13 ***
  1244.  
  1245. exit 0 # Just in case...
  1246.