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

  1. Newsgroups: comp.sources.misc
  2. From: Raphael Manfredi <ram@acri.fr>
  3. Subject: v41i013:  mailagent - Flexible mail filtering and processing package, v3.0, Part13/26
  4. Message-ID: <1993Dec2.134009.18793@sparky.sterling.com>
  5. X-Md4-Signature: 636878a5e50ff6738f25e7e7fd1036d4
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Advanced Computer Research Institute, Lyon, France.
  8. Date: Thu, 2 Dec 1993 13:40:09 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: Raphael Manfredi <ram@acri.fr>
  12. Posting-number: Volume 41, Issue 13
  13. Archive-name: mailagent/part13
  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:  agent/maildist.SH agent/man/mailagent.SH.04
  24. #   agent/pl/listqueue.pl agent/pl/runcmd.pl config_h.SH
  25. # Wrapped by ram@soft208 on Mon Nov 29 16:49:56 1993
  26. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  27. echo If this archive is complete, you will see the following message:
  28. echo '          "shar: End of archive 13 (of 26)."'
  29. if test -f 'agent/maildist.SH' -a "${1}" != "-c" ; then 
  30.   echo shar: Will not clobber existing file \"'agent/maildist.SH'\"
  31. else
  32.   echo shar: Extracting \"'agent/maildist.SH'\" \(9883 characters\)
  33.   sed "s/^X//" >'agent/maildist.SH' <<'END_OF_FILE'
  34. Xcase $CONFIG in
  35. X'')
  36. X    if test -f config.sh; then TOP=.;
  37. X    elif test -f ../config.sh; then TOP=..;
  38. X    elif test -f ../../config.sh; then TOP=../..;
  39. X    elif test -f ../../../config.sh; then TOP=../../..;
  40. X    elif test -f ../../../../config.sh; then TOP=../../../..;
  41. X    else
  42. X        echo "Can't find config.sh."; exit 1
  43. X    fi
  44. X    . $TOP/config.sh
  45. X    ;;
  46. Xesac
  47. Xcase "$0" in
  48. X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
  49. Xesac
  50. Xecho "Extracting agent/maildist (with variable substitutions)"
  51. X$spitshell >maildist <<!GROK!THIS!
  52. X$startperl
  53. X    eval "exec perl -S \$0 \$*"
  54. X        if \$running_under_some_shell;
  55. X
  56. X# $Id: maildist.SH,v 3.0 1993/11/29 13:48:23 ram Exp ram $
  57. X#
  58. X#  Copyright (c) 1990-1993, Raphael Manfredi
  59. X#  
  60. X#  You may redistribute only under the terms of the Artistic License,
  61. X#  as specified in the README file that comes with the distribution.
  62. X#  You may reuse parts of this distribution only within the terms of
  63. X#  that same Artistic License; a copy of which may be found at the root
  64. X#  of the source tree for mailagent 3.0.
  65. X#
  66. X# $Log: maildist.SH,v $
  67. X# Revision 3.0  1993/11/29  13:48:23  ram
  68. X# Baseline for mailagent 3.0 netwide release.
  69. X#
  70. X
  71. X\$mversion = '$VERSION';
  72. X\$patchlevel = '$PATCHLEVEL';
  73. X!GROK!THIS!
  74. X
  75. X$spitshell >>maildist <<'!NO!SUBS!'
  76. X
  77. X$prog_name = $0;                # Who I am
  78. X$prog_name =~ s|^.*/(.*)|$1|;    # Keep only base name
  79. X
  80. X&read_config;        # First, read configuration file (in ~/.mailagent)
  81. X
  82. X# take job number and command from environment
  83. X# (passed by mailagent)
  84. X$jobnum = $ENV{'jobnum'};
  85. X$fullcmd = $ENV{'fullcmd'};
  86. X$pack = $ENV{'pack'};
  87. X$path = $ENV{'path'};
  88. X
  89. X&read_dist;            # Read distributions
  90. X
  91. X$dest = shift;        # Who should the system be sent to
  92. X$system = shift;    # Which system
  93. X$version = shift;    # Which version it is
  94. X
  95. X# A single '-' as first argument stands for return path
  96. X$dest = $path if $dest eq '-';
  97. X
  98. X# A single '-' for version means "highest available" version
  99. X$version = $Version{$system} if $version eq '-' || $version eq '';
  100. X
  101. X# Full program's name for H table access
  102. X$pname = $system . "|" . $version;
  103. X
  104. X$maillist = "To obtain a list of what is available, send me the following mail:
  105. X
  106. X    Subject: Command
  107. X    @SH maillist $path
  108. X        ^ note the l";
  109. X
  110. Xif (!$System{$system}) {
  111. X    open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  112. X    print MAILER
  113. X"To: $path
  114. XSubject: No program called $system
  115. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  116. X
  117. XI don't know how to send a program called \"$system\".  Sorry.
  118. X
  119. X$maillist
  120. X
  121. XIf $cf'name can figure out what you meant, you'll get the program anyway.
  122. X
  123. X-- $prog_name speaking for $cf'user
  124. X";
  125. X    close MAILER;
  126. X    &add_log("FAILED (UNKNOWN SYSTEM)") if $loglvl > 1;
  127. X    exit 0;
  128. X}
  129. X
  130. Xif (!$Program{$pname}) {
  131. X    open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  132. X    print MAILER
  133. X"To: $path
  134. XSubject: No version $version for $system
  135. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  136. X
  137. XI don't know how to send version $version of $system.  Sorry.
  138. X
  139. X$maillist
  140. X
  141. XIf $cf'name can figure out what you meant, you'll get the program anyway.
  142. X
  143. X-- $prog_name speaking for $cf'user
  144. X";
  145. X    close MAILER;
  146. X    &add_log("FAILED (BAD VERSION NUMBER)") if $loglvl > 1;
  147. X    exit 0;
  148. X}
  149. X
  150. X# Has the user made a request for an old version (patch only) ?
  151. Xif ($Patch_only{$pname}) {
  152. X    # It is required that patch only systems have a version number
  153. X    &abort("old system has no version number") if $version eq '';
  154. X    open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  155. X    print MAILER
  156. X"To: $path
  157. XSubject: System $system $version is obsolete
  158. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  159. X
  160. XI can't send you version $version of $system. Sorry.
  161. X
  162. XThis version appears to be an old one, and only patches are available.
  163. XThe up-to-date version for $system is $Version{$system}.
  164. X
  165. X$maillist
  166. X
  167. XIf $cf'name can figure out what you meant, he may send you the latest version.
  168. X
  169. X-- $prog_name speaking for $cf'user
  170. X";
  171. X    close MAILER;
  172. X    &add_log("FAILED (PATCH ONLY VERSION)") if $loglvl > 1;
  173. X    exit 0;
  174. X}
  175. X
  176. X# If the request is not the most recent version, warn the user.
  177. Xif ($version < $Version{$system}) {
  178. X    open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  179. X    print MAILER
  180. X"To: $path
  181. XSubject: Version $version of $system is an old one
  182. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  183. X
  184. XYou asked for version $version of $system.
  185. X
  186. XThis version appears to be an old one, but it is sill available, and
  187. XI am currently processing your request. However, I wanted to let you
  188. Xknow that the up-to-date version for $system is $Version{$system}.
  189. X
  190. X$maillist
  191. X
  192. XUnless you receive an error message telling you otherwise, I am sending
  193. Xyou version $version of $system. You may also request for the new version
  194. Xright now if you wish.
  195. X
  196. X-- $prog_name speaking for $cf'user
  197. X";
  198. X    close MAILER;
  199. X    &add_log("MSG old version still available") if $loglvl > 8;
  200. X}
  201. X
  202. X# Create a temporary directory
  203. X$tmp = "$cf'tmpdir/dmd$$";
  204. Xmkdir($tmp, 0700);
  205. X
  206. X# Need to unarchive the distribution
  207. Xif ($Archived{$pname}) {
  208. X    # Create a temporary directory for distribution
  209. X    $tmp_loc = "$cf'tmpdir/dmdl$$";
  210. X    mkdir($tmp_loc, 0700);
  211. X    $location =
  212. X        &unpack($Location{$pname}, $tmp_loc, $Compressed{$pname});
  213. X} else {
  214. X    $location = $Location{$pname};
  215. X}
  216. X
  217. X# Go to top-level directory
  218. Xchdir "$location" ||
  219. X    &abort("cannot go to $location");
  220. X
  221. X# We are now in the place. Look for a MANIFEST file. If none, we will
  222. X# send *everything* held, RCS sub-directories and executable/object files
  223. X# excepted.
  224. X
  225. X$manifest = '';
  226. X$tmp_list = '';
  227. X
  228. Xif (-f 'MANIFEST') {
  229. X    $manifest = "$location/MANIFEST";
  230. X} else {
  231. X    $tmp_list = "$cf'tmpdir/mdlist$$";
  232. X    open(FIND, "find . -type f -print | sort |") ||
  233. X        &abort("cannot run find");
  234. X    open(LIST, ">$tmp_list") ||
  235. X        &abort("cannot create $tmp_list");
  236. X    while (<FIND>) {
  237. X        chop;
  238. X        s|\./||;
  239. X        next if (m|^U/| && -f '.package');    # Skip units if meta-configured
  240. X        next if m|^RCS/|;            # Skip RCS files
  241. X        next if m|/RCS/|;
  242. X        next if m|,v$|;
  243. X        next if m|bugs/|;            # Skip bugs files (patches and al.)
  244. X        next if m|^\.#|;            # Skip [marked for deletion] files
  245. X        next if m|/\.#|;
  246. X        next if m|\.o$|;            # Skip object files
  247. X        next if m|core$|;            # Skip core files
  248. X        next if (-x $_ && -B $_);    # Skip binaries
  249. X        print LIST $_,"\n";            # Keep that file
  250. X    }
  251. X    close FIND;
  252. X    close LIST;
  253. X    $manifest = $tmp_list;
  254. X}
  255. X
  256. X&add_log("manifest is in $manifest") if $loglvl > 19;
  257. X
  258. X# If distribution is maintained by dist 3.0 (at least), there is a .package
  259. X# file in there and we can invoke makedist. Otherwise, we have to do it by
  260. X# hand...
  261. X
  262. Xif (-f '.package') {
  263. X    system "makedist -c $tmp -f $manifest";
  264. X    &abort("cannot run makedist -c $tmp") if $?;
  265. X} else {
  266. X    &makedist;
  267. X}
  268. X
  269. X$subject = "$system";
  270. X$subject .= " $version" if $version ne '0';
  271. X$subject .= " package";
  272. X&sendfile($dest, $tmp, $pack, $subject);
  273. X&clean_tmp;
  274. X
  275. Xexit 0;        # Ok
  276. X
  277. X# Now for each file in manifest, look if there is an
  278. X# RCS file associated with it. If so, check out either
  279. X# the 'lastpat' version or the highest version on the
  280. X# default branch, provided that the file does not exists
  281. X# in checked-out form. Otherwise, only run co if 'lastpat'
  282. X# is defined.
  283. Xsub makedist {
  284. X    chdir $tmp || &abort("cannot chdir to $tmp");
  285. X    open(MANI, $manifest) || &abort("cannot open $manifest");
  286. X    while (<MANI>) {
  287. X        next if /^--/;
  288. X        s|^\s*||;                        # Remove leading spaces
  289. X        ($file,$foo) = split;            # Save filename, discard comments
  290. X        next if (-d "$location/$file");    # Skip directories
  291. X        next if ($file =~ /^\s*$/);        # Skip blank lines
  292. X        # Extract dirname and basename
  293. X        ($dir, $base) = ('', $file)
  294. X            unless ($dir, $base) = ($file =~ m|(.*/)(.*)|);
  295. X        $logmsg = '';                # String to add to log message
  296. X        $rcsfile = 'blurfl';
  297. X        $rcsfile = "$location/$file,v" if (-f "$location/$file,v");
  298. X        $rcsfile = "$location/${dir}RCS/$base,v"
  299. X            if (-f "$location/${dir}RCS/$base,v");
  300. X        next unless -f "$location/$file" || -f "$rcsfile";    # Skip unexisting files
  301. X        &makedir($dir) unless $dir eq '';
  302. X        open(COPY, ">$file") || &abort("cannot create $file");
  303. X        if ($rcsfile ne '') {
  304. X            $rlog = `rlog $rcsfile 2>&1`;
  305. X            ($revs) = ($rlog =~ /lastpat: (\d+)/);
  306. X            if (!$revs) {
  307. X                # Symbol 'lastpat' is not defined
  308. X                # If file exists, open it. Otherwise, run co
  309. X                if (-f "$location/$file") {
  310. X                    $logmsg = " (lastpat undefined)";
  311. X                    $origfile = "$location/$file";
  312. X                    open(FILE, $origfile) ||
  313. X                        &abort("cannot open $origfile");
  314. X                } else {
  315. X                    $logmsg = " (co but no lastpat)";
  316. X                    $origfile = $rcsfile;
  317. X                    open(FILE, "co -q -p $rcsfile |") ||
  318. X                        &abort("cannot run co on $rcsfile");
  319. X                }
  320. X            } else {
  321. X                # Symbol 'lastpat' is defined
  322. X                $logmsg = " (co lastpat)";
  323. X                $origfile = $rcsfile;
  324. X                open(FILE, "co -q -p -rlastpat $rcsfile |") ||
  325. X                    &abort("cannot run co on $rcsfile");
  326. X            }
  327. X        } else {
  328. X            $origfile = "$location/$file";
  329. X            open(FILE, "$location/$file") ||
  330. X                &abort("cannot open $location/$file");
  331. X        }
  332. X        while (<FILE>) {
  333. X            s|$|;      # Remove locker mark
  334. X            (print COPY) || &abort("copy error: $!");
  335. X        }
  336. X        close(FILE) || &abort("copy error: $!");
  337. X        close COPY;
  338. X        &add_log("copied $file$logmsg") if $loglvl > 19;
  339. X
  340. X        # If file is executable, change its permissions
  341. X        if (-x $origfile) {
  342. X            chmod 0755, $file;
  343. X        } else {
  344. X            chmod 0644, $file;
  345. X        }
  346. X    }
  347. X}
  348. X
  349. Xsub clean_tmp {
  350. X    # Do not stay in the directories we are removing...
  351. X    chdir $cf'home;
  352. X    if ($tmp ne '') {
  353. X        system '/bin/rm', '-rf', $tmp;
  354. X        &add_log("removed dir $tmp") if $loglvl > 19;
  355. X    }
  356. X    if ($Archived{$pname}) {
  357. X        system '/bin/rm', '-rf', $tmp_loc;
  358. X        &add_log("removed dir $tmp_loc") if $loglvl > 19;
  359. X    }
  360. X    unlink $tmp_list if $tmp_list ne '';
  361. X}
  362. X
  363. X# Emergency exit with clean-up
  364. Xsub abort {
  365. X    local($reason) = shift(@_);        # Why we are exiting
  366. X    &clean_tmp;
  367. X    &fatal($reason);
  368. X}
  369. X
  370. X!NO!SUBS!
  371. X$grep -v '^;#' pl/makedir.pl >>maildist
  372. X$grep -v '^;#' pl/fatal.pl >>maildist
  373. X$grep -v '^;#' pl/add_log.pl >>maildist
  374. X$grep -v '^;#' pl/read_conf.pl >>maildist
  375. X$grep -v '^;#' pl/unpack.pl >>maildist
  376. X$grep -v '^;#' pl/sendfile.pl >>maildist
  377. X$grep -v '^;#' pl/distribs.pl >>maildist
  378. X$grep -v '^;#' pl/secure.pl >>maildist
  379. Xchmod 755 maildist
  380. X$eunicefix maildist
  381. END_OF_FILE
  382.   if test 9883 -ne `wc -c <'agent/maildist.SH'`; then
  383.     echo shar: \"'agent/maildist.SH'\" unpacked with wrong size!
  384.   fi
  385.   chmod +x 'agent/maildist.SH'
  386.   # end of 'agent/maildist.SH'
  387. fi
  388. if test -f 'agent/man/mailagent.SH.04' -a "${1}" != "-c" ; then 
  389.   echo shar: Will not clobber existing file \"'agent/man/mailagent.SH.04'\"
  390. else
  391.   echo shar: Extracting \"'agent/man/mailagent.SH.04'\" \(14651 characters\)
  392.   sed "s/^X//" >'agent/man/mailagent.SH.04' <<'END_OF_FILE'
  393. Xcareful. Let's assume you choose \fIroot-pass\fR as a password.
  394. X.PP
  395. XEdit \fIpasswd\fR (defined in your \fI~/.mailagent\fR and add the following
  396. Xline:
  397. X.Ex
  398. Xroot:<root-pass>:
  399. X.Ef
  400. Xi.e. enter the password in clear between '<' and '>'. It won't stay in that
  401. Xform for long, but this is the easiest way to bootstrap it. Protect the
  402. X\fIpasswd\fR file tightly (read-write permissions only for you). Then create
  403. Xa \fIpowerdir/root\fR file, protect it the same way and add your e-mail
  404. Xaddress to it, on a line by itself. That \fBmust\fR be the address that
  405. Xwill show up in the \fIFrom:\fR line of your mails. Since clearance files
  406. Xsupport shell-style patterns, you may use \fIlogin@*domain.top\fR to allow
  407. Xmails from your login from any machine in your domain.
  408. X.PP
  409. XYou are almost done. Now simply issue the following command:
  410. X.Ex
  411. XFrom: \fIyour e-mail address\fR
  412. X
  413. Xpower root root-pass
  414. Xpassword root root-pass
  415. X.Ef
  416. Xand feed that to:
  417. X.Ex
  418. Xmailagent -i -e 'SERVER -t'
  419. X.Ef
  420. XThe side effect of re-instantiating your password will be to crypt it in
  421. Xthe \fIpasswd\fR file, so that anybody looking at that file cannot guess
  422. Xyour \fIroot\fR password, hopefully.
  423. X.PP
  424. XOnce you have a valid \fIroot\fR power installed, you may create the
  425. X\fIsystem\fR power by using \fInewpower\fR. Further powers may then be
  426. Xcreated and deleted using the \fIsystem\fR power only.
  427. X.PP
  428. XYou should also create the \fIsecurity\fR power and give it a different
  429. Xpassword than the \fIroot\fR password. This is really needed only if you wish
  430. Xto remotely administrate the server. If you have local access and things
  431. Xget corrupted, it's always possible to change the root password manually by
  432. Xrepeating this bootstrapping sequence.
  433. X.PP
  434. XNote that clearance checks are made using the envelope address of the message,
  435. Xwhich is a little harder to forge than plain header fields like \fISender:\fR.
  436. XThe envelope is extracted by looking at the first header line, which on Unix
  437. Xsystems looks like:
  438. X.Ex
  439. X    From \fIenvelope-address send-date\fR
  440. X.Ef
  441. Xand is inserted by the mail transport agent (MTA). If you are using
  442. X\fIsendmail\fR as the MTA, then only \fItrusted\fR users declared in the
  443. X\fIsendmail.cf\fR file are able to create a "fake" envelope address, a
  444. Xfeature typically used by mailing list dispatchers, since that address is then
  445. Xused as the bounce target in case the mail cannot be delivered.
  446. XIf that first header line is absent, the
  447. Xsender is computed using the \fISender:\fR field if present, then
  448. Xthe \fIFrom:\fR field, but the \fIauth\fR variable is set to false and the
  449. Xserver will not switch into trusted mode; in other words, it will not be
  450. Xpossible to gain powers in that session.
  451. X.PP
  452. XMoreover, since the session transcript is sent to that same envelope address
  453. Xused to authenticate the eligibility for a power, the server feature can
  454. Xhardly be used to retrieve confidential information held at the site where
  455. Xthe \fImailagent\fR is run since the information would be sent to one of the
  456. Xusers cleared for that power. It is the responsibility of you, the user, to
  457. Xmake sure this cannot happen or you could get into legal troubles.
  458. X.PP
  459. XFinally, sensitive commands should be protected by a proper power, and great
  460. Xcare should be taken in writing the command implementation to ensure the
  461. Xsecurity cannot be circumvented. But no, this mailagent feature is not
  462. Xbelieved to be dangerous for the system or site it is used on, since a
  463. Xdetermined user could implement one trivially via a five line shell script.
  464. XIf security is really an issue, \fI.forward\fR files using the piping feature
  465. Xshould be prohibited and access to \fIcron\fR forbidden in order to avoid
  466. Xautomatic mail processing (since it would be possible to have cron invoke
  467. Xa \fImailagent\fR process \-or any other program for that matter\- to process
  468. Xthe incoming mail in a comparable way).
  469. X'''
  470. X.SS Example
  471. X.PP
  472. XHere is an example showing the steps involved in creating a \fIshell\fR
  473. Xcommand, which would take a script by collecting lines until an EOF mark
  474. Xand feed it to a real shell for execution. Since allowing this feature
  475. Xwithout any safeguards would be a real security hole, we protect that by
  476. Xrequesting the power \fIshell\fR before allowing the execution.
  477. X.PP
  478. XHere is my implementation of the \fIshell\fR command (available in the
  479. Xmailagent distribution under \fImisc/shell\fR):
  480. X.Ex
  481. X#!/bin/sh
  482. X
  483. X# Execute commands from stdin, as transmitted by the mailagent server.
  484. X# File descriptor #3 is a channel to the session transcript.
  485. X
  486. X# Make sure we have the shell power.
  487. X# Don't even allow the root power to bypass that for security reasons.
  488. Xcase ":\$powers:" in
  489. X:shell:) ;;
  490. X*)
  491. X    echo "Permission denied." >&3
  492. X    exit 1
  493. X    ;;
  494. Xesac
  495. X
  496. X# Perhaps a shell was defined... Otherwise, use /bin/sh
  497. Xcase "\$shell" in
  498. X'') shell='/bin/sh';;
  499. Xesac
  500. X
  501. X# Normally, a shell command has its output included in the transcript only in
  502. X# case of error or when the user requests the trace. Here however, we need to
  503. X# see what happened, so everything is redirected to the session transcript.
  504. X
  505. Xexec \$shell -x >&3 2>&3
  506. X.Ef
  507. X.PP
  508. XNote how we make access to the \fI\$powers\fR and \fI\$shell\fR environment
  509. Xvariable. That last one is user-defined to allow dynamic set-up of a shell.
  510. X.PP
  511. XAssuming we store that command under \fIservdir/shell.sh\fR (don't forget to
  512. Xadd the execution bit on the file...), here is how
  513. Xwe declare it and its variable in the \fIcomserver\fR file.
  514. X.Ex
  515. Xshell    shell    -    y    -
  516. Xshell    var    -    -    -
  517. X.Ef
  518. XThis example shows that there is a separate name-space for variables and
  519. Xcommands. Moreover, the command bears the same name as its type -- don't let
  520. Xthat confuse you :-).
  521. X.PP
  522. XNow, assuming you have already created a \fIsystem\fR power and protected
  523. Xit with a password (let's assume \fIsys-pass\fR for the purpose of this
  524. Xexample), you need to create the shell power. Although you could do it
  525. Xmanually (like when you handcrafted the \fIroot\fR power), it's better to
  526. Xuse the SERVER interface since it ensures consistency.
  527. X.PP
  528. XIn order to create the \fIshell\fR power required to use the newly created
  529. X\fIshell\fR command, you need to add the following rule to your rule file:
  530. X.Ex
  531. XSubject: Server        { SAVE server; SERVER -t };
  532. X.Ef
  533. Xwhich will save all server mail in a dedicated folder and process them. Note
  534. Xthe \fB\-t\fR option, which allows trusted mode, in which powers may be gained.
  535. XNow send yourself the following mail:
  536. X.Ex
  537. XSubject: Server
  538. Xpower system sys-pass
  539. Xnewpower shell shell-pass
  540. Xram@acri.fr
  541. XEOF
  542. X.Ef
  543. Xwhich requests for the \fIsystem\fR power (needed to created most powers),
  544. Xand then creates a new power \fIshell\fR, assigning \fIshell-pass\fR as its
  545. Xpassword and clearing \fIram@acri.fr\fR for it. Note the here-document
  546. Xfill-in for the newpower command, up to the EOF marker. Of course, you need
  547. Xto replace the address by your real address.
  548. X.PP
  549. XYou will receive a session transcript along these lines:
  550. X.Ex
  551. X    ---- Mailagent session transcript for ram@acri.fr ----
  552. X
  553. X----> power system ********
  554. XOK.
  555. X
  556. X====> newpower shell ********
  557. XOK.
  558. X
  559. X====> --
  560. XEnd of processing (.signature)
  561. X
  562. X    ---- End of mailagent session transcript ----
  563. X.Ef
  564. XNote the concealed passwords, and the prompt change once the system
  565. Xpower has been granted. Since my mailer automatically appends a signature,
  566. Xthe processing stops on it.
  567. X.PP
  568. XNow let's use this new command... Send yourself the following mail:
  569. X.Ex
  570. XSubject: Server
  571. Xset shell /bin/ksh
  572. Xset eof END
  573. Xshell
  574. Xls -l /etc/passwd
  575. XEND
  576. Xpower shell shell-pass
  577. Xshell
  578. Xls -l /etc/passwd
  579. XEND
  580. X.Ef
  581. XIf you everything is right, you should receive back a transcript looking
  582. Xlike this:
  583. X.Ex
  584. X    ---- Mailagent session transcript for ram@acri.fr ----
  585. X
  586. X----> set shell /bin/ksh
  587. XOK.
  588. X
  589. X----> set eof END
  590. XOK.
  591. X
  592. X----> shell
  593. XPermission denied.
  594. XCommand returned a non-zero status (1).
  595. XFAILED.
  596. X
  597. X----> power shell ********
  598. XOK.
  599. X
  600. X====> shell
  601. X+ ls -l /etc/passwd
  602. X-rw-r--r--   1 root     system       691 Oct 01 14:24 /etc/passwd
  603. XOK.
  604. X
  605. X====> --
  606. XEnd of processing (.signature)
  607. X
  608. X    ---- End of mailagent session transcript ----
  609. X.Ef
  610. XThe first invocation of the \fIshell\fR command fails since we lack the
  611. X\fIshell\fR power. The string "Permission denied." is echoed by the
  612. Xcommand itself into file descriptor #3 and makes it to the transcript.
  613. X'''
  614. X.SS Conclusion
  615. X.PP
  616. XThe generic mail server implemented in the mailagent can be used to
  617. Ximplement a mailing list manager, a vote server, an archive server, etc...
  618. XUnfortunately, it does not currently have the notion of state, with a command
  619. Xset dedicated to each state, so it is not possible to implement an intelligent
  620. Xarchive server.
  621. X.PP
  622. XIf you implement new simple server commands and feel they are generic enough
  623. Xto be contributed, please send them to me and I will gladly integrate them.
  624. X.SH EXAMPLES
  625. XHere are some examples of rule files. First, if you do not specify a rule
  626. Xfile or if it is empty, the following built-in rule applies:
  627. X.Ex
  628. XAll: /^Subject: [Cc]ommand/ { LEAVE; PROCESS };
  629. X.Ef
  630. XEvery mail is left in the mailbox. Besides, mail with "Subject: Command"
  631. Xanywhere in the message are processed.
  632. X.PP
  633. XThe following rule file is the one I am currently using:
  634. X.Ex
  635. Xmaildir = ~/mail;
  636. X
  637. XAll: /^Subject: [Cc]ommand/    { SAVE cmds; PROCESS };
  638. X
  639. XTo: gue@eiffel.fr        { POST -l mail.gue };
  640. XApparently-To: ram,
  641. XNewsgroups: mail.gue        { BOUNCE gue@eiffel.fr };
  642. X
  643. X<_SEEN_>
  644. X    Apparently-To: ram,
  645. X    Newsgroups: mail.gue    { DELETE };
  646. X
  647. XFrom: root, To: root        { BEGIN ROOT; REJECT };
  648. X<ROOT> /^Daily run output/    { WRITE ~/var/log/york/daily.%D };
  649. X<ROOT> /^Weekly run output/    { WRITE ~/var/log/york/weekly };
  650. X<ROOT> /^Monthly run output/    { WRITE ~/var/log/york/monthly };
  651. X
  652. XFrom: ram        { BEGIN RAM; REJECT };
  653. X<RAM> To: ram        { LEAVE };
  654. X<RAM> X-Mailer: /mailagent/    { LEAVE };
  655. X<RAM>            { DELETE };
  656. X.Ef
  657. X.PP
  658. XThe folder directory is set to \fI~/mail\fR. All command mails are saved
  659. Xin the folder \fI~/mail/cmds\fR and processed. They do not show up in
  660. Xmy mailbox. Mails directed to the \fIgue\fR mailing list (French Eiffel's
  661. XUsers Group, namely Groupe des Utilisateurs Eiffel) are posted on the local
  662. Xnewsgroup \fImail.gue\fR and do not appear in my mailbox either. Any follow-up
  663. Xmade on this group is mailed to me by \fIinews\fR (and not directly to the
  664. Xmailing list, because those mails would get back to me again and be fed to the
  665. Xnewsgroup, which in turn would have them mailed back to the list, and so on,
  666. Xand so forth).
  667. XHence the next rule which catches those follow-ups and bounces them to the
  668. Xmailing list. Those mails will indeed come back, but the _SEEN_ rule will
  669. Xsimply delete them.
  670. X.PP
  671. XOn my machine, the mails for \fIroot\fR are forwarded to me. However,
  672. Xeveryday, the \fIcron\fR daemon starts some processes to do some
  673. Xadministration clean-up (rotating log files, etc...), and mails the results
  674. Xback. They are redirected into specific folders with the WRITE command, to
  675. Xensure they do not grow up without limit. Note the macro substitution for the
  676. Xdaily output (on Mondays, the output is stored in \fIdaily.1\fR for instance).
  677. X.PP
  678. XThe next group of rules prevents the mail system from sending back mails when
  679. XI am in a group alias expansion. This is a \fIsendmail\fR option which I
  680. Xdisabled on my machine. Care is taken however to keep mails coming from the
  681. Xmailagent which I receive as a blind carbon copy.
  682. X.SH CAVEAT
  683. XIn order to limit the load overhead on the system, only \fBone\fR mailagent
  684. Xprocess is allowed to run the commands. If some new mail arrives while another
  685. Xmailagent is running, that mail is queued and will be processed later by the
  686. X\fImain\fR mailagent.
  687. X.PP
  688. XFor the same reason, the mails sent back by the mailagent are queued by
  689. X\fIsendmail\fR, to avoid the cost of mail transfer while processing commands.
  690. X.SH "SECURITY"
  691. XTo avoid any problems, the \fIfilter\fR will refuse to run if the configuration
  692. Xfile \fI~/.mailagent\fR or the rule file specified are world writable or not
  693. Xowned by the user. Those tests are enforced even if the the \fIfilter\fR does
  694. Xnot run setuid, because they compromise the security of your account.
  695. XThe \fImailagent\fR will also perform those checks.
  696. X.PP
  697. XIndeed, if someone can write into your \fI~/.mailagent\fR file, then he can
  698. Xeasily change your \fIrules\fR configuration parameter to point to another
  699. Xfaked rule file and then send you a mail, which will trigger the mailagent,
  700. Xrunning as you. Via the RUN command, this potential intruder could run any
  701. Xcommand, using your privileges, and could set a Trojan horse for later
  702. Xperusal. Applying the same logic, the rule file must also be protected tightly.
  703. X.PP
  704. XAnd, no surprise, the same rules apply for your \fInewcmd\fR file, which is
  705. Xused to describe extended filtering commands. Otherwise it would allow someone
  706. Xto quietly redefine a commonly used standard command like LEAVE and later
  707. Xbe able to assume your identity.
  708. X.SH FILES
  709. X.PD 0
  710. X.TP 20
  711. X~/.mailagent
  712. Xconfiguration file for mailagent.
  713. X.TP
  714. X~/mbox.filter
  715. Xmailbox used by \fIfilter\fR in case of error
  716. X.TP
  717. X~/mbox.urgent
  718. Xmailbox used by \fImailagent\fR in case of error
  719. X.TP
  720. X~/mbox.<username>
  721. Xmailbox used if writing access is denied in the mail spool directory
  722. X.TP
  723. X$privlibexp/mailagent
  724. Xdirectory holding templates and samples.
  725. X.TP
  726. XLog/agentlog
  727. Xmailagent's log file.
  728. X.TP
  729. XQueue/agent.wait
  730. Xlist of mails waiting to be processed and stored outside of the mailagent's
  731. Xspool directory.
  732. X.TP
  733. XQueue/qmXXXXX
  734. Xmail spooled by \fIfilter\fR.
  735. X.TP
  736. XQueue/fmXXXXX
  737. Xmail spooled by \fImailagent\fR.
  738. X.TP
  739. XHash/X/Y
  740. Xhash files used by RECORD, UNIQUE, ONCE commands and vacation mode.
  741. X.PD
  742. X.SH BUGS
  743. XThere is a small chance that mail arrives while the \fImain\fR mailagent
  744. Xis about to finish its processing. That mail will be queued and not
  745. Xprocessed until another mail arrives (the \fImain\fR mailagent always
  746. Xprocesses the queue after having dealt with the message that invoked it).
  747. X.PP
  748. XA version number must currently contain a dot. Moreover, an old system
  749. X(i.e. a system with an \fIo\fR in the patches column) must have a version
  750. Xnumber, so that the mailagent can compute the name of the directory
  751. Xholding the patches.
  752. X.PP
  753. XThe lock file is deliberately ignored when \fB\-q\fR option is used (in fact,
  754. Xit is ignored whenever an option is specified).
  755. XThis may result in having mails processed more than once.
  756. X.PP
  757. XThe mailagent is at the mercy of any \fIperl\fR bug, and there is little I
  758. Xcan do about it. Some spurious warnings may be emitted by the data-loaded
  759. Xversion, although they do not appear with the plain version.
  760. X.PP
  761. XParsing of the rule file should be done by a real parser and not lexically.
  762. XOr at least, it should be possible to escape otherwise meaningful characters
  763. Xlike ';' or '}' within the rules.
  764. X.SH AUTHOR
  765. XRaphael Manfredi <ram@acri.fr>.
  766. X.SH "SEE ALSO"
  767. Xmaildist($manext), mailhelp($manext), maillist($manext), mailpatch($manext),
  768. Xperl(1).
  769. X!GROK!THIS!
  770. Xchmod 444 mailagent.$manext
  771. END_OF_FILE
  772.   if test 14651 -ne `wc -c <'agent/man/mailagent.SH.04'`; then
  773.     echo shar: \"'agent/man/mailagent.SH.04'\" unpacked with wrong size!
  774.   fi
  775.   # end of 'agent/man/mailagent.SH.04'
  776. fi
  777. if test -f 'agent/pl/listqueue.pl' -a "${1}" != "-c" ; then 
  778.   echo shar: Will not clobber existing file \"'agent/pl/listqueue.pl'\"
  779. else
  780.   echo shar: Extracting \"'agent/pl/listqueue.pl'\" \(5316 characters\)
  781.   sed "s/^X//" >'agent/pl/listqueue.pl' <<'END_OF_FILE'
  782. X;# $Id: listqueue.pl,v 3.0 1993/11/29 13:48:56 ram Exp ram $
  783. X;#
  784. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  785. X;#  
  786. X;#  You may redistribute only under the terms of the Artistic License,
  787. X;#  as specified in the README file that comes with the distribution.
  788. X;#  You may reuse parts of this distribution only within the terms of
  789. X;#  that same Artistic License; a copy of which may be found at the root
  790. X;#  of the source tree for mailagent 3.0.
  791. X;#
  792. X;# $Log: listqueue.pl,v $
  793. X;# Revision 3.0  1993/11/29  13:48:56  ram
  794. X;# Baseline for mailagent 3.0 netwide release.
  795. X;#
  796. X;# 
  797. X# List the current mails held in the queue, if any at all.
  798. X# See also the pqueue subroutine for other comments about the queue.
  799. Xsub list_queue {
  800. X    local(*DIR);
  801. X    unless (opendir(DIR, $cf'queue)) {
  802. X        &add_log("ERROR unable to open $cf'queue: $!");
  803. X        return;
  804. X    }
  805. X    local(@dir) = readdir DIR;        # Slurp the whole directory
  806. X    closedir DIR;
  807. X    local(@files) = grep(s!^(q|f)m!$cf'queue/${1}m!, @dir);
  808. X    undef @dir;
  809. X    if (-f "$cf'queue/$agent_wait") {
  810. X        if (open(WAITING, "$cf'queue/$agent_wait")) {
  811. X            while (<WAITING>) {
  812. X                chop;
  813. X                push(@files, $_);
  814. X            }
  815. X            close WAITING;
  816. X        } else {
  817. X            &add_log("ERROR cannot open $cf'queue/$agent_wait: $!") if $loglvl;
  818. X        }
  819. X    }
  820. X    # The @files array now contains the path name of all the queued mails
  821. X    # (at least those known to the mailagent).
  822. X    if (@files == 0) {
  823. X        print "Mailagent queue is empty.\n";
  824. X        return;
  825. X    }
  826. X    format STDOUT_TOP =
  827. XFilename      Size Queue time  Status    Sender / Recipient list
  828. X--------- -------- ----------- --------- --------------------------------------
  829. X.
  830. X    local($file);                # Base name of file (eventually stripped)
  831. X    local($directory);            # Directory where queued mail is stored
  832. X    local($queued);                # Queuing date
  833. X    local($status);                # Status of mail
  834. X    local($sender);                # Sender of mail
  835. X    local($star);                # The '*' identifies out of queue mails
  836. X    local($recipient);            # Recipient of mail
  837. X    local($buffer);                # Temporary buffer to build recipient list
  838. X    local($address);            # E-mail address candidate for recipient list
  839. X    local(%seen);                # Records addresses already seen
  840. X    $: = " ,";                    # Break recipients on white space or colon
  841. X    format STDOUT =
  842. X@<<<<<<<< @>>>>>>>@@<<<<<<<<<< @<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  843. X$file     $size $star $queued  $status   $sender
  844. X                                         ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  845. X                                         $recipient
  846. X~                                        ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  847. X                                         $recipient
  848. X~                                        ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  849. X                                         $recipient
  850. X~                                        ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  851. X                                         $recipient
  852. X~                                        ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  853. X                                         $recipient
  854. X~                                        ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<...
  855. X                                         $recipient
  856. X.
  857. X    local($n) = $#files + 1;
  858. X    local($s) = $n > 1 ? 's' : '';
  859. X    print STDOUT "                   Mailagent Queue ($n request$s)\n";
  860. X    foreach (@files) {
  861. X        ($directory, $file) = m|^(.*)/(.*)|;
  862. X        &parse_mail($_, 'head_only');
  863. X        next unless defined $Header{'All'};
  864. X        # Remove comments from all the addresses. The From field is used to
  865. X        # identify the (possibly forged) sender while the To and Cc fields
  866. X        # are concatenated to list the recipients;
  867. X        $sender = (&parse_address($Header{'From'}))[0];
  868. X        $buffer = $Header{'To'};
  869. X        $buffer .= ',' . $Header{'Cc'} if $Header{'Cc'};
  870. X        $recipient = '';
  871. X        undef %seen;
  872. X        while ($buffer =~ s/^(.*),(.*)/$1/) {
  873. X            $address = (&parse_address($2))[0];
  874. X            unless ($seen{$address}++) {
  875. X                $recipient .= ', ' if $recipient;
  876. X                $recipient .= $address;
  877. X            }
  878. X        }
  879. X        $address = (&parse_address($buffer))[0];
  880. X        unless ($seen{$address}++) {
  881. X            $recipient .= ', ' if $recipient;
  882. X            $recipient .= $address;
  883. X        }
  884. X        unless (-f $_) {
  885. X            &add_log("WARNING unable to stat $_");
  886. X            next;
  887. X        }
  888. X        ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
  889. X            $atime,$mtime,$ctime,$blksize,$blocks) = stat(_);
  890. X        $status = '';
  891. X        # If file has 'mbox.' as part of its name, then it is an emergency
  892. X        # saving done by the mailagent. If it starts with 'logname', then it
  893. X        # is an emergency saving done by the filter.
  894. X        $file =~ s/^mbox\.// && ($status = 'Backup');
  895. X        $file =~ s/^$cf'user\.// && ($status = 'Backup');
  896. X        if ($file =~ /^qm/ && (time - $mtime) < 1800) {
  897. X            # Queue mails starting with 'qm' have been queued by the filter
  898. X            # program. To avoid race conditions, those mails are skipped for
  899. X            # half an hour (cf to pqueue subroutine).
  900. X            $status = 'Skipped' unless $status;        # Filter queued mail
  901. X        } else {
  902. X            # Processing of mail allowed (mailagent -q would flush it)
  903. X            $status = 'Deferred' unless $status;
  904. X        }
  905. X        ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
  906. X            localtime($mtime);
  907. X        $queued = sprintf("%.2d/%.2d-%.2d:%.2d", ++$mon,$mday,$hour,$min);
  908. X        $queued = 'Now' if (time - $mtime) < 60;
  909. X        $star = '';
  910. X        $star = '*' if $directory ne $cf'queue;    # Spot out-of-queue mails
  911. X        if ((time - $mtime) > 86400) {            # Also spot old mails
  912. X            $star = '#';
  913. X            $star = '@' if $directory ne $cf'queue;
  914. X        }
  915. X        write(STDOUT);
  916. X    }
  917. X}
  918. X
  919. END_OF_FILE
  920.   if test 5316 -ne `wc -c <'agent/pl/listqueue.pl'`; then
  921.     echo shar: \"'agent/pl/listqueue.pl'\" unpacked with wrong size!
  922.   fi
  923.   # end of 'agent/pl/listqueue.pl'
  924. fi
  925. if test -f 'agent/pl/runcmd.pl' -a "${1}" != "-c" ; then 
  926.   echo shar: Will not clobber existing file \"'agent/pl/runcmd.pl'\"
  927. else
  928.   echo shar: Extracting \"'agent/pl/runcmd.pl'\" \(10178 characters\)
  929.   sed "s/^X//" >'agent/pl/runcmd.pl' <<'END_OF_FILE'
  930. X;# $Id: runcmd.pl,v 3.0 1993/11/29 13:49:15 ram Exp ram $
  931. X;#
  932. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  933. X;#  
  934. X;#  You may redistribute only under the terms of the Artistic License,
  935. X;#  as specified in the README file that comes with the distribution.
  936. X;#  You may reuse parts of this distribution only within the terms of
  937. X;#  that same Artistic License; a copy of which may be found at the root
  938. X;#  of the source tree for mailagent 3.0.
  939. X;#
  940. X;# $Log: runcmd.pl,v $
  941. X;# Revision 3.0  1993/11/29  13:49:15  ram
  942. X;# Baseline for mailagent 3.0 netwide release.
  943. X;#
  944. X;# 
  945. X;# Execute the action enclosed in braces. The current working mode 'wmode' is
  946. X;# a local variable defined in analyze_mail. But this variable is visible when
  947. X;# 'xeqte' is called from within it. Thanks perl.
  948. X;#
  949. X;# The following commands are available (case is irrelevent):
  950. X;#  ABORT                    Aborts filtering right away
  951. X;#  ANNOTATE field <value>   Annotation in header a la MH
  952. X;#  APPLY rulefile           Apply an alternate rule file on message
  953. X;#  ASSIGN var <value>       Assign value to the user-defined variable
  954. X;#  BACK <cmd>               Execute <cmd> and eval its output
  955. X;#  BEGIN state              Enter in a new state for analysis
  956. X;#  BOUNCE address(es)       As FORWARD but leave header intact
  957. X;#  DELETE                   Trash the mail away
  958. X;#  FEED program             Same as PASS, but the whole message is given
  959. X;#  FORWARD address(es)      Forwards mail to specified addresses
  960. X;#  GIVE program             Give the body of the message to a program
  961. X;#  KEEP header(s)           Lists the header fields we want to keep
  962. X;#  LEAVE                    Leave mail in incomming mailbox
  963. X;#  MACRO name = (val, type) Define a user macro
  964. X;#  MESSAGE vacation         Sends a vacation-like message back
  965. X;#  NOP                      No operation (useful only with ONCE)
  966. X;#  NOTIFY address message   Notifies address with a given message
  967. X;#  ONCE (period) <cmd>      Executes any other single command once per period
  968. X;#  PASS program             Pass body to program and get new body back
  969. X;#  PERL script              Run script to perform some filtering actions
  970. X;#  PIPE program             Pipes message to program
  971. X;#  POST newsgroup(s)        Post message on specified newsgroups
  972. X;#  PROCESS                  The mailagent processes the commands in body
  973. X;#  PURIFY program           Feed header to program and get new header back
  974. X;#  QUEUE                    Queue mail (counts as save if successful)
  975. X;#  RECORD                   Record message and REJECT in seen mode if present
  976. X;#  REJECT                   Abort execution and continue analysis
  977. X;#  REQUIRE file [package]   Load perl code from file
  978. X;#  RESTART                  Abort execution and restart analysis from scratch
  979. X;#  RESYNC                   Resynchronize header (useful only with FEED)
  980. X;#  RUN program              Run the specified program
  981. X;#  SAVE folder              Saves mail in folder for delayed reading
  982. X;#  SELECT (when) <cmd>      Run command only within certain time period
  983. X;#  SERVER                   Process server commands
  984. X;#  SPLIT folder             Split digest message into folder
  985. X;#  STORE folder             Same as SAVE folder; LEAVE
  986. X;#  STRIP header(s)          Removes the lines from the message's header
  987. X;#  SUBST var //             Apply a substitution on variable
  988. X;#  TR var //                Apply a translation on variable
  989. X;#  UNIQUE                   Delete message if already in history and REJECT
  990. X;#  VACATION on/off          Allow/disallow vacation messages
  991. X;#  WRITE folder             Writes mail in folder (replaces, does not append)
  992. X
  993. X# Split the commands and execute them. This function is the main entry point
  994. X# for nesting level (e.g. execution of commands from BACK are driven by xeqte).
  995. X# We wish to keep track of the execution status of the last command, as does
  996. X# the shell with its $? variable. This is done by $lastcmd.
  997. Xsub xeqte {
  998. X    local($line) = shift(@_);        # Commands to execute
  999. X    local(@cmd);                    # The commands to be ran
  1000. X    local($status) = $FT_CONT;        # Status returned by run_command
  1001. X    local($lastcmd) = 0;            # Failure status from last command
  1002. X    local($_);
  1003. X
  1004. X    # Normally, a ';' separates each action. However, an escaped one as in \;
  1005. X    # must not be taken into account. We also need to escape a single \, in
  1006. X    # case we want a \ followed by a ; grr...
  1007. X    $line =~ s/\\\\/\02/g;            # \\ -> ^B
  1008. X    $line =~ s/\\;/\01/g;            # \; -> ^A
  1009. X    @cmd = split(/;/, $line);        # Put all commands in an array
  1010. X    foreach (@cmd) {                # Now restore orginal escaped sequences
  1011. X        s/\01/;/g;                    # ^A -> ;
  1012. X        s/\02/\\/g;                    # ^B -> \
  1013. X    }
  1014. X
  1015. X    # Now run each command in turn
  1016. X    foreach $cmd (@cmd) {
  1017. X        $status = &run_command($cmd);
  1018. X        last unless $status == $FT_CONT;
  1019. X    }
  1020. X
  1021. X    # Remap $FT_ABORT on $FT_CONT. In effect, we just skipped the remaining
  1022. X    # commands on the line and act as if they had been executed. This indeed
  1023. X    # achieves the ABORT command.
  1024. X    $status = $FT_CONT if $status == $FT_ABORT;
  1025. X    $status;
  1026. X}
  1027. X
  1028. X# Executes a filter command and return continuing status:
  1029. X#  FT_CONT to continue
  1030. X#  FT_REJECT if a reject was found
  1031. X#  FT_RESTART if a restart was found
  1032. X#  FT_ABORT if an abort was found
  1033. Xsub run_command {
  1034. X    local($cmd) = @_;                # Command to be run (passed to subroutines)
  1035. X    local($cmd_name);                # Command name
  1036. X    local($cont) = $FT_CONT;        # Continue by default
  1037. X    local($mfile) = $file_name =~ m|.*/(.*)|;    # Basename of mail file
  1038. X    $mfile = $file_name unless $mfile;            # There was no / in name
  1039. X    $mfile = '<stdin>' unless $mfile;            # No $file_name if from STDIN
  1040. X    ¯os_subst(*cmd);            # Macros substitutions
  1041. X    $cmd =~ s/^\s*//;                # Remove leading spaces
  1042. X    $cmd =~ s/\s*$//;                # And trailing ones
  1043. X    return $cont unless $cmd;        # Ignore null instructions
  1044. X    ($cmd_name) = $cmd =~ /^(\w+)/;
  1045. X    $cmd_name =~ tr/a-z/A-Z/;        # In uppercase from now on
  1046. X    # In the special mode _SEEN_, only a restricted set of action are allowed
  1047. X    if ($wmode eq '_SEEN_') {
  1048. X        if ($Rfilter{$cmd_name}) {
  1049. X            &add_log("WARNING command $cmd_name not allowed") if $loglvl > 5;
  1050. X            return $cont;
  1051. X        }
  1052. X    }
  1053. X    &add_log("XEQ ($cmd)") if $loglvl > 10;
  1054. X    print ">> $cmd\n" if $track_all;        # Option -t
  1055. X    local($routine) = $Filter{$cmd_name};
  1056. X    # Unknown commands default to LEAVE if no save have ever been done.
  1057. X    # Otherwise, they are simply ignored.
  1058. X    unless ($routine) {
  1059. X        local($what) = 'defaults to LEAVE';
  1060. X        $what = 'ignored' if $ever_saved;
  1061. X        &add_log("ERROR unknown command $cmd_name ($what)")
  1062. X            if $loglvl > 1;
  1063. X        $routine = $Filter{'LEAVE'};        # Default action
  1064. X        return $cont if $ever_saved;        # Command ignored
  1065. X    }
  1066. X    local($failed) = eval("&$routine");        # Eval traps all fatal errors
  1067. X    $failed = 1 if &eval_error;                # Make sure eval worked
  1068. X
  1069. X    # If command does not belong to the set of those who do not modify the
  1070. X    # last execution status recorded, then update $lastcmd with the failure
  1071. X    # status.
  1072. X    $lastcmd = $failed unless $Nostatus{$cmd_name};
  1073. X
  1074. X    # Update statistics
  1075. X    unless ($failed) {
  1076. X        &s_action($cmd_name, $wmode);
  1077. X    } else {
  1078. X        &s_failed($cmd_name, $wmode);
  1079. X    }
  1080. X    $cont;                # Continue status
  1081. X}
  1082. X
  1083. X# Each filter command is handled by a specific function. The Filter array
  1084. X# maps an action name to a subroutine, while the Rfilter array lists the
  1085. X# authorized actions in the special mode _SEEN_ (used when a mail already
  1086. X# filtered is processed).
  1087. X# The %Nostatus array records the commands which do not modify the execution
  1088. X# status recorded by the last command. Typically, those are commands which can
  1089. X# never fail.
  1090. Xsub init_filter {
  1091. X    %Filter = (
  1092. X        'ABORT', 'run_abort',        # Aborts application of filtering rules
  1093. X        'ANNOTATE', 'run_annotate',    # Add new field into header
  1094. X        'APPLY', 'run_apply',        # Apply alternate rule file on message
  1095. X        'ASSIGN', 'run_assign',        # Assign value to variable
  1096. X        'BACK', 'run_back',            # Eval feedback
  1097. X        'BEGIN', 'run_begin',        # Enter in a new state
  1098. X        'BOUNCE', 'run_bounce',        # Bounce message
  1099. X        'DELETE', 'run_delete',        # Throw mail away, explicitely
  1100. X        'FEED', 'run_feed',            # Feed back mail through program
  1101. X        'FORWARD', 'run_forward',    # Forward mail
  1102. X        'GIVE', 'run_give',            # Give body to command
  1103. X        'KEEP', 'run_keep',            # Keep only the listed header fields
  1104. X        'LEAVE', 'run_leave',        # Saving in incomming mailbox
  1105. X        'MACRO', 'run_macro',        # Define a user macro
  1106. X        'MESSAGE', 'run_message',    # Send a vacation-like file
  1107. X        'NOP', 'run_nop',            # No operation
  1108. X        'NOTIFY', 'run_notify',        # Notify reception of message
  1109. X        'ONCE', 'run_once',            # Once control
  1110. X        'PASS', 'run_pass',            # Pass body to program with feedback
  1111. X        'PERL', 'run_perl',            # Perform actions from within a perl script
  1112. X        'PIPE', 'run_pipe',            # Pipe message to specified command
  1113. X        'POST', 'run_post',            # Post mail to the net
  1114. X        'PROCESS', 'run_process',    # Mailagent processing
  1115. X        'PURIFY', 'run_purify',        # Purify header through a program
  1116. X        'QUEUE', 'run_queue',        # Queue mail
  1117. X        'RECORD', 'run_record',        # Record message in history
  1118. X        'REJECT', 'run_reject',        # Reject
  1119. X        'REQUIRE', 'run_require',    # Load perl code
  1120. X        'RESTART', 'run_restart',    # Restart
  1121. X        'RESYNC', 'run_resync',        # Resynchronizes the header
  1122. X        'RUN', 'run_run',            # Run specified program
  1123. X        'SAVE', 'run_save',            # Save in a folder
  1124. X        'SELECT', 'run_select',        # Time selection control
  1125. X        'SERVER', 'run_server',        # Server processing
  1126. X        'SPLIT', 'run_split',        # Split digest message
  1127. X        'STORE', 'run_store',        # Save and leave copy in mailbox
  1128. X        'STRIP', 'run_strip',        # Strip some header lines
  1129. X        'SUBST', 'run_subst',        # Substitution on variable
  1130. X        'TR', 'run_tr',                # Translation on variable
  1131. X        'UNIQUE', 'run_unique',        # Delete message if already in history
  1132. X        'VACATION', 'run_vacation',    # Allow or forbid vacation messages
  1133. X        'WRITE', 'run_write',        # Write mail in folder
  1134. X    );
  1135. X    # Restricted filter actions: the commands listed below cannot be
  1136. X    # executed in the special seen mode (in order to avoid loops).
  1137. X    %Rfilter = (
  1138. X        'BACK', 1,
  1139. X        'BOUNCE', 1,
  1140. X        'FEED', 1,
  1141. X        'FORWARD', 1,
  1142. X        'GIVE', 1,
  1143. X        'NOTIFY', 1,
  1144. X        'PASS', 1,
  1145. X        'PIPE', 1,
  1146. X        'POST', 1,
  1147. X        'PURIFY', 1,
  1148. X        'QUEUE', 1,
  1149. X        'RUN', 1,
  1150. X    );
  1151. X    # The following commands do not modify the last status recorded.
  1152. X    %Nostatus = (
  1153. X        'ABORT', 1,
  1154. X        'ASSIGN', 1,
  1155. X        'BEGIN', 1,
  1156. X        'KEEP', 1,
  1157. X        'MACRO', 1,
  1158. X        'NOP', 1,
  1159. X        'REJECT', 1,
  1160. X        'RESTART', 1,
  1161. X        'RESYNC', 1,
  1162. X        'STRIP', 1,
  1163. X        'VACATION', 1,
  1164. X    );
  1165. X}
  1166. X
  1167. END_OF_FILE
  1168.   if test 10178 -ne `wc -c <'agent/pl/runcmd.pl'`; then
  1169.     echo shar: \"'agent/pl/runcmd.pl'\" unpacked with wrong size!
  1170.   fi
  1171.   # end of 'agent/pl/runcmd.pl'
  1172. fi
  1173. if test -f 'config_h.SH' -a "${1}" != "-c" ; then 
  1174.   echo shar: Will not clobber existing file \"'config_h.SH'\"
  1175. else
  1176.   echo shar: Extracting \"'config_h.SH'\" \(10622 characters\)
  1177.   sed "s/^X//" >'config_h.SH' <<'END_OF_FILE'
  1178. Xcase $CONFIG in
  1179. X'')
  1180. X    if test -f config.sh; then TOP=.;
  1181. X    elif test -f ../config.sh; then TOP=..;
  1182. X    elif test -f ../../config.sh; then TOP=../..;
  1183. X    elif test -f ../../../config.sh; then TOP=../../..;
  1184. X    elif test -f ../../../../config.sh; then TOP=../../../..;
  1185. X    else
  1186. X        echo "Can't find config.sh."; exit 1
  1187. X    fi
  1188. X    . $TOP/config.sh
  1189. X    ;;
  1190. Xesac
  1191. Xcase "$0" in
  1192. X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
  1193. Xesac
  1194. Xecho "Extracting config.h (with variable substitutions)"
  1195. Xsed <<!GROK!THIS! >config.h -e 's!^#undef!/\*#define!' -e 's!^#un-def!#undef!'
  1196. X/*
  1197. X * This file was produced by running the config_h.SH script, which
  1198. X * gets its values from config.sh, which is generally produced by
  1199. X * running Configure.
  1200. X *
  1201. X * Feel free to modify any of this as the need arises.  Note, however,
  1202. X * that running config.h.SH again will wipe out any changes you've made.
  1203. X * For a more permanent change edit config.sh and rerun config.h.SH.
  1204. X *
  1205. X * \$Id: config_h.SH,v 3.0 1993/11/29 13:50:28 ram Exp ram $
  1206. X */
  1207. X
  1208. X/* Configuration time: $cf_time
  1209. X * Configured by: $cf_by
  1210. X * Target system: $myuname
  1211. X */
  1212. X
  1213. X#ifndef _config_h_
  1214. X#define _config_h_
  1215. X
  1216. X/* HAS_BCOPY:
  1217. X *    This symbol is defined if the bcopy() routine is available to
  1218. X *    copy blocks of memory.
  1219. X */
  1220. X#$d_bcopy HAS_BCOPY    /**/
  1221. X
  1222. X/* HAS_GETHOSTNAME:
  1223. X *    This symbol, if defined, indicates that the C program may use the
  1224. X *    gethostname() routine to derive the host name.  See also HAS_UNAME
  1225. X *    and PHOSTNAME.
  1226. X */
  1227. X/* HAS_UNAME:
  1228. X *    This symbol, if defined, indicates that the C program may use the
  1229. X *    uname() routine to derive the host name.  See also HAS_GETHOSTNAME
  1230. X *    and PHOSTNAME.
  1231. X */
  1232. X/* PHOSTNAME:
  1233. X *    This symbol, if defined, indicates that the C program may use the
  1234. X *    contents of PHOSTNAME as a command to feed to the popen() routine
  1235. X *    to derive the host name.  See also HAS_GETHOSTNAME and HAS_UNAME.
  1236. X *    Note that the command uses a fully qualified path, so that it is safe
  1237. X *    even if used by a process with super-user privileges.
  1238. X */
  1239. X#$d_gethname HAS_GETHOSTNAME    /**/
  1240. X#$d_uname HAS_UNAME        /**/
  1241. X#$d_phostname PHOSTNAME "$aphostname"    /* How to get the host name */
  1242. X
  1243. X/* HAS_RENAME:
  1244. X *    This symbol, if defined, indicates that the rename routine is available
  1245. X *    to rename files.  Otherwise you should do the unlink(), link(), unlink()
  1246. X *    trick.
  1247. X */
  1248. X#$d_rename HAS_RENAME    /**/
  1249. X
  1250. X/* HAS_INDEX:
  1251. X *    This symbol is defined to indicate that the index()/rindex()
  1252. X *    functions are available for string searching.
  1253. X */
  1254. X#$d_index HAS_INDEX    /**/
  1255. X
  1256. X/* HAS_STRERROR:
  1257. X *    This symbol, if defined, indicates that the strerror routine is
  1258. X *    available to translate error numbers to strings.
  1259. X */
  1260. X/* HAS_SYS_ERRLIST:
  1261. X *    This symbol, if defined, indicates that the sys_errlist array is
  1262. X *    available to translate error numbers to strings. The extern int
  1263. X *    sys_nerr gives the size of that table.
  1264. X */
  1265. X/* HAS_SYS_ERRNOLIST:
  1266. X *    This symbol, if defined, indicates that the sys_errnolist array is
  1267. X *    available to translate an errno code into its symbolic name (e.g.
  1268. X * ENOENT). The extern int sys_nerrno gives the size of that table.
  1269. X */
  1270. X/* strerror:
  1271. X *    This preprocessor symbol is defined as a macro if strerror() is
  1272. X *    not available to translate error numbers to strings but sys_errlist[]
  1273. X *    array is there.
  1274. X */
  1275. X#$d_strerror HAS_STRERROR        /**/
  1276. X#$d_syserrlst HAS_SYS_ERRLIST    /**/
  1277. X#$d_sysernlst HAS_SYS_ERRNOLIST    /**/
  1278. X#$d_strerrm strerror(e) ((e)<0||(e)>=sys_nerr?"unknown":sys_errlist[e]) /**/
  1279. X
  1280. X/* Time_t:
  1281. X *    This symbol holds the type returned by time(). It can be long,
  1282. X *    or time_t on BSD sites (in which case <sys/types.h> should be
  1283. X *    included).
  1284. X */
  1285. X#define Time_t $timetype        /* Time type */
  1286. X
  1287. X/* UNION_WAIT:
  1288. X *    This symbol if defined indicates to the C program that the argument
  1289. X *    for the wait() system call should be declared as 'union wait status'
  1290. X *    instead of 'int status'. You probably need to include <sys/wait.h>
  1291. X *    in the former case (see I_SYSWAIT).
  1292. X */
  1293. X#$d_uwait UNION_WAIT        /**/
  1294. X
  1295. X/* HAS_VFORK:
  1296. X *    This symbol, if defined, indicates that vfork() exists.
  1297. X */
  1298. X#$d_vfork HAS_VFORK    /**/
  1299. X
  1300. X/* Signal_t:
  1301. X *    This symbol's value is either "void" or "int", corresponding to the
  1302. X *    appropriate return type of a signal handler.  Thus, you can declare
  1303. X *    a signal handler using "Signal_t (*handler)()", and define the
  1304. X *    handler using "Signal_t handler(sig)".
  1305. X */
  1306. X#define Signal_t $signal_t    /* Signal handler's return type */
  1307. X
  1308. X/* I_FCNTL:
  1309. X *    This manifest constant tells the C program to include <fcntl.h>.
  1310. X */
  1311. X#$i_fcntl I_FCNTL    /**/
  1312. X
  1313. X/* I_STRING:
  1314. X *    This symbol, if defined, indicates to the C program that it should
  1315. X *    include <string.h> (USG systems) instead of <strings.h> (BSD systems).
  1316. X */
  1317. X#$i_string I_STRING        /**/
  1318. X
  1319. X/* I_SYS_FILE:
  1320. X *    This symbol, if defined, indicates to the C program that it should
  1321. X *    include <sys/file.h> to get definition of R_OK and friends.
  1322. X */
  1323. X#$i_sysfile I_SYS_FILE        /**/
  1324. X
  1325. X/* I_SYS_WAIT:
  1326. X *    This symbol, if defined, indicates to the C program that it should
  1327. X *    include <sys/wait.h>.
  1328. X */
  1329. X#$i_syswait I_SYS_WAIT    /**/
  1330. X
  1331. X/* I_TIME:
  1332. X *    This symbol, if defined, indicates to the C program that it should
  1333. X *    include <time.h>.
  1334. X */
  1335. X/* I_SYS_TIME:
  1336. X *    This symbol, if defined, indicates to the C program that it should
  1337. X *    include <sys/time.h>.
  1338. X */
  1339. X/* I_SYS_TIME_KERNEL:
  1340. X *    This symbol, if defined, indicates to the C program that it should
  1341. X *    include <sys/time.h> with KERNEL defined.
  1342. X */
  1343. X#$i_time I_TIME        /**/
  1344. X#$i_systime I_SYS_TIME        /**/
  1345. X#$i_systimek I_SYS_TIME_KERNEL        /**/
  1346. X
  1347. X/* INTSIZE:
  1348. X *    This symbol contains the size of an int, so that the C preprocessor
  1349. X *    can make decisions based on it.
  1350. X */
  1351. X#define INTSIZE $intsize        /**/
  1352. X
  1353. X/* MYHOSTNAME:
  1354. X *    This symbol contains name of the host the program is going to run on.
  1355. X *    The domain is not kept with hostname, but must be gotten from MYDOMAIN.
  1356. X *    The dot comes with MYDOMAIN, and need not be supplied by the program.
  1357. X *    If gethostname() or uname() exist, MYHOSTNAME may be ignored. If MYDOMAIN
  1358. X *    is not used, MYHOSTNAME will hold the name derived from PHOSTNAME.
  1359. X */
  1360. X#define MYHOSTNAME "$myhostname"        /**/
  1361. X
  1362. X/* PERLPATH:
  1363. X *    This symbol contains the absolute location of the perl interpeter.
  1364. X */
  1365. X#define PERLPATH "$perlpath"        /**/
  1366. X
  1367. X/* Pid_t:
  1368. X *    This symbol holds the type used to declare process ids in the kernel.
  1369. X *    It can be int, uint, pid_t, etc... It may be necessary to include
  1370. X *    <sys/types.h> to get any typedef'ed information.
  1371. X */
  1372. X#define Pid_t $pidtype        /* PID type */
  1373. X
  1374. X/* CAN_PROTOTYPE:
  1375. X *    If defined, this macro indicates that the C compiler can handle
  1376. X *    function prototypes.
  1377. X */
  1378. X/* DOTS:
  1379. X *    This macro is used to specify the ... in function prototypes which
  1380. X *    have arbitrary additional arguments.
  1381. X */
  1382. X/* NXT_ARG:
  1383. X *    This macro is used to separate arguments in the declared argument list.
  1384. X */
  1385. X/* P_FUNC:
  1386. X *    This macro is used to declare "private" (static) functions.
  1387. X *    It takes three arguments: the function type and name, a parenthesized
  1388. X *    traditional (comma separated) argument list, and the declared argument
  1389. X *    list (in which arguments are separated with NXT_ARG, and additional
  1390. X *    arbitrary arguments are specified with DOTS).  For example:
  1391. X *
  1392. X *        P_FUNC(int foo, (bar, baz), int bar NXT_ARG char *baz[])
  1393. X */
  1394. X/* P_FUNC_VOID:
  1395. X *    This macro is used to declare "private" (static) functions that have
  1396. X *    no arguments.  The macro takes one argument: the function type and name.
  1397. X *    For example:
  1398. X *
  1399. X *        P_FUNC_VOID(int subr)
  1400. X */
  1401. X/* V_FUNC:
  1402. X *    This macro is used to declare "public" (non-static) functions.
  1403. X *    It takes three arguments: the function type and name, a parenthesized
  1404. X *    traditional (comma separated) argument list, and the declared argument
  1405. X *    list (in which arguments are separated with NXT_ARG, and additional
  1406. X *    arbitrary arguments are specified with DOTS).  For example:
  1407. X *
  1408. X *        V_FUNC(int main, (argc, argv), int argc NXT_ARG char *argv[])
  1409. X */
  1410. X/* V_FUNC_VOID:
  1411. X *    This macro is used to declare "public" (non-static) functions that have
  1412. X *    no arguments.  The macro takes one argument: the function type and name.
  1413. X *    For example:
  1414. X *
  1415. X *        V_FUNC_VOID(int fork)
  1416. X */
  1417. X/* _:
  1418. X *    This macro is used to declare function parameters for folks who want
  1419. X *    to make declarations with prototypes using a different style than
  1420. X *    the above macros.  Use double parentheses.  For example:
  1421. X *
  1422. X *        int main _((int argc, char *argv[]));
  1423. X */
  1424. X#$prototype    CAN_PROTOTYPE    /**/
  1425. X#ifdef CAN_PROTOTYPE
  1426. X#define    NXT_ARG ,
  1427. X#define    DOTS , ...
  1428. X#define    V_FUNC(name, arglist, args)name(args)
  1429. X#define    P_FUNC(name, arglist, args)static name(args)
  1430. X#define    V_FUNC_VOID(name)name(void)
  1431. X#define    P_FUNC_VOID(name)static name(void)
  1432. X#define    _(args) args
  1433. X#else
  1434. X#define    NXT_ARG ;
  1435. X#define    DOTS
  1436. X#define    V_FUNC(name, arglist, args)name arglist args;
  1437. X#define    P_FUNC(name, arglist, args)static name arglist args;
  1438. X#define    V_FUNC_VOID(name)name()
  1439. X#define    P_FUNC_VOID(name)static name()
  1440. X#define    _(args) ()
  1441. X#endif
  1442. X
  1443. X/* register1:
  1444. X *    This symbol, along with register2, register3, etc. is either the word
  1445. X *    "register" or null, depending on whether the C compiler pays attention
  1446. X *    to this many register declarations.  The intent is that you don't have
  1447. X *    to order your register declarations in the order of importance, so you
  1448. X *    can freely declare register variables in sub-blocks of code and as
  1449. X *    function parameters.  Do not use register<n> more than once per routine.
  1450. X */
  1451. X#define register1 $reg1        /**/
  1452. X#define register2 $reg2        /**/
  1453. X#define register3 $reg3        /**/
  1454. X#define register4 $reg4        /**/
  1455. X#define register5 $reg5        /**/
  1456. X#define register6 $reg6        /**/
  1457. X
  1458. X/* Uid_t:
  1459. X *    This symbol holds the type used to declare user ids in the kernel.
  1460. X *    It can be int, ushort, uid_t, etc... It may be necessary to include
  1461. X *    <sys/types.h> to get any typedef'ed information.
  1462. X */
  1463. X#define Uid_t $uidtype        /* UID type */
  1464. X
  1465. X/* VOIDFLAGS:
  1466. X *    This symbol indicates how much support of the void type is given by this
  1467. X *    compiler.  What various bits mean:
  1468. X *
  1469. X *        1 = supports declaration of void
  1470. X *        2 = supports arrays of pointers to functions returning void
  1471. X *        4 = supports comparisons between pointers to void functions and
  1472. X *            addresses of void functions
  1473. X *        8 = suports declaration of generic void pointers
  1474. X *
  1475. X *    The package designer should define VOIDUSED to indicate the requirements
  1476. X *    of the package.  This can be done either by #defining VOIDUSED before
  1477. X *    including config.h, or by defining defvoidused in Myinit.U.  If the
  1478. X *    latter approach is taken, only those flags will be tested.  If the
  1479. X *    level of void support necessary is not present, defines void to int.
  1480. X */
  1481. X#ifndef VOIDUSED
  1482. X#define VOIDUSED $defvoidused
  1483. X#endif
  1484. X#define VOIDFLAGS $voidflags
  1485. X#if (VOIDFLAGS & VOIDUSED) != VOIDUSED
  1486. X#define void int        /* is void to be avoided? */
  1487. X#define M_VOID            /* Xenix strikes again */
  1488. X#endif
  1489. X
  1490. X#endif
  1491. X!GROK!THIS!
  1492. END_OF_FILE
  1493.   if test 10622 -ne `wc -c <'config_h.SH'`; then
  1494.     echo shar: \"'config_h.SH'\" unpacked with wrong size!
  1495.   fi
  1496.   # end of 'config_h.SH'
  1497. fi
  1498. echo shar: End of archive 13 \(of 26\).
  1499. cp /dev/null ark13isdone
  1500. MISSING=""
  1501. 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
  1502.     if test ! -f ark${I}isdone ; then
  1503.     MISSING="${MISSING} ${I}"
  1504.     fi
  1505. done
  1506. if test "${MISSING}" = "" ; then
  1507.     echo You have unpacked all 26 archives.
  1508.     echo "Now run 'sh PACKNOTES', then read README and type Configure.'"
  1509.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1510. else
  1511.     echo You still must unpack the following archives:
  1512.     echo "        " ${MISSING}
  1513. fi
  1514. exit 0
  1515.  
  1516. exit 0 # Just in case...
  1517.