home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume41 / mailagnt / part20 < prev    next >
Encoding:
Text File  |  1993-12-03  |  54.3 KB  |  1,613 lines

  1. Newsgroups: comp.sources.misc
  2. From: Raphael Manfredi <ram@acri.fr>
  3. Subject: v41i020:  mailagent - Flexible mail filtering and processing package, v3.0, Part20/26
  4. Message-ID: <1993Dec3.213440.22851@sparky.sterling.com>
  5. X-Md4-Signature: d8128968ad9b751a305fb0f11d1eb027
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Advanced Computer Research Institute, Lyon, France.
  8. Date: Fri, 3 Dec 1993 21:34:40 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: Raphael Manfredi <ram@acri.fr>
  12. Posting-number: Volume 41, Issue 20
  13. Archive-name: mailagent/part20
  14. Environment: UNIX, Perl
  15. Supersedes: mailagent: Volume 33, Issue 93-109
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then feed it
  19. # into a shell via "sh file" or similar.  To overwrite existing files,
  20. # type "sh file -c".
  21. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  22. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  23. # Contents:  Changes agent/files/chkagent.sh agent/man/package.SH
  24. #   agent/pl/acs_rqst.pl agent/pl/context.pl agent/pl/distribs.pl
  25. #   agent/pl/dynload.pl agent/pl/history.pl agent/pl/mbox.pl
  26. #   agent/pl/pqueue.pl agent/pl/secure.pl agent/test/README
  27. #   agent/test/basic/config.t agent/test/filter/hook.t
  28. #   agent/test/misc/usrmac.t
  29. # Wrapped by ram@soft208 on Mon Nov 29 16:49:57 1993
  30. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  31. echo If this archive is complete, you will see the following message:
  32. echo '          "shar: End of archive 20 (of 26)."'
  33. if test -f 'Changes' -a "${1}" != "-c" ; then 
  34.   echo shar: Will not clobber existing file \"'Changes'\"
  35. else
  36.   echo shar: Extracting \"'Changes'\" \(2765 characters\)
  37.   sed "s/^X//" >'Changes' <<'END_OF_FILE'
  38. XThis file shortly documents the new features appearing in mailagent 3.0
  39. Xcompared to mailagent 2.9 PL19. For more details, please refer to the manual
  40. Xpage [yes, it's getting bigger and bigger, sorry].
  41. X
  42. X. Mailhook disappears. Folder hooks are now handled without the need for an
  43. X  extra process.
  44. X
  45. X. NOTIFY now takes its FIRST argument to indicate the message file,
  46. X  instead of its LAST as in the 2.9 release. This change in order to make
  47. X  it compatible with MESSAGE.
  48. X
  49. X. Mailagent secure configuration checks. Impossible to use mailagent if the
  50. X  ~/.mailagent file or the rule file are not correctly protected.
  51. X
  52. X. Dynamic loading interface (dynload.pl) available for perl commands.
  53. X
  54. X. Added a generic command server. Mailagent provides the server engine and
  55. X  users write their own commands, with special provision for perl scripts
  56. X  which can be directly loaded and executed within mailagent itself.
  57. X
  58. X. User-defined macro support %-(x) and perl interface.
  59. X
  60. X. New APPLY, REQUIRE, SERVER, MACRO commands.
  61. X
  62. X. Support for rule caching. This avoids recompiling large rule files at every
  63. X  mailagent run, but speed has never never been a main concern in this program
  64. X  anyway.
  65. X
  66. X. Negated mode support <!MODE>. Rule is not executed if in the specified
  67. X  negated mode. This supersedes normal modes, i.e. <MODE, !MODE> is never
  68. X  executed.
  69. X
  70. X. Can now configure sendmail process and inews, with options, from ~/.mailagent.
  71. X  If your sendmail behaves strangely or want to have interactive delivery
  72. X  instead of queuing, this is the place to look at.
  73. X
  74. X. New usr_log facility, enabling user-defined logfiles. Available for your
  75. X  own commands and used internally by mailagent.
  76. X
  77. X. Saving operations now check on the size of the produced folder for NFS.
  78. X
  79. X. Can now access ~/.mailagent config params via %=var
  80. X
  81. X. Fixed bug in agent queue parsing. This happened mainly on SUN systems, and
  82. X  was apparently a perl fileglob bug (or is it a /bin/csh bug?). Anyway, I
  83. X  now use readdir() to access the queue, which suppresses forking of an extra
  84. X  process.
  85. X
  86. X. Improved RFC822 address parsing. Now understands group names as login names.
  87. X
  88. X. Output for mailagent -d formatted differently.
  89. X
  90. X. Selector range Body <1,4>: available. This example selects body lines 1 to
  91. X  4 (inclusive) for matching.
  92. X
  93. X. Can now deliver to MH folders (without the need for an extra process). Use
  94. X  'SAVE +foo' to deliver to the MH folder foo. Unseen sequences specified in
  95. X  your ~/.mh_profile are correctly updated.
  96. X
  97. X. Minimal support for directory hooks (only behaves like MH folders currently).
  98. X
  99. X. New @SH package command for dist-3.0 MailAuthor.U support. That metaconfig
  100. X  units sends a mail in specific format to record users of some package, and
  101. X  the package command is there to automate the process.
  102. END_OF_FILE
  103.   if test 2765 -ne `wc -c <'Changes'`; then
  104.     echo shar: \"'Changes'\" unpacked with wrong size!
  105.   fi
  106.   # end of 'Changes'
  107. fi
  108. if test -f 'agent/files/chkagent.sh' -a "${1}" != "-c" ; then 
  109.   echo shar: Will not clobber existing file \"'agent/files/chkagent.sh'\"
  110. else
  111.   echo shar: Extracting \"'agent/files/chkagent.sh'\" \(2504 characters\)
  112.   sed "s/^X//" >'agent/files/chkagent.sh' <<'END_OF_FILE'
  113. X#!/bin/sh
  114. X#
  115. X# $Id: chkagent.sh,v 3.0 1993/11/29 13:47:49 ram Exp ram $
  116. X#
  117. X#  Copyright (c) 1990-1993, Raphael Manfredi
  118. X#  
  119. X#  You may redistribute only under the terms of the Artistic License,
  120. X#  as specified in the README file that comes with the distribution.
  121. X#  You may reuse parts of this distribution only within the terms of
  122. X#  that same Artistic License; a copy of which may be found at the root
  123. X#  of the source tree for mailagent 3.0.
  124. X#
  125. X# $Log: chkagent.sh,v $
  126. X# Revision 3.0  1993/11/29  13:47:49  ram
  127. X# Baseline for mailagent 3.0 netwide release.
  128. X#
  129. X
  130. X# Make sure the mailagent is working well
  131. Xlookat='ERROR|FAILED|WARNING|FATAL|DUMPED'
  132. X
  133. Xtrap "rm -f $report $output $todaylog $msg" 1 2 3 15
  134. X
  135. X# Interpret the ~/.mailagent configuration file
  136. Xset X `<$HOME/.mailagent sed -n \
  137. X    -e '/^[     ]*#/d' \
  138. X    -e 's/[     ]*#/#/' \
  139. X    -e 's/^[     ]*\([^     :\/]*\)[     ]*:[     ]*\([^#]*\).*/\1="\2";/p'`
  140. Xshift
  141. X
  142. X# Deal with possible white spaces in variables and ~ substitution
  143. Xcmd=''
  144. Xfor line in $*; do
  145. X    cmd="$cmd$line"
  146. Xdone
  147. Xcmd=`echo $cmd | sed -e "s|~|$HOME|g"`
  148. Xeval $cmd
  149. X
  150. X# Compute location of report file and log file
  151. Xreport="/tmp/cAg$$"
  152. Xoutput="/tmp/cAo$$"
  153. Xlogfile="$logdir/$log"
  154. Xtodaylog="/tmp/tAg$$"
  155. X
  156. X# Current date format to look for in logfile
  157. Xtoday=`date "+%y/%m/%d"`
  158. X
  159. Xif test -f "$logfile"; then
  160. X    grep "$today" $logfile > $todaylog
  161. X    egrep ": ($lookat)" $todaylog > $output
  162. X    if test -s "$output"; then
  163. X        echo "*** Errors from logfile ($logfile):" > $report
  164. X        echo " " >> $report
  165. X        cat $output >> $report
  166. X    fi
  167. X    rm -f $todaylog $output
  168. Xelse
  169. X    echo "Cannot find $logfile" > $report
  170. Xfi
  171. X
  172. X# ~/.bak is the output from .forward
  173. Xif test -s "$HOME/.bak"; then
  174. X    echo " " >> $report
  175. X    echo "*** Errors from ~/.bak:" >> $report
  176. X    echo " " >> $report
  177. X    cat $HOME/.bak >> $report
  178. X    cp /dev/null $HOME/.bak
  179. Xfi
  180. X
  181. X# Look for mails in the emergency directory
  182. Xls -C $emergdir > $output
  183. Xif test -s "$output"; then
  184. X    echo " " >> $report
  185. X    echo "*** Mails held in lost+mail ($emergdir):" >> $report
  186. X    echo " " >> $report
  187. X    cat $output >> $report
  188. Xfi
  189. Xrm -f $output
  190. X
  191. X# Spot any unprocessed mails in the queue
  192. Xcd $queue
  193. Xls -C qm* fm* > $output 2>/dev/null
  194. Xif test -s "$output"; then
  195. X    echo " " >> $report
  196. X    echo "*** Unprocessed mails in queue ($queue):" >> $report
  197. X    echo " " >> $report
  198. X    cat $output >> $report
  199. Xfi
  200. Xrm -f $output
  201. X
  202. Xif test -s "$report"; then
  203. X    msg="/tmp/mAg$$"
  204. X    cat >$msg <<EOM
  205. XTo: $user
  206. XSubject: Errors from mailagent system
  207. X
  208. XEOM
  209. X    cat $report >>$msg
  210. X    rm -f $report
  211. X    /usr/lib/sendmail -odi -t <$msg
  212. X    rm -f $msg
  213. Xelse
  214. X    rm -f $report
  215. Xfi
  216. X
  217. Xexit 0
  218. END_OF_FILE
  219.   if test 2504 -ne `wc -c <'agent/files/chkagent.sh'`; then
  220.     echo shar: \"'agent/files/chkagent.sh'\" unpacked with wrong size!
  221.   fi
  222.   chmod +x 'agent/files/chkagent.sh'
  223.   # end of 'agent/files/chkagent.sh'
  224. fi
  225. if test -f 'agent/man/package.SH' -a "${1}" != "-c" ; then 
  226.   echo shar: Will not clobber existing file \"'agent/man/package.SH'\"
  227. else
  228.   echo shar: Extracting \"'agent/man/package.SH'\" \(3534 characters\)
  229.   sed "s/^X//" >'agent/man/package.SH' <<'END_OF_FILE'
  230. Xcase $CONFIG in
  231. X'')
  232. X    if test -f config.sh; then TOP=.;
  233. X    elif test -f ../config.sh; then TOP=..;
  234. X    elif test -f ../../config.sh; then TOP=../..;
  235. X    elif test -f ../../../config.sh; then TOP=../../..;
  236. X    elif test -f ../../../../config.sh; then TOP=../../../..;
  237. X    else
  238. X        echo "Can't find config.sh."; exit 1
  239. X    fi
  240. X    . $TOP/config.sh
  241. X    ;;
  242. Xesac
  243. Xcase "$0" in
  244. X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
  245. Xesac
  246. Xecho "Extracting agent/man/package.$manext (with variable substitutions)"
  247. X$rm -f package.$manext
  248. X$spitshell >package.$manext <<!GROK!THIS!
  249. X.TH PACKAGE $manext
  250. X''' @(#) Manual page for mailagent's package command
  251. X'''
  252. X''' $Id: package.SH,v 3.0 1993/11/29 13:48:31 ram Exp ram $
  253. X'''
  254. X'''  Copyright (c) 1990-1993, Raphael Manfredi
  255. X'''  
  256. X'''  You may redistribute only under the terms of the Artistic License,
  257. X'''  as specified in the README file that comes with the distribution.
  258. X'''  You may reuse parts of this distribution only within the terms of
  259. X'''  that same Artistic License; a copy of which may be found at the root
  260. X'''  of the source tree for mailagent 3.0.
  261. X'''
  262. X'''  Original Author: Graham Stoney, 1993
  263. X'''
  264. X''' $Log: package.SH,v $
  265. X''' Revision 3.0  1993/11/29  13:48:31  ram
  266. X''' Baseline for mailagent 3.0 netwide release.
  267. X'''
  268. X''' 
  269. X.SH NAME
  270. Xpackage \- register package user via mailagent
  271. X.SH SYNOPSIS
  272. Xpackage\fR \fIaddress\fR \fIsystem\fR \fIversion\fR \fIpatchlevel\fR
  273. X[ mailpatches | notifypatches ]
  274. X.SH DESCRIPTION
  275. XThis command is not intended to be run directly by a user, but may
  276. Xappear in any mail whose subject is set to \fICommand\fR. Such mail
  277. Xwill be processed by the \fImailagent\fR(1), which will extract all lines
  278. Xbeginning with \fI@SH\fR, which may specify this command. The
  279. Xmailagent first sets environment variables that will be used by the
  280. Xcommand.
  281. X.PP
  282. X.I Package
  283. Xis used to notify the author of a package about its users.
  284. XIt is normally generated automatically by the MailAuthor.U unit when the user
  285. Xruns
  286. X.IR Configure .
  287. X.PP
  288. XIf the
  289. X.I patchlevel
  290. Xspecified is not the latest for that
  291. X.I system
  292. Xand
  293. X.IR version ,
  294. Xmail is immediately sent suggesting that they upgrade and remindng them how to
  295. Xrequest the latest patches.
  296. X.PP
  297. XThe final parameter, if included may be set to
  298. X.I mailpatches
  299. Xto specify that the user would like to have future patches mailed to them, or
  300. X.I notifypatches
  301. Xto specify that a mail notification of future patches should be sent, rather
  302. Xthan the entire patch.
  303. X.PP
  304. XThe user's
  305. X.I address
  306. Xand notification request are saved in the file
  307. X.I users
  308. Xin the package's directory.
  309. X.SH FILES
  310. X.PD 0
  311. X.TP 20
  312. X~/.mailagent
  313. Xconfiguration file for mailagent.
  314. X.TP
  315. XSystem/users
  316. Xlist of users of that system.
  317. X.IP
  318. XThis file consists of single line records, one for each registered user.
  319. XEach record consists of three tab-separated fields.
  320. X.sp
  321. XThe first field indicates
  322. Xthe level of updates requested by the user by a single letter as follows:
  323. X.RS
  324. X.TP
  325. X.B M
  326. XMail future patches directly to the user when they are issued.
  327. X.TP
  328. X.B N
  329. XNotify the user of future patches.
  330. X.TP
  331. X.B U
  332. XThe users chose to let the author know that they have tried the program, but
  333. Xdoes not wish to know about future updates.
  334. X.TP
  335. X.B L
  336. XThe user is no longer interested in the program and wants to be left alone.
  337. X.RE
  338. X.sp
  339. X.IP
  340. XThe second field is their last notified patch level, or a dash
  341. X.RB ( - )
  342. Xif it is not known.
  343. X.sp
  344. X.IP
  345. XThe third field is the user's Email address.
  346. X.TP
  347. XLog/agentlog
  348. Xmailagent's log file
  349. X.PD
  350. X.SH AUTHOR
  351. XGraham Stoney <greyham@research.canon.oz.au>
  352. X.SH "SEE ALSO"
  353. Xmailagent($manext), metaconfig($manext).
  354. X!GROK!THIS!
  355. Xchmod 444 package.$manext
  356. END_OF_FILE
  357.   if test 3534 -ne `wc -c <'agent/man/package.SH'`; then
  358.     echo shar: \"'agent/man/package.SH'\" unpacked with wrong size!
  359.   fi
  360.   # end of 'agent/man/package.SH'
  361. fi
  362. if test -f 'agent/pl/acs_rqst.pl' -a "${1}" != "-c" ; then 
  363.   echo shar: Will not clobber existing file \"'agent/pl/acs_rqst.pl'\"
  364. else
  365.   echo shar: Extracting \"'agent/pl/acs_rqst.pl'\" \(3325 characters\)
  366.   sed "s/^X//" >'agent/pl/acs_rqst.pl' <<'END_OF_FILE'
  367. X;# $Id: acs_rqst.pl,v 3.0 1993/11/29 13:48:32 ram Exp ram $
  368. X;#
  369. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  370. X;#  
  371. X;#  You may redistribute only under the terms of the Artistic License,
  372. X;#  as specified in the README file that comes with the distribution.
  373. X;#  You may reuse parts of this distribution only within the terms of
  374. X;#  that same Artistic License; a copy of which may be found at the root
  375. X;#  of the source tree for mailagent 3.0.
  376. X;#
  377. X;# $Log: acs_rqst.pl,v $
  378. X;# Revision 3.0  1993/11/29  13:48:32  ram
  379. X;# Baseline for mailagent 3.0 netwide release.
  380. X;#
  381. X;#
  382. X;# The basic file locking scheme implemented here by acs_rqst is not completely
  383. X;# suitable with NFS if multiple mailagent can run, since they could have the
  384. X;# same PID on different machine and both think they got a lock. To make this
  385. X;# work with NFS, the ~/.mailagent config file must have the 'nfslock' variable
  386. X;# set to 'YES', which will cause the mailagent to include hostname informations
  387. X;# in the lock file.
  388. X;#
  389. X;# The traditional NFS scheme of having a `hostname`.pid file linked to .lock
  390. X;# (since the linking operation remains atomic even with NFS) does not seem
  391. X;# suitable here, since I want to be able to recover from crashes, and detect
  392. X;# out-of-date locks. Therefore, I must be able to know what is the name of the
  393. X;# lock file. The link/unlink trick could leave some temporary files around.
  394. X;# Since write on disks are atomic anyway, only one process can conceivably
  395. X;# obtain a lock with my scheme.
  396. X;#
  397. X;# The NFS-secure lock is made optional because, in order to get the hostname,
  398. X;# perl must fork to exec an appropriate program. This added overhead might not
  399. X;# be necessary in all the situations.
  400. X;#
  401. X# Asks for the exclusive access of a file. The config variable 'nfslock'
  402. X# determines whether the locking scheme has to be NFS-secure or not.
  403. X# The given parameter (let's say F) is the absolute path of the file we want
  404. X# to access. The routine checks for the presence of F.lock. If it exists, it
  405. X# sleeps 2 seconds and tries again. After 10 trys, it reports failure by
  406. X# returning -1. Otherwise, file F.lock is created and the pid of the current
  407. X# process is written. It is checked afterwards.
  408. Xsub acs_rqst {
  409. X    local($file) = @_;    # file to be locked
  410. X    local($max) = 30;    # max number of attempts
  411. X    local($delay) = 2;    # seconds to wait between attempts
  412. X    local($mask);        # to save old umask
  413. X    local($stamp);        # string written in lock file
  414. X    &checklock($file);    # avoid long-lasting locks
  415. X    if ($cf'nfslock =~ /on/i) {            # NFS-secure lock wanted
  416. X        $stamp = "$$" . &hostname;        # use PID and hostname
  417. X    } else {
  418. X        $stamp = "$$";                    # use PID only (may spare a fork)
  419. X    }
  420. X    local($lockfile) = $file . $lockext;
  421. X    while ($max) {
  422. X        $max--;
  423. X        if (-f $lockfile) {
  424. X            sleep($delay);                # busy: wait
  425. X            next;
  426. X        }
  427. X        # Attempt to create lock
  428. X        $mask = umask(0333);            # no write permission
  429. X        if (open(FILE, ">$lockfile")) {
  430. X            print FILE "$stamp\n";        # write locking stamp
  431. X            close FILE;
  432. X            umask($mask);                # restore old umask
  433. X            # Check lock
  434. X            open(FILE, $lockfile);
  435. X            chop($_ = <FILE>);            # read contents
  436. X            close FILE;
  437. X            last if $_ eq $stamp;        # lock is ok
  438. X        } else {
  439. X            umask($mask);                # restore old umask
  440. X            sleep($delay);                # busy: wait
  441. X        }
  442. X    }
  443. X    if ($max) {
  444. X        $result = 0;    # ok
  445. X    } else {
  446. X        $result = -1;    # could not lock
  447. X    }
  448. X    $result;            # return status
  449. X}
  450. X
  451. END_OF_FILE
  452.   if test 3325 -ne `wc -c <'agent/pl/acs_rqst.pl'`; then
  453.     echo shar: \"'agent/pl/acs_rqst.pl'\" unpacked with wrong size!
  454.   fi
  455.   # end of 'agent/pl/acs_rqst.pl'
  456. fi
  457. if test -f 'agent/pl/context.pl' -a "${1}" != "-c" ; then 
  458.   echo shar: Will not clobber existing file \"'agent/pl/context.pl'\"
  459. else
  460.   echo shar: Extracting \"'agent/pl/context.pl'\" \(3590 characters\)
  461.   sed "s/^X//" >'agent/pl/context.pl' <<'END_OF_FILE'
  462. X;# $Id: context.pl,v 3.0 1993/11/29 13:48:38 ram Exp ram $
  463. X;#
  464. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  465. X;#  
  466. X;#  You may redistribute only under the terms of the Artistic License,
  467. X;#  as specified in the README file that comes with the distribution.
  468. X;#  You may reuse parts of this distribution only within the terms of
  469. X;#  that same Artistic License; a copy of which may be found at the root
  470. X;#  of the source tree for mailagent 3.0.
  471. X;#
  472. X;# $Log: context.pl,v $
  473. X;# Revision 3.0  1993/11/29  13:48:38  ram
  474. X;# Baseline for mailagent 3.0 netwide release.
  475. X;#
  476. X;# 
  477. X;# Keep track of the mailagent's context, in particular all the actions which
  478. X;# may be performed in a batched way and need to save some contextual data.
  479. X;#
  480. Xpackage context;
  481. X
  482. X#
  483. X# General handling
  484. X#
  485. X
  486. X# Initialize context from context file
  487. Xsub init {
  488. X    &default;                        # Load a default context
  489. X    return unless -f $cf'context;    # Finished if no saved context
  490. X    &load;                            # Load context, overwriting default context
  491. X    &clean;                            # Remove uneeded entries from context
  492. X}
  493. X
  494. X# Provide a default context
  495. Xsub default {
  496. X    %Context = (
  497. X        'last-clean', '0',            # Last cleaning of hash files
  498. X    );
  499. X}
  500. X
  501. X# Load the context entries
  502. Xsub load {
  503. X    unless(open(CONTEXT, "$cf'context")) {
  504. X        &'add_log("WARNING unable to open context file: $!") if $'loglvl > 5;
  505. X        return;
  506. X    }
  507. X    &'add_log("loading mailagent context") if $'loglvl > 15;
  508. X    local($_, $.);
  509. X    while (<CONTEXT>) {
  510. X        next if /^\s*#/;
  511. X        if (/^([\w\-]+)\s*:\s*(\S+)/) {
  512. X            $Context{$1} = $2;
  513. X            next;
  514. X        }
  515. X        &'add_log("WARNING context file corrupted, line $.") if $'loglvl > 5;
  516. X        last;
  517. X    }
  518. X    close CONTEXT;
  519. X}
  520. X
  521. X# Clean context, removing useless entries
  522. Xsub clean {
  523. X    delete $Context{'last-clean'} unless $cf'autoclean =~ /^on/i;
  524. X}
  525. X
  526. X# Save a new context file
  527. Xsub save {
  528. X    require 'ctime.pl';
  529. X    local($existed) = -f $cf'context;
  530. X    &'acs_rqst($cf'context) if $existed;    # Lock existing file
  531. X    unless (open(CONTEXT, ">$cf'context")) {
  532. X        &'add_log("ERROR cannot overwrite context file: $!") if $'loglvl > 1;
  533. X        return;
  534. X    }
  535. X    &'add_log("saving context file $cf'context") if $'loglvl > 17;
  536. X    local($key, $value, $item);
  537. X    print CONTEXT "# Mailagent context, last updated " . &'ctime(time);
  538. X    while (($key, $value) = each %Context) {
  539. X        next unless $value;
  540. X        $item++;
  541. X        print CONTEXT $key, ': ', $value, "\n";
  542. X    }
  543. X    close CONTEXT;
  544. X    unlink "$cf'context" unless $item;        # Do not leave empty context
  545. X    &'add_log("deleted empty context") if $'loglvl > 17 && !$item;
  546. X    &'free_file($cf'context) if $existed;
  547. X}
  548. X
  549. X#
  550. X# Context-dependant actions
  551. X#
  552. X
  553. X# Remove entries in dbr hash files which are old enough. For this operation
  554. X# to be performed, the autoclean variable must be set to ON in ~/.mailagent,
  555. X# the cleanlaps indicates the period for those automatic cleanings, and agemax
  556. X# specifies the maximum allowed time within the database.
  557. Xsub autoclean {
  558. X    return unless $cf'autoclean =~ /^on/i;
  559. X    local($period) = &'seconds_in_period($cf'cleanlaps);
  560. X    return if ($Context{'last-clean'} + $period) > time;
  561. X    # Retry time reached -- start auto cleaning
  562. X    &'add_log("autocleaning of dbr files") if $'loglvl > 8;
  563. X    $period = &'seconds_in_period($cf'agemax);
  564. X    &dbr'clean($period);
  565. X    $Context{'last-clean'} = time;            # Update last cleaning time
  566. X}
  567. X
  568. X#
  569. X# Perform all contextual actions
  570. X#
  571. X
  572. X# Run all the contextual actions, each action returning if not needed or if
  573. X# the retry time was not reached. This routine is the main entry point in
  574. X# the package, and is the only one called from the outside world.
  575. Xsub main'contextual_operations {
  576. X    &init;                    # Initialize context
  577. X    &autoclean;                # Clean dbr hash files
  578. X    &save;                    # Save new context
  579. X}
  580. X
  581. Xpackage main;
  582. X
  583. END_OF_FILE
  584.   if test 3590 -ne `wc -c <'agent/pl/context.pl'`; then
  585.     echo shar: \"'agent/pl/context.pl'\" unpacked with wrong size!
  586.   fi
  587.   # end of 'agent/pl/context.pl'
  588. fi
  589. if test -f 'agent/pl/distribs.pl' -a "${1}" != "-c" ; then 
  590.   echo shar: Will not clobber existing file \"'agent/pl/distribs.pl'\"
  591. else
  592.   echo shar: Extracting \"'agent/pl/distribs.pl'\" \(3366 characters\)
  593.   sed "s/^X//" >'agent/pl/distribs.pl' <<'END_OF_FILE'
  594. X;# $Id: distribs.pl,v 3.0 1993/11/29 13:48:40 ram Exp ram $
  595. X;#
  596. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  597. X;#  
  598. X;#  You may redistribute only under the terms of the Artistic License,
  599. X;#  as specified in the README file that comes with the distribution.
  600. X;#  You may reuse parts of this distribution only within the terms of
  601. X;#  that same Artistic License; a copy of which may be found at the root
  602. X;#  of the source tree for mailagent 3.0.
  603. X;#
  604. X;# $Log: distribs.pl,v $
  605. X;# Revision 3.0  1993/11/29  13:48:40  ram
  606. X;# Baseline for mailagent 3.0 netwide release.
  607. X;#
  608. X;#
  609. X;# This file relies on the following external conditions:
  610. X;#    - operation &fatal() available for fatal errors
  611. X;#    - the configuration variables are properly set
  612. X;#    - logging is done via &add_log()
  613. X;#
  614. X# Read a distribution file and fill in data structures for
  615. X# the query functions. All the data are stored in associative
  616. X# arrays, indexed by the system's name and version number.
  617. X# Associative arrays are:
  618. X#
  619. X# name          indexed by       information
  620. X#
  621. X# %Program      name + version   have we seen that line ?
  622. X# %System       name             is name a valid system ?
  623. X# %Version      name             latest version for system
  624. X# %Location        name + version   location of the distribution
  625. X# %Archived     name + version   is distribution archived ?
  626. X# %Compressed   name + version   is archive compressed ?
  627. X# %Patch_only   name + version   true if only patches delivered
  628. X# %Maintained   name + version   true if distribution is maintained
  629. X# %Patches      name + version   true if official patches available
  630. X#
  631. X# For systems with a version of '---' in the file, the version
  632. X# for accessing the data has to be a "0" string.
  633. X#
  634. X# Expected format for the distribution file:
  635. X#     system version location archive compress patches
  636. X#
  637. X# The `archive', `compress' and `patches' fields can take one
  638. X# of the following states: "yes" and "no". An additional state
  639. X# for `patches' is "old", which means that only patches are
  640. X# available for the version, and not the distribution. Another is
  641. X# "patch" which means that official patches are available.
  642. X# All these states can be abbreviated with the first letter.
  643. X#
  644. Xsub read_dist {
  645. X    local($fullname);
  646. X    open(DIST, "$cf'distlist") ||
  647. X        &fatal("cannot open distribution file");
  648. X    while (<DIST>) {
  649. X        next if /^\s*#/;    # skip comments
  650. X        next if /^\s*$/;    # skip empty lines
  651. X        next unless s/^\s*(\w+)\s+([.\-0-9]+)//;
  652. X        $fullname = $1 . "|" . ($2 eq '---'? "0" : $2);
  653. X        if (defined $Program{$fullname}) {
  654. X            &add_log("WARNING duplicate distlist entry $1 $2 ignored")
  655. X                if $loglvl > 5;
  656. X            next;
  657. X        }
  658. X        $Program{$fullname}++;
  659. X        $Version{$1} = ($2 eq '---' ? "0" : $2) unless
  660. X            defined($System{$1}) && $Version{$1} > ($2 eq '---' ? "0":$2);
  661. X        $System{$1}++;
  662. X        unless (/^\s*(\S+)\s+(\w+)\s+(\w+)\s+(\w+)/) {
  663. X            &add_log("WARNING bad system description line $.")
  664. X                if $loglvl > 5;
  665. X            next;    # Ignore, but it may corrupt further processing
  666. X        }
  667. X        local($location) = $1;
  668. X        local($archive) = $2;
  669. X        local($compress) = $3;
  670. X        local($patch) = $4;
  671. X        $location =~ s/~\//$cf'home\//;        # ~ expansion
  672. X        $Location{$fullname} = $location;
  673. X        $Archived{$fullname}++ if $archive =~ /^y/;
  674. X        $Compressed{$fullname}++ if $compress =~ /^y/;
  675. X        $Patch_only{$fullname}++ if $patch =~ /^o/;
  676. X        $Maintained{$fullname}++ if $patch =~ /^y|o/;
  677. X        $Patches{$fullname}++ if $patch =~ /^p/;
  678. X    }
  679. X    close DIST;
  680. X}
  681. X
  682. END_OF_FILE
  683.   if test 3366 -ne `wc -c <'agent/pl/distribs.pl'`; then
  684.     echo shar: \"'agent/pl/distribs.pl'\" unpacked with wrong size!
  685.   fi
  686.   # end of 'agent/pl/distribs.pl'
  687. fi
  688. if test -f 'agent/pl/dynload.pl' -a "${1}" != "-c" ; then 
  689.   echo shar: Will not clobber existing file \"'agent/pl/dynload.pl'\"
  690. else
  691.   echo shar: Extracting \"'agent/pl/dynload.pl'\" \(3176 characters\)
  692.   sed "s/^X//" >'agent/pl/dynload.pl' <<'END_OF_FILE'
  693. X;# $Id: dynload.pl,v 3.0 1993/11/29 13:48:40 ram Exp ram $
  694. X;#
  695. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  696. X;#  
  697. X;#  You may redistribute only under the terms of the Artistic License,
  698. X;#  as specified in the README file that comes with the distribution.
  699. X;#  You may reuse parts of this distribution only within the terms of
  700. X;#  that same Artistic License; a copy of which may be found at the root
  701. X;#  of the source tree for mailagent 3.0.
  702. X;#
  703. X;# $Log: dynload.pl,v $
  704. X;# Revision 3.0  1993/11/29  13:48:40  ram
  705. X;# Baseline for mailagent 3.0 netwide release.
  706. X;#
  707. X;# 
  708. X;# Dynamic loading of a file into a given package, with a few extra features,
  709. X;# like having the private mailagent lib prepended automatically to the @INC
  710. X;# array. The %Loaded array records the files which have already been loaded
  711. X;# so that we do not load the same file twice. The key records the package
  712. X;# name and then the file, separated by a ':'.
  713. X#
  714. X# Load function into package
  715. X#
  716. X
  717. Xpackage dynload;
  718. X
  719. X# Load function within a package and returns undef if the package cannot be
  720. X# loaded, 0 if the file was loaded but contained some syntax error and 1 if
  721. X# loading was successful. If the function parameter is also specified, then
  722. X# the file is supposed to define that function, so we make sure it is so.
  723. Xsub load {
  724. X    local($package, $file, $function) = @_;
  725. X    local($key) = "$package:$file";
  726. X    unless ($Loaded{$key}) {                    # No reading attempt made yet
  727. X        local($res) = &parse($package, $file);    # Load and parse file
  728. X        $Loaded{$key} = 0;                        # Mark loading attempt
  729. X        unless (defined($res) && $res) {        # Error
  730. X            return defined($res) ? $res : undef;
  731. X        }
  732. X    }
  733. X
  734. X    if (defined $function) {    # File supposed to have defined a function
  735. X        # Make sure the function is defined by eval'ing a small script in the
  736. X        # context of the package where the file was loaded. Indeed, the package
  737. X        # name is implicit and defaults to that loading package.
  738. X        local($defined);
  739. X        eval("package $package; \$dynload'defined = 1 if defined &$function");
  740. X        unless ($defined) {
  741. X            &'add_log("ERROR script $file did not provide &$function")
  742. X                if $'loglvl;
  743. X            return 0;            # Definition failed
  744. X        }
  745. X    }
  746. X
  747. X    $Loaded{$key} = 1;            # Mark and propagate success
  748. X}
  749. X
  750. X# Load file into memory and parse it. Returns undef if file cannot be loaded,
  751. X# 0 on parsing error and 1 if ok.
  752. Xsub parse {
  753. X    local($package, $file) = @_;
  754. X    unless (open(PERL, $file)) {
  755. X        &'add_log("SYSERR open: $!") if $'loglvl;
  756. X        &'add_log("ERROR cannot load $file into $package") if $'loglvl;
  757. X        return undef;        # Cannot load file
  758. X    }
  759. X    local($body) = ' ' x (-s PERL);        # Pre-extend variable
  760. X    {
  761. X        local($/) = undef;                # Slurp the whole thing
  762. X        $body = <PERL>;                    # Load into memory
  763. X    }
  764. X    close PERL;
  765. X    local(@saved) = @INC;                # Save perl INC path (might change)
  766. X    unshift(@INC, $'privlib);            # Required files first searched there
  767. X    eval "package $package;" . $body;    # Eval code into memory
  768. X    @INC = @saved;                        # Restore original require search path
  769. X    $Loaded{$key} = 0;                    # Be conservative and assume error...
  770. X
  771. X    if (chop($@)) {                # Script has an error
  772. X        &'add_log("ERROR in $file: $@") if $'loglvl;
  773. X        $@ = '';                # Clear error
  774. X        return 0;                # Eval failed
  775. X    }
  776. X    1;        # Ok so far
  777. X}
  778. X
  779. Xpackage main;
  780. X
  781. END_OF_FILE
  782.   if test 3176 -ne `wc -c <'agent/pl/dynload.pl'`; then
  783.     echo shar: \"'agent/pl/dynload.pl'\" unpacked with wrong size!
  784.   fi
  785.   # end of 'agent/pl/dynload.pl'
  786. fi
  787. if test -f 'agent/pl/history.pl' -a "${1}" != "-c" ; then 
  788.   echo shar: Will not clobber existing file \"'agent/pl/history.pl'\"
  789. else
  790.   echo shar: Extracting \"'agent/pl/history.pl'\" \(2514 characters\)
  791.   sed "s/^X//" >'agent/pl/history.pl' <<'END_OF_FILE'
  792. X;# $Id: history.pl,v 3.0 1993/11/29 13:48:50 ram Exp ram $
  793. X;#
  794. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  795. X;#  
  796. X;#  You may redistribute only under the terms of the Artistic License,
  797. X;#  as specified in the README file that comes with the distribution.
  798. X;#  You may reuse parts of this distribution only within the terms of
  799. X;#  that same Artistic License; a copy of which may be found at the root
  800. X;#  of the source tree for mailagent 3.0.
  801. X;#
  802. X;# $Log: history.pl,v $
  803. X;# Revision 3.0  1993/11/29  13:48:50  ram
  804. X;# Baseline for mailagent 3.0 netwide release.
  805. X;#
  806. X;# 
  807. X;# Handle the message history mechanism, which is used to reject duplicates.
  808. X;# Each message-id tag is stored in a file, along with a time-stamp (to enable
  809. X;# its removal after a given period.
  810. X;#
  811. X# Record the message ID of the current message and return 0 if the
  812. X# message was recorded for the first time or if there is no valid message ID.
  813. X# Return 1 if the message was already recorded, and hence was already seen.
  814. Xsub history_record {
  815. X    local($msg_id) = $Header{'Message-Id'};        # Message-ID header
  816. X
  817. X    # If there is no message ID, use the concatenation of date + from fields.
  818. X    if ($msg_id) {
  819. X        # Keep only the ID stored within <> brackets
  820. X        ($msg_id) = $msg_id =~ m|^<(.*)>\s*$|;
  821. X    } else {
  822. X        # Use date + from iff there is a date. We cannot use the from field
  823. X        # alone, obviously!! We also have to ensure there is an '@' in the
  824. X        # message id, which is the case unless the address is in uucp form.
  825. X        $msg_id = $Header{'Date'};
  826. X        local($from, $comment) = &parse_address($Header{'From'});
  827. X        $from =~ s/^([\w-.]+)!([\w-.]+)/@$1:$2/;    # host!user -> @host:user
  828. X        $msg_id .= '.' . $from if $msg_id;
  829. X    }
  830. X    $msg_id =~ s/\s+/./g;            # Suppress all spaces
  831. X    $msg_id =~ s/\(a\)/@/;            # X-400 gateways sometimes use (a) for @
  832. X    return 0 unless $msg_id;        # Cannot record message without an ID
  833. X
  834. X    # Hashing of the message ID is done based on the two first letters of
  835. X    # the host name (assuming message ID has the form whatever@host).
  836. X    local($stamp, $host) = $msg_id =~ m|^(.*)@([.\w]+)|;
  837. X    unless ($stamp) {
  838. X        &add_log("WARNING incorrect message ID <$msg_id>") if $loglvl > 5;
  839. X        return 0;                    # Cannot record message if invalid ID
  840. X    }
  841. X
  842. X    local($time, $line) = &dbr'info($host, 'HISTORY', $stamp);
  843. X    return 0 if $time == -1;                # An error occurred
  844. X    if ($time > 0) {                        # Message already recorded
  845. X        &add_log("history duplicate <$msg_id>") if $loglvl > 6;
  846. X        return 1;
  847. X    }
  848. X    &dbr'update($host, 'HISTORY', 0, $stamp);    # Record message (appending)
  849. X    0;            # First time ever seen
  850. X}
  851. X
  852. END_OF_FILE
  853.   if test 2514 -ne `wc -c <'agent/pl/history.pl'`; then
  854.     echo shar: \"'agent/pl/history.pl'\" unpacked with wrong size!
  855.   fi
  856.   # end of 'agent/pl/history.pl'
  857. fi
  858. if test -f 'agent/pl/mbox.pl' -a "${1}" != "-c" ; then 
  859.   echo shar: Will not clobber existing file \"'agent/pl/mbox.pl'\"
  860. else
  861.   echo shar: Extracting \"'agent/pl/mbox.pl'\" \(2971 characters\)
  862.   sed "s/^X//" >'agent/pl/mbox.pl' <<'END_OF_FILE'
  863. X;# $Id: mbox.pl,v 3.0 1993/11/29 13:49:01 ram Exp ram $
  864. X;#
  865. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  866. X;#  
  867. X;#  You may redistribute only under the terms of the Artistic License,
  868. X;#  as specified in the README file that comes with the distribution.
  869. X;#  You may reuse parts of this distribution only within the terms of
  870. X;#  that same Artistic License; a copy of which may be found at the root
  871. X;#  of the source tree for mailagent 3.0.
  872. X;#
  873. X;# $Log: mbox.pl,v $
  874. X;# Revision 3.0  1993/11/29  13:49:01  ram
  875. X;# Baseline for mailagent 3.0 netwide release.
  876. X;#
  877. X;# 
  878. X;# This package enables the mailagent to incorporate mail from a UNIX-style
  879. X;# mailbox (i.e. those produced by standard mail utilities with a leading From
  880. X;# line stating sender and date) into the mailagent's queue. This will be
  881. X;# especially useful on those sites where users are not allowed to have a
  882. X;# .forward file. By using the -f option on the mailbox in /usr/spool/mail,
  883. X;# mail will be queued and filtered as if it had come from filter via .forward.
  884. Xpackage mbox;
  885. X
  886. X# Get mail from UNIX mailbox and queue each item
  887. Xsub main'mbox_mail {
  888. X    local($mbox) = @_;            # Where mail is stored
  889. X    unless (open(MBOX, "$mbox")) {
  890. X        &'add_log("ERROR cannot open $mbox: $!") if $'loglvl > 1;
  891. X        return -1;                # Failed
  892. X    }
  893. X    local(@buffer);                # Buffer used for look-ahead
  894. X    local(@blanks);                # Trailing blank lines are ignored
  895. X    local(@mail);                # Where mail is stored
  896. X    while (<MBOX>) {
  897. X        chop;
  898. X        if (/^\s*$/ && 0 == @buffer) {
  899. X            push(@blanks, $_);
  900. X            next;                # Remove empty lines before end of mail
  901. X        }
  902. X        if (/^From\s/) {
  903. X            push(@buffer, $_);
  904. X            next;
  905. X        }
  906. X        if (@buffer > 0) {
  907. X            if (/^$/) {
  908. X                &flush(1);        # End of header
  909. X                push(@mail, $_);
  910. X                next;
  911. X            }
  912. X            if (/^[\w\-]+:/) {
  913. X                $last_was_header = 1;
  914. X                push(@buffer, $_);
  915. X                next;
  916. X            }
  917. X            if (/^\s/ && $last_was_header) {
  918. X                push(@buffer, $_);
  919. X                next;
  920. X            }
  921. X            &flush(0);            # Not a header
  922. X            push(@mail, $_);
  923. X            next;
  924. X        }
  925. X        &flush_blanks;
  926. X        push(@mail, $_);
  927. X    }
  928. X    close MBOX;
  929. X    &flush(1);            # Flush mail buffer at end of file
  930. X    &flush_buffer;        # Maybe header was incomplete?
  931. X    &'add_log("WARNING incomplete last mail discarded")
  932. X        if $'loglvl > 5 && @mail > 0;
  933. X    0;                    # Ok (but there might have been some queue problems)
  934. X}
  935. X
  936. X# Flush blanks into @mail
  937. Xsub flush_blanks {
  938. X    return unless @blanks;
  939. X    foreach $blank (@blanks) {
  940. X        push(@mail, $blank);
  941. X    }
  942. X    @blanks = ();
  943. X}
  944. X
  945. X# Flush look-ahead buffer into @mail
  946. Xsub flush_buffer {
  947. X    return unless @buffer;
  948. X    foreach $buffer (@buffer) {
  949. X        push(@mail, $buffer);
  950. X    }
  951. X    @buffer = ();
  952. X}
  953. X
  954. X# Flush mail buffer onto queue
  955. Xsub flush {
  956. X    local($was_header) = @_;    # Did we reach a new header
  957. X    # NB: we don't have to worry if the very first mail does not have a From
  958. X    # line, as qmail will add a faked one if necessary.
  959. X    if ($was_header && @mail > 0) {
  960. X        &main'qmail(*mail);
  961. X        @mail = ();                # Reset mail buffer
  962. X    }
  963. X    &flush_buffer;                # Fill @mail with what we got so far in @buffer
  964. X    @blanks = ();                # Discard trailing blanks
  965. X}
  966. X
  967. Xpackage main;
  968. X
  969. END_OF_FILE
  970.   if test 2971 -ne `wc -c <'agent/pl/mbox.pl'`; then
  971.     echo shar: \"'agent/pl/mbox.pl'\" unpacked with wrong size!
  972.   fi
  973.   # end of 'agent/pl/mbox.pl'
  974. fi
  975. if test -f 'agent/pl/pqueue.pl' -a "${1}" != "-c" ; then 
  976.   echo shar: Will not clobber existing file \"'agent/pl/pqueue.pl'\"
  977. else
  978.   echo shar: Extracting \"'agent/pl/pqueue.pl'\" \(3364 characters\)
  979.   sed "s/^X//" >'agent/pl/pqueue.pl' <<'END_OF_FILE'
  980. X;# $Id: pqueue.pl,v 3.0 1993/11/29 13:49:09 ram Exp ram $
  981. X;#
  982. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  983. X;#  
  984. X;#  You may redistribute only under the terms of the Artistic License,
  985. X;#  as specified in the README file that comes with the distribution.
  986. X;#  You may reuse parts of this distribution only within the terms of
  987. X;#  that same Artistic License; a copy of which may be found at the root
  988. X;#  of the source tree for mailagent 3.0.
  989. X;#
  990. X;# $Log: pqueue.pl,v $
  991. X;# Revision 3.0  1993/11/29  13:49:09  ram
  992. X;# Baseline for mailagent 3.0 netwide release.
  993. X;#
  994. X;# 
  995. X# Process the queue
  996. Xsub pqueue {
  997. X    local($length);                        # Length of message, in bytes
  998. X    undef %waiting;                        # Reset waiting array
  999. X    local(*DIR);                        # File descriptor to list the queue
  1000. X    unless (opendir(DIR, $cf'queue)) {
  1001. X        &add_log("ERROR unable to open $cf'queue: $!") if $loglvl;
  1002. X        return 0;                        # No file processed
  1003. X    }
  1004. X    local(@dir) = readdir DIR;            # Slurp the all directory contents
  1005. X    closedir DIR;
  1006. X
  1007. X    # The qm files are put there by the filter and left in case of error
  1008. X    # Only files older than 30 minutes are re-parsed (because otherwise it
  1009. X    # might have just been queued by the filter). The fm files are normal
  1010. X    # queued file which may be processed immediately.
  1011. X
  1012. X    # Prefix each file name with the queue directory path
  1013. X    local(@files) = grep(s|^fm|$cf'queue/fm|, @dir);
  1014. X    local(@filter_files) = grep(s|^qm|$cf'queue/qm|, @dir);
  1015. X    undef @dir;                            # Directory listing not need any longer
  1016. X
  1017. X    foreach $file (@filter_files) {
  1018. X        ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
  1019. X            $atime,$mtime,$ctime,$blksize,$blocks) = stat($file);
  1020. X        if ((time - $mtime) > 1800) {
  1021. X            # More than 30 minutes -- there must have been a failure
  1022. X            push(@files, $file);        # Add file to the to-be-parsed list
  1023. X        }
  1024. X    }
  1025. X
  1026. X    # In $agent_wait are stored the names of the mails outside the queue
  1027. X    # directory, waiting to be processed.
  1028. X    if (-f "$cf'queue/$agent_wait") {
  1029. X        if (open(WAITING, "$cf'queue/$agent_wait")) {
  1030. X            while (<WAITING>) {
  1031. X                chop;
  1032. X                push(@files, $_);        # Process this file too
  1033. X                $waiting{$_} = 1;        # Record it comes from waiting file
  1034. X            }
  1035. X            close WAITING;
  1036. X        } else {
  1037. X            &add_log("ERROR cannot open $cf'queue/$agent_wait: $!") if $loglvl;
  1038. X        }
  1039. X    }
  1040. X    return 0 unless $#files >= 0;
  1041. X
  1042. X    &add_log("processing the whole queue") if $loglvl > 11;
  1043. X    $processed = 0;
  1044. X    foreach $file (@files) {
  1045. X        &add_log("dealing with $file") if $loglvl > 19;
  1046. X        $file_name = $file;
  1047. X        if ($waiting{$file} && ! -f "$file") {
  1048. X            # We may have already processed this file without having resynced
  1049. X            # agent_wait or the file has been removed.
  1050. X            &add_log ("WARNING could not find $file") if $loglvl > 4;
  1051. X            $waiting{$file} = 0;    # Mark it as processed
  1052. X            next;                    # And skip it
  1053. X        }
  1054. X        if (0 == &analyze_mail($file_name)) {
  1055. X            unlink $file;
  1056. X            ++$processed;
  1057. X            $waiting{$file} = 0 if $waiting{$file};
  1058. X            $file =~ s|.*/(.*)|$1|;    # Keep only basename
  1059. X            $length = $Header{'Length'};
  1060. X            &add_log("FILTERED [$file] $length bytes") if $loglvl > 4;
  1061. X        } else {
  1062. X            $file =~ s|.*/(.*)|$1|;    # Keep only basename
  1063. X            &add_log("ERROR leaving [$file] in queue") if $loglvl > 0;
  1064. X            unlink $lockfile;
  1065. X            &resync;                # Resynchronize waiting file
  1066. X            exit 0;                    # Do not continue now
  1067. X        }
  1068. X    }
  1069. X    if ($processed == 0) {
  1070. X        &add_log("was unable to process queue") if $loglvl > 5;
  1071. X    }
  1072. X    &resync;            # Resynchronize waiting file
  1073. X    $processed;            # Return the number of files processed
  1074. X}
  1075. X
  1076. END_OF_FILE
  1077.   if test 3364 -ne `wc -c <'agent/pl/pqueue.pl'`; then
  1078.     echo shar: \"'agent/pl/pqueue.pl'\" unpacked with wrong size!
  1079.   fi
  1080.   # end of 'agent/pl/pqueue.pl'
  1081. fi
  1082. if test -f 'agent/pl/secure.pl' -a "${1}" != "-c" ; then 
  1083.   echo shar: Will not clobber existing file \"'agent/pl/secure.pl'\"
  1084. else
  1085.   echo shar: Extracting \"'agent/pl/secure.pl'\" \(2890 characters\)
  1086.   sed "s/^X//" >'agent/pl/secure.pl' <<'END_OF_FILE'
  1087. X;# $Id: secure.pl,v 3.0 1993/11/29 13:49:16 ram Exp ram $
  1088. X;#
  1089. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  1090. X;#  
  1091. X;#  You may redistribute only under the terms of the Artistic License,
  1092. X;#  as specified in the README file that comes with the distribution.
  1093. X;#  You may reuse parts of this distribution only within the terms of
  1094. X;#  that same Artistic License; a copy of which may be found at the root
  1095. X;#  of the source tree for mailagent 3.0.
  1096. X;#
  1097. X;# $Log: secure.pl,v $
  1098. X;# Revision 3.0  1993/11/29  13:49:16  ram
  1099. X;# Baseline for mailagent 3.0 netwide release.
  1100. X;#
  1101. X;# 
  1102. X# A file "secure" if it is owned by the user and not world writable. Some key
  1103. X# file within the mailagent have to be kept secure or they might compromise the
  1104. X# security of the user account. Additionally, for 'root' users or if the
  1105. X# 'secure' parameter in the config file is set to ON, checks are made for
  1106. X# group writable files and suspicious directory as well.
  1107. X# Return true if the file is secure or missing, false otherwise.
  1108. Xsub file_secure {
  1109. X    local($file, $type) = @_;    # File to be checked
  1110. X    return 1 unless -e $file;    # Missing file considered secure
  1111. X    if (-l $file) {                # File is a symbolic link
  1112. X        &add_log("WARNING sensitive $type file $file is a symbolic link")
  1113. X            if $loglvl > 5;
  1114. X        return 0;        # Unsecure file
  1115. X    }
  1116. X    local($ST_MODE) = 2 + $[;    # Field st_mode from inode structure
  1117. X    local($S_IWOTH) = 02;        # Writable by world (no .ph files here)
  1118. X    unless (-O _) {                # Reuse stat info from -e
  1119. X        &add_log("WARNING you do not own $type file $file") if $loglvl > 5;
  1120. X        return 0;        # Unsecure file
  1121. X    }
  1122. X    local($st_mode) = (stat(_))[$ST_MODE];
  1123. X    if ($st_mode & $S_IWOTH) {
  1124. X        &add_log("WARNING $type file is world writable!") if $loglvl > 5;
  1125. X        return 0;        # Unsecure file
  1126. X    }
  1127. X    return 1 unless $cf'secure =~ /on/i || $< == 0;
  1128. X
  1129. X    # Extra checks for secure mode (or if root user). We make sure the
  1130. X    # file is not writable by group and then we conduct the same secure tests
  1131. X    # on the directory itself
  1132. X    local($S_IWGRP) = 020;        # Writable by group
  1133. X    if ($st_mode & $S_IWGRP) {
  1134. X        &add_log("WARNING $type file is group writable!") if $loglvl > 5;
  1135. X        return 0;        # Unsecure file
  1136. X    }
  1137. X    local($dir);        # directory where file is located
  1138. X    $dir = '.' unless ($dir) = ($file =~ m|(.*)/.*|);
  1139. X    unless (-O $dir) {
  1140. X        &add_log("WARNING you do not own directory of $type file")
  1141. X            if $loglvl > 5;
  1142. X        return 0;        # Unsecure directory, therefore unsecure file
  1143. X    }
  1144. X    $st_mode = (stat(_))[$ST_MODE];
  1145. X    if ($st_mode & $S_IWOTH) {
  1146. X        &add_log("WARNING directory of $type file is world writable!")
  1147. X            if $loglvl > 5;
  1148. X        return 0;        # Unsecure directory
  1149. X    }
  1150. X    if ($st_mode & $S_IWGRP) {
  1151. X        &add_log("WARNING directory of $type file is group writable!")
  1152. X            if $loglvl > 5;
  1153. X        return 0;        # Unsecure directory
  1154. X    }
  1155. X    if (-l $dir) {
  1156. X        &add_log("WARNING directory of $type file $file is a symbolic link")
  1157. X            if $loglvl > 5;
  1158. X        return 0;        # Unsecure directory
  1159. X    }
  1160. X
  1161. X    1;        # At last! File is secure...
  1162. X}
  1163. X
  1164. END_OF_FILE
  1165.   if test 2890 -ne `wc -c <'agent/pl/secure.pl'`; then
  1166.     echo shar: \"'agent/pl/secure.pl'\" unpacked with wrong size!
  1167.   fi
  1168.   # end of 'agent/pl/secure.pl'
  1169. fi
  1170. if test -f 'agent/test/README' -a "${1}" != "-c" ; then 
  1171.   echo shar: Will not clobber existing file \"'agent/test/README'\"
  1172. else
  1173.   echo shar: Extracting \"'agent/test/README'\" \(3573 characters\)
  1174.   sed "s/^X//" >'agent/test/README' <<'END_OF_FILE'
  1175. XThis is the root directory for the regression test suite.
  1176. X
  1177. XA regression test suite is not meant to be a validation suite. Rather, it is
  1178. Xused by developpers to make sure nothing breaks between two snapshots or
  1179. Xreleases. Thoroughness is not a requirement, since it only affects the
  1180. Xaccuracy of the test.
  1181. X
  1182. XThe single TEST executable will run the test suite and report any failure.
  1183. XAlthough not every feature of the mailagent is tested, having it pass
  1184. Xthe whole test suite is a Good Thing. Some commands like PROCESS or POST
  1185. Xare not easy to test automatically, but if you can design good tests
  1186. Xfor them, I will be glad to include them.
  1187. X
  1188. XThis set of programs were written quickly, as effeciency or maintainability
  1189. Xwas not the main issue, obviously. I believe they are reasonably well
  1190. Xwritten, making it possible for someone to be able to understand and modify
  1191. Xthem.
  1192. X
  1193. XRunning the whole test suite takes a long time. On my machine with 40 Mb of
  1194. Xmain memory, it requires 12 minutes to complete. It may take a lot longer
  1195. Xif you do not have at least 16 Mb of RAM.
  1196. X
  1197. XThe option -i turns the incremental mode on. This proved really nice to
  1198. Xme when I was writing this suite, as I was able to skip all the successful
  1199. Xtests and focus only on those which failed or the new ones. The -s option will
  1200. Xcause the test suite to stop at the first error. Normally, only failed basic
  1201. Xtests abort the process. The -o option will not restart the tests from scratch,
  1202. Xeven if the mailagent or filter is newer than the current OK file. Option -n
  1203. Xwill test the non-dataloaded version of the mailagent (because of some bugs
  1204. Xwith eval() which cause the dataloaded version to dump core via a segmentation
  1205. Xviolation).
  1206. X
  1207. XI don't know why I spent some time documenting all this, as I don't expect
  1208. Xanybody to have any chance working on this suite. Anyway, it might be nice
  1209. Xknowing that all the successful tests are recorded in an OK file, along
  1210. Xwith the time stamp of the test, so we may re-run those which were updated
  1211. Xsince last run. In the event the mailagent or the filter are modified, the
  1212. Xtests are re-run throughoutfully.
  1213. X
  1214. XThe file 'level' is optional. If present, it gives the default logging level
  1215. Xto be applied when most of the tests are run (i.e. for those who do not require
  1216. Xany special logging level). If absent, no logging will be done (except for
  1217. Xthose tests who do require... etc...). All the tests are performed in the
  1218. X'out' subdirectory, with the user name set to 'nobody'. That may help a lot
  1219. Xwhen testing commands like RUN, as they have the nasty habbit to mail you, the
  1220. Xuser, their output when they fail for whatever reason.
  1221. X
  1222. XThe generic mail used by the test is an automatic answer I got from the
  1223. Xcomp.compilers newsgroup moderator the day I posted my first article to that
  1224. Xgroup. It has no special value, appart from having some constants relative
  1225. Xto it hardwired within the tests themselves. Don't touch it, even to remove
  1226. Xa white space or some tests may fail (particularily GIVE and PIPE, which have
  1227. Xthe output of 'wc' hardwired). On my machine, here is the output of 'wc mail':
  1228. X
  1229. X     34     227    1620  mail
  1230. X
  1231. XIn the event some of the tests do not pass, there is no reason to panic, and
  1232. Xit doesn't necesseratily mean the mailagent has a bug. It is more likely a
  1233. Xcombinaison of perl + dataloading + bugs + memory + moon's position. Try
  1234. Xto run the test suite again, and then one more time. It sometimes helps.
  1235. XAlso try changing the logging level via 'level' to see if it doesn't make
  1236. Xany difference. This is not really rational, but empirical law :-).
  1237. X
  1238. XI think that's all there is to say.
  1239. END_OF_FILE
  1240.   if test 3573 -ne `wc -c <'agent/test/README'`; then
  1241.     echo shar: \"'agent/test/README'\" unpacked with wrong size!
  1242.   fi
  1243.   # end of 'agent/test/README'
  1244. fi
  1245. if test -f 'agent/test/basic/config.t' -a "${1}" != "-c" ; then 
  1246.   echo shar: Will not clobber existing file \"'agent/test/basic/config.t'\"
  1247. else
  1248.   echo shar: Extracting \"'agent/test/basic/config.t'\" \(2697 characters\)
  1249.   sed "s/^X//" >'agent/test/basic/config.t' <<'END_OF_FILE'
  1250. X# This MUST be the first test ever run
  1251. X
  1252. X# $Id: config.t,v 3.0 1993/11/29 13:49:23 ram Exp ram $
  1253. X#
  1254. X#  Copyright (c) 1990-1993, Raphael Manfredi
  1255. X#  
  1256. X#  You may redistribute only under the terms of the Artistic License,
  1257. X#  as specified in the README file that comes with the distribution.
  1258. X#  You may reuse parts of this distribution only within the terms of
  1259. X#  that same Artistic License; a copy of which may be found at the root
  1260. X#  of the source tree for mailagent 3.0.
  1261. X#
  1262. X# $Log: config.t,v $
  1263. X# Revision 3.0  1993/11/29  13:49:23  ram
  1264. X# Baseline for mailagent 3.0 netwide release.
  1265. X#
  1266. X
  1267. Xdo '../pl/init.pl';
  1268. Xdo '../pl/logfile.pl';
  1269. Xchdir '../out' || exit 0;
  1270. Xchop($pwd = `pwd`);
  1271. X$path = $ENV{'PATH'};
  1272. X$host = $ENV{'HOST'};
  1273. X$host =~ s/-/_/g;        # Filter translates '-' into '_' in hostnames
  1274. X$user = $ENV{'USER'};
  1275. Xopen(CONFIG, ">.mailagent") || print "1\n";
  1276. Xprint CONFIG <<EOF;
  1277. Xhome     : $pwd
  1278. Xlevel    : 21            # Undocumented of course
  1279. Xtmpdir   : /tmp
  1280. Xemergdir : $pwd/emerg
  1281. Xtrack    : OFF
  1282. Xpath     : .
  1283. Xp_$host  : .
  1284. Xuser     : $user
  1285. Xname     : Mailagent Test Suite
  1286. Xvacation : OFF
  1287. Xvacfile  : ~/.vacation
  1288. Xvacperiod: 1d
  1289. Xspool    : ~
  1290. Xqueue    : ~/queue        # This is a good test for comments
  1291. Xlogdir   : ~
  1292. Xcontext  : \$spool/context
  1293. Xlog      : agentlog
  1294. Xseq      : .seq
  1295. Xtimezone : PST8PDT
  1296. Xstatfile : \$spool/mailagent.st
  1297. Xrules    : ~/.rules
  1298. Xrulecache: ~/.cache
  1299. Xmaildrop : $pwd            # Do not LEAVE messages in /usr/spool/mail
  1300. Xmailbox  : \$user        # Use config variable, not current perl $user
  1301. Xhash     : dbr
  1302. Xcleanlaps: 1M
  1303. Xautoclean: OFF
  1304. Xagemax   : 1y
  1305. Xcomfile  : \$spool/commands
  1306. Xdistlist : \$spool/distribs
  1307. Xproglist : \$spool/proglist
  1308. Xmaxsize  : 150000
  1309. Xplsave   : \$spool/plsave
  1310. Xauthfile : \$spool/auth
  1311. Xsecure   : ON
  1312. Xsendmail : msend
  1313. Xsendnews : nsend
  1314. XEOF
  1315. Xclose CONFIG;
  1316. X`rm -rf queue emerg`;
  1317. X`mkdir emerg`;
  1318. X$? == 0 || print "2\n";
  1319. X# Use the special undocumented -t option from filter to get HOME directory
  1320. X# via environment instead of /etc/passwd.
  1321. Xopen(FILTER, "|$filter -t >/dev/null 2>&1") || print "3\n";
  1322. Xprint FILTER <<EOF;
  1323. XDummy mail
  1324. XEOF
  1325. Xclose FILTER;
  1326. X$? != 0 || print "4\n";            # No valid queue directory
  1327. X$file = <emerg/*>;
  1328. Xif (-f "$file") {
  1329. X    open(FILE, $file) || print "5\n";
  1330. X    @file = <FILE>;
  1331. X    close FILE;
  1332. X    $file[0] eq "Dummy mail\n" || print "6\n";
  1333. X    unlink "$file";
  1334. X} else {
  1335. X    print "5\n";                # No emergency dump
  1336. X}
  1337. X-s 'agentlog' || print "6\n";    # No logfile or empty
  1338. X&get_log(7);
  1339. X&check_log('FATAL', 8);                # There must be a FATAL
  1340. X&check_log('MTA', 9);                # Filter must think mail is in MTA's queue
  1341. X&check_log('updating PATH', 10);    # Make sure hostname is computed
  1342. X&check_log('unable to queue', 11);    # Filter did not queue mail
  1343. Xunlink 'agentlog';
  1344. X`mkdir queue`;
  1345. X$? == 0 || print "12\n";        # Cannot make queue
  1346. Xprint "0\n";
  1347. END_OF_FILE
  1348.   if test 2697 -ne `wc -c <'agent/test/basic/config.t'`; then
  1349.     echo shar: \"'agent/test/basic/config.t'\" unpacked with wrong size!
  1350.   fi
  1351.   # end of 'agent/test/basic/config.t'
  1352. fi
  1353. if test -f 'agent/test/filter/hook.t' -a "${1}" != "-c" ; then 
  1354.   echo shar: Will not clobber existing file \"'agent/test/filter/hook.t'\"
  1355. else
  1356.   echo shar: Extracting \"'agent/test/filter/hook.t'\" \(2625 characters\)
  1357.   sed "s/^X//" >'agent/test/filter/hook.t' <<'END_OF_FILE'
  1358. X# Test hooking facilities
  1359. X
  1360. X# $Id: hook.t,v 3.0 1993/11/29 13:50:00 ram Exp ram $
  1361. X#
  1362. X#  Copyright (c) 1990-1993, Raphael Manfredi
  1363. X#  
  1364. X#  You may redistribute only under the terms of the Artistic License,
  1365. X#  as specified in the README file that comes with the distribution.
  1366. X#  You may reuse parts of this distribution only within the terms of
  1367. X#  that same Artistic License; a copy of which may be found at the root
  1368. X#  of the source tree for mailagent 3.0.
  1369. X#
  1370. X# $Log: hook.t,v $
  1371. X# Revision 3.0  1993/11/29  13:50:00  ram
  1372. X# Baseline for mailagent 3.0 netwide release.
  1373. X#
  1374. X
  1375. Xdo '../pl/filter.pl';
  1376. Xdo '../pl/logfile.pl';
  1377. Xunlink 'never', 'always', 'always.2', 'always.3';
  1378. Xunlink 'hook.1', 'hook.2', 'hook.3', 'hook.4';
  1379. X
  1380. Xopen(HOOK, '>hook.1') || print "1\n";
  1381. Xprint HOOK <<'EOH';
  1382. X#! /bin/sh
  1383. Xcat > always
  1384. Xexit 0
  1385. XEOH
  1386. Xclose HOOK;
  1387. X
  1388. Xopen(HOOK, '>hook.2') || print "2\n";
  1389. Xprint HOOK <<'EOH';
  1390. X#: deliver
  1391. Xopen(OUT, '>always.2') || exit 1;
  1392. Xprint OUT "$login\n";
  1393. Xclose OUT;
  1394. Xprint "SAVE ~/always; RUN /bin/echo hi! > always.3";
  1395. XEOH
  1396. Xclose HOOK;
  1397. X
  1398. Xopen(HOOK, '>hook.3') || print "3\n";
  1399. Xprint HOOK <<'EOH';
  1400. X#: rules
  1401. X!To: ram { SAVE never };
  1402. X{ SAVE ~/always; RUN /bin/echo hi! > always.3 };
  1403. XEOH
  1404. Xclose HOOK;
  1405. X
  1406. Xopen(HOOK, '>hook.4') || print "29\n";
  1407. Xprint HOOK <<'EOH';
  1408. X#: perl
  1409. X&save("~/always");
  1410. X&run("/bin/echo hi! > always.3");
  1411. XEOH
  1412. Xclose HOOK;
  1413. Xchmod 0544, 'hook.1', 'hook.2', 'hook.3', 'hook.4';
  1414. X
  1415. X&add_header('X-Tag: hook #1');
  1416. X`$cmd`;
  1417. X$? == 0 || print "4\n";
  1418. X-f 'never' && print "5\n";
  1419. X&get_log(6, 'always');
  1420. X&check_log('^To: ram', 7) == 1 || print "8\n";
  1421. X&get_log(9, 'hook.1');
  1422. X¬_log('^To: ram', 10);
  1423. Xunlink 'never', 'always', 'always.2', 'always.3';
  1424. X
  1425. X&replace_header('X-Tag: hook #2');
  1426. X`$cmd`;
  1427. X$? == 0 || print "11\n";
  1428. X-f 'never' && print "12\n";
  1429. X&get_log(13, 'always');
  1430. X&check_log('^To: ram', 14) == 1 || print "15\n";
  1431. X&get_log(16, 'always.3');
  1432. X&check_log('^hi!', 17) == 1 || print "18\n";
  1433. X&get_log(19, 'always.2');
  1434. X&check_log('^compilers-request$', 20);
  1435. Xunlink 'never', 'always', 'always.2', 'always.3';
  1436. X
  1437. X&replace_header('X-Tag: hook #3');
  1438. X`$cmd`;
  1439. X$? == 0 || print "21\n";
  1440. X-f 'never' && print "22\n";
  1441. X&get_log(23, 'always');
  1442. X&check_log('^To: ram', 24) == 1 || print "25\n";
  1443. X&get_log(26, 'always.3');
  1444. X&check_log('^hi!', 27) == 1 || print "28\n";
  1445. Xunlink 'never', 'always', 'always.2', 'always.3';
  1446. X
  1447. X&replace_header('X-Tag: hook #4');
  1448. X`$cmd`;
  1449. X$? == 0 || print "30\n";
  1450. X-f 'never' && print "31\n";
  1451. X&get_log(32, 'always');
  1452. X&check_log('^To: ram', 33) == 1 || print "34\n";
  1453. X&get_log(35, 'always.3');
  1454. X&check_log('^hi!', 36) == 1 || print "37\n";
  1455. X
  1456. Xunlink 'hook.1', 'hook.2', 'hook.3', 'hook.4';
  1457. Xunlink 'never', 'always', 'always.2', 'always.3';
  1458. Xprint "0\n";
  1459. END_OF_FILE
  1460.   if test 2625 -ne `wc -c <'agent/test/filter/hook.t'`; then
  1461.     echo shar: \"'agent/test/filter/hook.t'\" unpacked with wrong size!
  1462.   fi
  1463.   # end of 'agent/test/filter/hook.t'
  1464. fi
  1465. if test -f 'agent/test/misc/usrmac.t' -a "${1}" != "-c" ; then 
  1466.   echo shar: Will not clobber existing file \"'agent/test/misc/usrmac.t'\"
  1467. else
  1468.   echo shar: Extracting \"'agent/test/misc/usrmac.t'\" \(2693 characters\)
  1469.   sed "s/^X//" >'agent/test/misc/usrmac.t' <<'END_OF_FILE'
  1470. X# Test user-defined macros at the perl level
  1471. X# NOTE: this test relies on a working PERL command
  1472. X
  1473. X# $Id: usrmac.t,v 3.0 1993/11/29 13:50:11 ram Exp ram $
  1474. X#
  1475. X#  Copyright (c) 1990-1993, Raphael Manfredi
  1476. X#  
  1477. X#  You may redistribute only under the terms of the Artistic License,
  1478. X#  as specified in the README file that comes with the distribution.
  1479. X#  You may reuse parts of this distribution only within the terms of
  1480. X#  that same Artistic License; a copy of which may be found at the root
  1481. X#  of the source tree for mailagent 3.0.
  1482. X#
  1483. X# $Log: usrmac.t,v $
  1484. X# Revision 3.0  1993/11/29  13:50:11  ram
  1485. X# Baseline for mailagent 3.0 netwide release.
  1486. X#
  1487. X
  1488. Xdo '../pl/cmd.pl';
  1489. Xunlink "$user";
  1490. X
  1491. Xopen(SCRIPT, '>script') || print "1\n";
  1492. Xprint SCRIPT <<'EOC';
  1493. Xsub macfunc {                # Used for function macro substitution
  1494. X    "macfunc $_[0] string";
  1495. X}
  1496. X$macval = 'macval string';    # Used for perl expression macro substitution
  1497. X
  1498. X&usrmac'new('m', 'orig-macro-m', 'SCALAR');
  1499. X&usrmac'push('m', 'this-is-macro-m', 'SCALAR');
  1500. X&usrmac'new('mac1', "\$mailhook'macval", 'CONST');
  1501. X&usrmac'new('mac2', "mailhook'macfunc", 'FN');
  1502. X&substitute(1);
  1503. X
  1504. X&usrmac'new('m', 'this-is-macro-mbis', 'SCALAR');
  1505. X&usrmac'push('mac1', "\$mailhook'macval", 'EXPR');
  1506. X&usrmac'push('mac2', '/bin/sh -c "echo macro %%-[%n]"', 'PROG');
  1507. X$macval = 'macval bis';
  1508. X&substitute(2);
  1509. X
  1510. X&usrmac'pop('mac1');
  1511. X&usrmac'pop('mac2');
  1512. X&usrmac'pop('m');
  1513. X&substitute(3);
  1514. X
  1515. Xsub substitute {
  1516. X    local($num) = @_;
  1517. X    open(TEXT, 'text');
  1518. X    open(OUT, ">subst.$num");
  1519. X    local($_);
  1520. X    while (<TEXT>) {
  1521. X        print OUT &'macros_subst(*_);
  1522. X    }
  1523. X    close OUT;
  1524. X    close TEXT;
  1525. X}
  1526. XEOC
  1527. Xclose SCRIPT;
  1528. X
  1529. Xopen(TEXT, '>text') || print "2\n";
  1530. Xprint TEXT <<'EOT';
  1531. X%%%A%%
  1532. X%N
  1533. X%-m
  1534. X%=vacation
  1535. X%-(mac1)
  1536. X%-(mac2)
  1537. XThis %-m is %-(mac1) and %-(mac2).
  1538. XEOT
  1539. Xclose TEXT;
  1540. X
  1541. X$result1 = <<'EOR';
  1542. X%cambridge.ma.us%
  1543. Xcompilers-request
  1544. Xthis-is-macro-m
  1545. XOFF
  1546. Xmacval string
  1547. Xmacfunc mac2 string
  1548. XThis this-is-macro-m is macval string and macfunc mac2 string.
  1549. XEOR
  1550. X
  1551. X$result2 = <<'EOR';
  1552. X%cambridge.ma.us%
  1553. Xcompilers-request
  1554. Xthis-is-macro-mbis
  1555. XOFF
  1556. Xmacval bis
  1557. Xmacro %-[mac2]
  1558. XThis this-is-macro-mbis is macval bis and macro %-[mac2].
  1559. XEOR
  1560. X
  1561. X$result3 = <<'EOR';
  1562. X%cambridge.ma.us%
  1563. Xcompilers-request
  1564. Xorig-macro-m
  1565. XOFF
  1566. Xmacval string
  1567. Xmacfunc mac2 string
  1568. XThis orig-macro-m is macval string and macfunc mac2 string.
  1569. XEOR
  1570. X
  1571. Xsub verify {
  1572. X    local($file, $result, $error) = @_;
  1573. X    local($var);
  1574. X    $var = `cat $file 2>&1`;
  1575. X    $var eq $result || print "$error\n";
  1576. X}
  1577. X
  1578. X&add_header('X-Tag: usrmac');
  1579. X`$cmd`;
  1580. X$? == 0 || print "3\n";
  1581. X-f "$user" && print "4\n";    # Created only if perl script fails
  1582. X&verify('subst.1', $result1, 5);
  1583. X&verify('subst.2', $result2, 6);
  1584. X&verify('subst.3', $result3, 7);
  1585. Xunlink "$user", 'mail', 'script', 'text', 'subst.1', 'subst.2', 'subst.3';
  1586. Xprint "0\n";
  1587. X
  1588. END_OF_FILE
  1589.   if test 2693 -ne `wc -c <'agent/test/misc/usrmac.t'`; then
  1590.     echo shar: \"'agent/test/misc/usrmac.t'\" unpacked with wrong size!
  1591.   fi
  1592.   # end of 'agent/test/misc/usrmac.t'
  1593. fi
  1594. echo shar: End of archive 20 \(of 26\).
  1595. cp /dev/null ark20isdone
  1596. MISSING=""
  1597. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ; do
  1598.     if test ! -f ark${I}isdone ; then
  1599.     MISSING="${MISSING} ${I}"
  1600.     fi
  1601. done
  1602. if test "${MISSING}" = "" ; then
  1603.     echo You have unpacked all 26 archives.
  1604.     echo "Now run 'sh PACKNOTES', then read README and type Configure.'"
  1605.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1606. else
  1607.     echo You still must unpack the following archives:
  1608.     echo "        " ${MISSING}
  1609. fi
  1610. exit 0
  1611.  
  1612. exit 0 # Just in case...
  1613.