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

  1. Newsgroups: comp.sources.misc
  2. From: Raphael Manfredi <ram@acri.fr>
  3. Subject: v41i015:  mailagent - Flexible mail filtering and processing package, v3.0, Part15/26
  4. Message-ID: <1993Dec2.134054.18983@sparky.sterling.com>
  5. X-Md4-Signature: ed4ab781aadd5112421b4c0db07b0d8b
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Advanced Computer Research Institute, Lyon, France.
  8. Date: Thu, 2 Dec 1993 13:40:54 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: Raphael Manfredi <ram@acri.fr>
  12. Posting-number: Volume 41, Issue 15
  13. Archive-name: mailagent/part15
  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/examples/rules agent/maillist.SH agent/package.SH
  24. #   agent/pl/file_edit.pl agent/pl/mh.pl agent/pl/power.pl misc/README
  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 15 (of 26)."'
  29. if test -f 'agent/examples/rules' -a "${1}" != "-c" ; then 
  30.   echo shar: Will not clobber existing file \"'agent/examples/rules'\"
  31. else
  32.   echo shar: Extracting \"'agent/examples/rules'\" \(8076 characters\)
  33.   sed "s/^X//" >'agent/examples/rules' <<'END_OF_FILE'
  34. X#
  35. X# Rule file for mailagent
  36. X#
  37. X
  38. X# The 'maildir' variable tells the mailagent where the folders are located.
  39. X# By default, it is set to ~/Mail (because it is a convention used by other
  40. X# mail-related programs), but the author prefers to use ~/mail.
  41. X
  42. Xmaildir = ~/mail;
  43. X
  44. X# The 'mailfilter' variable points to the place where all the loaded files
  45. X# are stored (e.g. loaded patterns or addresses) and is used only when a
  46. X# relative path is specified.
  47. X
  48. Xmailfilter = ~/mail;
  49. X
  50. X# This set of rules catches command mails early in the process.
  51. X# Currently, only the author, whose login name is 'ram', is allowed to use
  52. X# this feature. All others get a message explaining why their command was
  53. X# rejected (random reasons), and then the message is processed normally
  54. X# by the other set of rules. Note how the BEGIN and REJECT commands
  55. X# inefficiently replace the missing if/else structure.
  56. X
  57. XAll: /^Subject:\s*[Cc]ommand/    { BEGIN CMD; REJECT };
  58. X<CMD> From: ram            { STRIP Received; SAVE cmds; PROCESS };
  59. X<CMD> *                    { BEGIN INITIAL; MESSAGE ~/tmp/nocmds; REJECT };
  60. X
  61. X# Here, I am turning a mailing list into a newsgroup by locally posting the
  62. X# messages I get, so that others can read them too. I have configured inews to
  63. X# mail me any follow-up made into this group, and those are caught with the
  64. X# next rule and bounced directly to the mailing list... which will of course
  65. X# resend the message to me. But the BOUNCE operation left an ``X-Filter'' field
  66. X# in the message and the mailagent enters in the special seen mode, recognizing
  67. X# an already filtered message. The third rule then simply deletes those
  68. X# duplicates.
  69. X
  70. XTo Cc: gue@eiffel.fr            { POST -l mail.gue };
  71. XApparently-To: ram,
  72. XNewsgroups: mail.gue            { STRIP Apparently-To; BOUNCE gue@eiffel.fr };
  73. X<_SEEN_> Newsgroups: mail.gue    { DELETE };
  74. X
  75. X# The MH users mailing list. I am the sole reader of this list. In the past,
  76. X# I used to get some duplicate messages, but since I've added the UNIQUE
  77. X# command, I havn't seen any... weird! :-)
  78. X
  79. XTo Cc: /^mh-users@ics.uci.edu$/i
  80. X    { STRIP Received; UNIQUE -a; SAVE comp.mail.mh };
  81. X
  82. X# This mailing list is a digest version of the comp.unix.wizards newsgroups.
  83. X# It is not perfectly RFC-934, but close, so I simply discard the original
  84. X# message and delete the header which is only the table of contents... Well,
  85. X# I'm not sure there hasn't been any changes...
  86. X
  87. XTo Cc: /^unix-wizards@.*brl.mil$/i
  88. X    { STRIP Received; SPLIT -id unix-wiz };
  89. X
  90. X# Those are news from the French embassy, which are forwarded to us "froggies".
  91. X# I am forwarding this list to all the French people who are working in this
  92. X# company (they are all listed in the file ~/mail/frog-list) and I keep a
  93. X# copy for myself, of course.
  94. X
  95. XTo Cc: /^.*frog:;@guvax.georgetown.edu$/i
  96. X    { FORWARD "frog-list"; STRIP Received; SAVE frog };
  97. X
  98. X# This mailing list is not at all RFC-934, but it usually has no headers. The
  99. X# moderator prefers to add some comments at the end of the digest, hence the
  100. X# -w flag, mainly to keep the trailing garbage.
  101. X
  102. XTo Cc: /^magic@crdgw1.ge.com$/i
  103. X    { STRIP Received; SPLIT -eiw magic };
  104. X
  105. X# The following mailing list used to forward messages from many newsgroups,
  106. X# but not all of them are valid now, and Paul Vixie is talking about moving
  107. X# the src list to pa.dec.com. Anyway, I am filtering the messages according
  108. X# to the ``Newsgroups'' field.
  109. X
  110. XTo Cc: /^unix-sources.*@.*brl.mil$/i
  111. X    { STRIP Received; BEGIN SRC; REJECT };
  112. X
  113. X<SRC> Newsgroups:
  114. X        comp.sources.unix,
  115. X        comp.sources.misc        { SAVE unix-src/src }
  116. X        comp.sources.games        { SAVE unix-src/games }
  117. X        comp.sources.x            { SAVE unix-src/x }
  118. X        comp.sources.bugs        { SAVE unix-src/bugs }
  119. X        comp.sources.wanted        { SAVE unix-src/wanted };
  120. X<SRC> *                            { SAVE unix-src/other };
  121. X
  122. X# Other mailing list, with nothing particular. Ftpmail is not really a mailing
  123. X# list, nor is it a valid user name, hence the regular not anchored regular
  124. X# expression.
  125. X
  126. XTo Cc: rdb-interest        { STRIP Received; SAVE rdb };
  127. XFrom: /ftpmail/i        { STRIP Received; SAVE ftp.mail };
  128. X
  129. X# I am working with Harlan Stenn on the dist 3.0 release, and I automatically
  130. X# forward to him every mail with the word ``metaconfig'' in the subject.
  131. X# I avoid mailing him back his own mails though.
  132. X
  133. XFrom: harlan, To Cc: ram    { SAVE dist };
  134. XSubject: /metaconfig/i        { BEGIN DIST; REJECT };
  135. X<DIST> From: harlan            { SAVE dist };
  136. X<DIST>                        { SAVE dist; FORWARD harlan@mumps.pfcs.com };
  137. X
  138. X# This is administrative stuff. I am a system administrator here, among other
  139. X# things, and we have several MIPS machine with a verbose cron daemon. I have
  140. X# set up a /.forward on all those machines (which redirect all the root mail
  141. X# to me) and I filter the output according to the machine name.
  142. X
  143. XFrom: root, To: root            { BEGIN ROOT; REJECT };
  144. X<ROOT> Subject: /host (\w+)/    { ASSIGN host %1; REJECT };
  145. X<ROOT> /^Daily run output/        { WRITE ~/var/log/%#host/daily.%D };
  146. X<ROOT> /^Weekly run output/        { WRITE ~/var/log/%#host/weekly };
  147. X<ROOT> /^Monthly run output/    { WRITE ~/var/log/%#host/monthly };
  148. X
  149. X# I have a cron job every day a 5:00 a.m. which cleans up my mail folders. I
  150. X# am using the cron program from Paul Vixie, hence the rule testing against
  151. X# the ``X-Cron-Cmd'' header. This is a nice feature from Paul's cron.
  152. X
  153. XTo: ram, X-Cron-Cmd: /mhclean/    { WRITE ~/var/log/mh/mh.%D };
  154. X
  155. X# I belong to multiple internal mailing lists at ISE, and when I send a mail
  156. X# to this list, I do not wish to get a copy of it, as I already saved mine
  157. X# via the ``Fcc' field provided by MH. Therefore, I delete everything which
  158. X# comes from me and is not explicitely directed to me, with the exception of
  159. X# the mailgent error messages which I receive as ``Bcc''.
  160. X
  161. XFrom: ram        { BEGIN RAM; REJECT };
  162. X<RAM> To: ram    { LEAVE };
  163. X<RAM> X-Mailer: /mailagent/i    { LEAVE };
  164. X<RAM>            { DELETE };
  165. X
  166. X# Every system-related mail is saved in a special folder. Note that the pattern
  167. X# matching is done in a case insensitive manner because all these patterns are
  168. X# implicit matches on the ``login name'' of the sender.
  169. X
  170. XTo Cc:
  171. X    postmaster,
  172. X    newsmaster,
  173. X    usenet, news,
  174. X    mailer-daemon,
  175. X    uucp, daemon,
  176. X    system,
  177. X    unknown-user    { SAVE admin };
  178. X
  179. X# Mail about the mailagent (sometimes called mail filter, hence the double
  180. X# pattern) is handled specially. I have a special pattern file held in
  181. X# ~/mail/auto-msg/agent.key. Every message which is NOT a reply and has one
  182. X# of those patterns in its body will be automatically replied to, once a week,
  183. X# by sending the message held in ~/mail/auto-msg/agent.msg. In order for me
  184. X# to know that this message has been already "replied-to", I annotate it.
  185. X# Ultimately, the message is dropped in a dedicated folder.
  186. X
  187. XSubject:
  188. X    /mail\s*agent/i,
  189. X    /mail\s*filter/i        { BEGIN AGENT; REJECT };
  190. X<AGENT>
  191. X    Subject: !/^Re:/,
  192. X    Body: "~/mail/auto-msg/agent.key"
  193. X        {
  194. X            ONCE (%r, agent, 1w) REJECT AGENT_MSG;
  195. X            SAVE agent;
  196. X        };
  197. X<AGENT_MSG>
  198. X        {
  199. X            MESSAGE ~/mail/auto-msg/agent.msg;
  200. X            ANNOTATE Auto-Replied: %r;
  201. X            SAVE agent;
  202. X        };
  203. X<AGENT> { SAVE agent };
  204. X
  205. X# Here, I am detecting mails sent by someone at ISE, i.e. mails with the
  206. X# domain name ``eiffel.com'' appended or simply mails with no domain name.
  207. X# I also turn off vacation messages, for when I am away, people at ISE usually
  208. X# know about it :-).
  209. X
  210. XFrom:
  211. X    /^\w+@.*eiffel\.com$/i
  212. X    /^\w+@\w+$/i
  213. X        { BEGIN ISE; STRIP Received; VACATION off; REJECT };
  214. X
  215. X# A mail explicitely sent to me, leave it in the mailbox.
  216. X
  217. X<ISE> To: ram            { LEAVE };
  218. X
  219. X# Various internal mailing list. Note the ``*eiffel*'' pattern which takes care
  220. X# of various aliases including the word ``eiffel'', as in eiffel, eiffelgroup,
  221. X# ueiffel, etc...
  222. X
  223. X<ISE> To Cc:
  224. X    compiler    { SAVE ise/compiler }
  225. X    *eiffel*    { SAVE ise/eiffel }
  226. X    local        { SAVE ise/local };
  227. X
  228. X# Take care of all the "junk" mails. Usually, I check this mailbox once a week.
  229. X# There is never anything interesting in there, trust me...
  230. X
  231. X<ISE>            { SAVE ise/other };
  232. X
  233. X# Finally, mails coming from the outside world are also filtered into specific
  234. X# folders. This ends the rule file. Anything not matched past this point will
  235. X# simply be left in the mailbox.
  236. X
  237. XTo Cc:
  238. X    *eiffel*,
  239. X    users        { SAVE ise/extern }
  240. X    everyone    { SAVE ise/local };
  241. X
  242. X#
  243. X# End of mailagent rules
  244. X#
  245. END_OF_FILE
  246.   if test 8076 -ne `wc -c <'agent/examples/rules'`; then
  247.     echo shar: \"'agent/examples/rules'\" unpacked with wrong size!
  248.   fi
  249.   # end of 'agent/examples/rules'
  250. fi
  251. if test -f 'agent/maillist.SH' -a "${1}" != "-c" ; then 
  252.   echo shar: Will not clobber existing file \"'agent/maillist.SH'\"
  253. else
  254.   echo shar: Extracting \"'agent/maillist.SH'\" \(7866 characters\)
  255.   sed "s/^X//" >'agent/maillist.SH' <<'END_OF_FILE'
  256. Xcase $CONFIG in
  257. X'')
  258. X    if test -f config.sh; then TOP=.;
  259. X    elif test -f ../config.sh; then TOP=..;
  260. X    elif test -f ../../config.sh; then TOP=../..;
  261. X    elif test -f ../../../config.sh; then TOP=../../..;
  262. X    elif test -f ../../../../config.sh; then TOP=../../../..;
  263. X    else
  264. X        echo "Can't find config.sh."; exit 1
  265. X    fi
  266. X    . $TOP/config.sh
  267. X    ;;
  268. Xesac
  269. Xcase "$0" in
  270. X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
  271. Xesac
  272. Xecho "Extracting agent/maillist (with variable substitutions)"
  273. X$spitshell >maillist <<!GROK!THIS!
  274. X$startperl
  275. X    eval "exec perl -S \$0 \$*"
  276. X        if \$running_under_some_shell;
  277. X
  278. X# $Id: maillist.SH,v 3.0 1993/11/29 13:48:24 ram Exp ram $
  279. X#
  280. X#  Copyright (c) 1990-1993, Raphael Manfredi
  281. X#  
  282. X#  You may redistribute only under the terms of the Artistic License,
  283. X#  as specified in the README file that comes with the distribution.
  284. X#  You may reuse parts of this distribution only within the terms of
  285. X#  that same Artistic License; a copy of which may be found at the root
  286. X#  of the source tree for mailagent 3.0.
  287. X#
  288. X# $Log: maillist.SH,v $
  289. X# Revision 3.0  1993/11/29  13:48:24  ram
  290. X# Baseline for mailagent 3.0 netwide release.
  291. X#
  292. X
  293. X\$mversion = '$VERSION';
  294. X\$patchlevel = '$PATCHLEVEL';
  295. X!GROK!THIS!
  296. X
  297. X$spitshell >>maillist <<'!NO!SUBS!'
  298. X
  299. X$prog_name = $0;                # Who I am
  300. X$prog_name =~ s|^.*/(.*)|$1|;    # Keep only base name
  301. X
  302. X&read_config;        # First, read configuration file (in ~/.mailagent)
  303. X
  304. X# take job number and command from environment
  305. X# (passed by mailagent)
  306. X$jobnum = $ENV{'jobnum'};
  307. X$fullcmd = $ENV{'fullcmd'};
  308. X
  309. X$dest=shift;                            # Who should the list to be sent to
  310. X$dest = $ENV{'path'} if $dest eq '';    # If dest was ommitted
  311. X
  312. X# A single '-' as first argument stands for return path
  313. X$dest = $ENV{'path'} if $dest eq '-';
  314. X
  315. X&read_dist;            # Read distributions and descriptions
  316. X
  317. Xopen(INFO, "$cf'proglist") ||
  318. X    &fatal("cannot open description file");
  319. X@sysinfo = <INFO>;
  320. Xclose INFO;
  321. X
  322. X&read_plsave;        # Read patchlevel description file
  323. X        
  324. X$tmp_mail = "$cf'tmpdir/xml$$";
  325. X
  326. Xopen(XHEAD, ">$tmp_mail") || &fatal("cannot create $tmp_mail");
  327. Xprint XHEAD
  328. X"To: $dest
  329. XSubject: List of available distributions
  330. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  331. X
  332. XHere are the different packages available. If you want the whole
  333. Xdistribution, send me the following:
  334. X
  335. X    @SH maildist $dest system version
  336. X
  337. XIf you want patches, use:
  338. X
  339. X    @SH mailpatch $dest system version LIST
  340. X
  341. Xwhere LIST is a list of patches number, separated by spaces, commas,
  342. Xand/or hyphens. Saying 23- means everything from 23 to the end.
  343. X
  344. XDetailed instructions can be obtained by:
  345. X
  346. X    @SH mailhelp $dest
  347. X
  348. X
  349. X";
  350. X
  351. Xforeach $pname (keys %Program) {
  352. X    ($system, $version) = $pname =~ /^(\w+)\|([\w\.]+)*$/;
  353. X    $version = '---' if $version eq '0';
  354. X    $location = $Location{$pname};
  355. X    &add_log("dealing with $system $version") if $loglvl > 19;
  356. X
  357. X    # Look for highest patchlevel (even if not maintained)
  358. X    $tmp = "";            # Temporary directory created
  359. X
  360. X    if ($Archived{$pname}) {
  361. X        unless ($PSystem{$pname}) {
  362. X            # Archive not already listed in 'plsave'. Create a new
  363. X            # entry with a modification time of zero.
  364. X            $PSystem{$pname} = 1;
  365. X            $Patch_level{$pname} = -1;    # Not a valid patch level
  366. X            $Mtime{$pname} = 0;            # Force unpacking of archive
  367. X        }
  368. X
  369. X        # We need to unarchive the directory only if archive
  370. X        # modification time is newer than the one in patchlist
  371. X        local($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime, $mtime,
  372. X            $ctime,$blksize,$blocks) = stat(&expand($location));
  373. X
  374. X        if ($mtime != $Mtime{$pname}) {     # Archive was updated
  375. X            $Mtime{$pname} = $mtime;     # Update mod time in 'plsave'
  376. X            # Create a temporary directory
  377. X            $tmp = "$cf'tmpdir/dml$$";
  378. X            mkdir($tmp, 0700) ||
  379. X                &fatal("cannot create $tmp");
  380. X            # Need to unarchive the distribution
  381. X            $location = &unpack($location, $tmp, $Compressed{$pname});
  382. X            $Patch_level{$pname} = -1;    # Force updating
  383. X        } else {
  384. X            &add_log("no changes in $system $version archive")
  385. X                if $loglvl > 15;
  386. X        }
  387. X
  388. X    } else {
  389. X        # System is not archived
  390. X        $Patch_level{$pname} = -1;        # Force computation
  391. X    }
  392. X
  393. X    if ($Patch_level{$pname} == -1) {
  394. X        # We still don't know wether there is a patchlevel or not...
  395. X        # Go to system directory, and look there.
  396. X        if (!chdir("$location")) {
  397. X            &add_log("ERROR cannot go to $location") if $loglvl;
  398. X            next;
  399. X        }
  400. X        if ($Patch_only{$pname}) {        # Only patches available
  401. X            if ($version eq '') {
  402. X                &add_log("ERROR old system $system has no version number")
  403. X                    if $loglvl;
  404. X                next;
  405. X            }
  406. X            if (!chdir("bugs-$version")) {
  407. X                &add_log("ERROR no bugs-$version dir for $system")
  408. X                    if $loglvl;
  409. X                next;
  410. X            }
  411. X            local($maxnum);
  412. X            # There is no patchlevel to look at -- compute by hand.
  413. X            for ($maxnum = 1; ; $maxnum++) {
  414. X                last unless -f "patch$maxnum" || -f "patch$maxnum.Z";
  415. X            }
  416. X            $maxnum--;        # We've gone too far
  417. X            $Patch_level{$pname} = $maxnum;
  418. X        } elsif (! -f 'patchlevel.h') {
  419. X            &add_log("no patchlevel.h for $system $version") if $loglvl > 17;
  420. X        } elsif (!open(PATCHLEVEL, "patchlevel.h")) {
  421. X            &add_log("cannot open patchlevel.h for $system $version")
  422. X                if $loglvl > 5;
  423. X        } else {
  424. X            while (<PATCHLEVEL>) {
  425. X                if (/.*PATCHLEVEL[ \t]*(\w+)/) {    # May have letters
  426. X                    $Patch_level{$pname} = $1;
  427. X                    last;
  428. X                }
  429. X            }
  430. X            close PATCHLEVEL;
  431. X            if ($Patch_level{$pname} == -1) {
  432. X                &add_log("malformed patchlevel.h for $system $version")
  433. X                    if $loglvl > 5;
  434. X            }
  435. X        }
  436. X    }
  437. X
  438. X    if ($Patch_level{$pname} >= 0) {
  439. X        &add_log("patchlevel is #$Patch_level{$pname} for $system $version")
  440. X            if $loglvl > 18;
  441. X    } else {
  442. X        $Patch_level{$pname} = -2;        # Signals: no patchlevel
  443. X        &add_log("no patchlevel for $system $version") if $loglvl > 18;
  444. X    }
  445. X    
  446. X    &clean_dir;             # Remove tmp directory, if necessary
  447. X
  448. X    # Now look for a description of the package...
  449. X    $describe = "";
  450. X    $found = 0;
  451. X    foreach (@sysinfo) {
  452. X        next if /^\s*#/;    # Skip comments
  453. X        next if /^\s*$/;    # Skip blank lines
  454. X        next if /^\*\s+$system/ && ($found = 1);
  455. X        last if $found && /^---|^\*/;        # Reached end of description
  456. X        $describe .= "X" . $_ if $found;
  457. X    }
  458. X    $* = 1;
  459. X    $describe =~ s/^X/\t/g;        # Indent description
  460. X    $* = 0;
  461. X
  462. X    print XHEAD "System: $system";
  463. X    print XHEAD " version $version" if $version !~ /---/;
  464. X    print XHEAD "\nStatus: ";
  465. X    print XHEAD $Maintained{$pname} ? "maintained" : "not maintained";
  466. X    print XHEAD " (patches only)" if $Patch_only{$pname};
  467. X    print XHEAD " (official patches available)" if $Patches{$pname};
  468. X    print XHEAD "\n";
  469. X    if ($Maintained{$pname}) {
  470. X        if ($Patch_level{$pname} > 0) {
  471. X            print XHEAD "Highest patch: #$Patch_level{$pname}\n";
  472. X        } else {
  473. X            print XHEAD "No patches yet\n";
  474. X        }
  475. X    } else {
  476. X        print XHEAD "Patch level: #$Patch_level{$pname}\n"
  477. X            if $Patch_level{$pname} > 0;
  478. X    }
  479. X    print XHEAD "\n";
  480. X    print XHEAD "$describe\n" if $describe ne '';
  481. X    print XHEAD "\n";
  482. X}
  483. Xprint XHEAD "-- $prog_name speaking for $cf'user\n";
  484. Xclose XHEAD;
  485. X
  486. Xopen(XHEAD, "$tmp_mail") || &fatal("cannot open mail file");
  487. Xopen(MAILER, "|$cf'sendmail $cf'mailopt $dest");
  488. Xwhile (<XHEAD>) {
  489. X    print MAILER;
  490. X}
  491. Xclose MAILER;
  492. Xif ($?) {
  493. X    &add_log("ERROR couldn't send list to $dest") if $loglvl > 0;
  494. X} else {
  495. X    &add_log("SENT list to $dest") if $loglvl > 2;
  496. X}
  497. Xclose XHEAD;
  498. X
  499. X&write_plsave;            # Write new patchlist file
  500. X&clean_tmp;                # Remove temporary dirs/files
  501. Xexit 0;                    # All OK
  502. X
  503. Xsub clean_dir {
  504. X    chdir $cf'home;        # Leave [to be removed directory] first
  505. X    if ($tmp ne '') {
  506. X        system '/bin/rm', '-rf', $tmp if -d "$tmp";
  507. X        &add_log("directory $tmp removed") if $loglvl > 19;
  508. X        $tmp = "";
  509. X    }
  510. X}
  511. X
  512. Xsub clean_tmp {
  513. X    &clean_dir;
  514. X    unlink "$tmp_mail" if -f "$tmp_mail";
  515. X}
  516. X
  517. X!NO!SUBS!
  518. X$grep -v '^;#' pl/fatal.pl >>maillist
  519. X$grep -v '^;#' pl/acs_rqst.pl >>maillist
  520. X$grep -v '^;#' pl/free_file.pl >>maillist
  521. X$grep -v '^;#' pl/add_log.pl >>maillist
  522. X$grep -v '^;#' pl/read_conf.pl >>maillist
  523. X$grep -v '^;#' pl/unpack.pl >>maillist
  524. X$grep -v '^;#' pl/distribs.pl >>maillist
  525. X$grep -v '^;#' pl/checklock.pl >>maillist
  526. X$grep -v '^;#' pl/plsave.pl >>maillist
  527. X$grep -v '^;#' pl/secure.pl >>maillist
  528. Xchmod 755 maillist
  529. X$eunicefix maillist
  530. END_OF_FILE
  531.   if test 7866 -ne `wc -c <'agent/maillist.SH'`; then
  532.     echo shar: \"'agent/maillist.SH'\" unpacked with wrong size!
  533.   fi
  534.   chmod +x 'agent/maillist.SH'
  535.   # end of 'agent/maillist.SH'
  536. fi
  537. if test -f 'agent/package.SH' -a "${1}" != "-c" ; then 
  538.   echo shar: Will not clobber existing file \"'agent/package.SH'\"
  539. else
  540.   echo shar: Extracting \"'agent/package.SH'\" \(8539 characters\)
  541.   sed "s/^X//" >'agent/package.SH' <<'END_OF_FILE'
  542. Xcase $CONFIG in
  543. X'')
  544. X    if test -f config.sh; then TOP=.;
  545. X    elif test -f ../config.sh; then TOP=..;
  546. X    elif test -f ../../config.sh; then TOP=../..;
  547. X    elif test -f ../../../config.sh; then TOP=../../..;
  548. X    elif test -f ../../../../config.sh; then TOP=../../../..;
  549. X    else
  550. X        echo "Can't find config.sh."; exit 1
  551. X    fi
  552. X    . $TOP/config.sh
  553. X    ;;
  554. Xesac
  555. Xcase "$0" in
  556. X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
  557. Xesac
  558. Xecho "Extracting agent/package (with variable substitutions)"
  559. X$spitshell >package <<!GROK!THIS!
  560. X$startperl
  561. X    eval "exec perl -S \$0 \$*"
  562. X        if \$running_under_some_shell;
  563. X
  564. X# $Id: package.SH,v 3.0 1993/11/29 13:48:32 ram Exp ram $
  565. X#
  566. X#  Copyright (c) 1990-1993, Raphael Manfredi
  567. X#  
  568. X#  You may redistribute only under the terms of the Artistic License,
  569. X#  as specified in the README file that comes with the distribution.
  570. X#  You may reuse parts of this distribution only within the terms of
  571. X#  that same Artistic License; a copy of which may be found at the root
  572. X#  of the source tree for mailagent 3.0.
  573. X#
  574. X#  Original Author: Graham Stoney, 1993
  575. X#
  576. X# $Log: package.SH,v $
  577. X# Revision 3.0  1993/11/29  13:48:32  ram
  578. X# Baseline for mailagent 3.0 netwide release.
  579. X#
  580. X# 
  581. X
  582. X\$cat = '$cat';
  583. X\$zcat = '$zcat';
  584. X\$mversion = '$VERSION';
  585. X\$patchlevel = '$PATCHLEVEL';
  586. X!GROK!THIS!
  587. X$spitshell >>package <<'!NO!SUBS!'
  588. X
  589. X$userlist = "users";
  590. X$prog_name = $0;                # Who I am
  591. X$prog_name =~ s|^.*/(.*)|$1|;    # Keep only base name
  592. X
  593. X&read_config;        # First, read configuration file (in ~/.mailagent)
  594. X
  595. X# take job number and command from environment
  596. X# (passed by mailagent)
  597. X$jobnum = $ENV{'jobnum'};
  598. X$fullcmd = $ENV{'fullcmd'};
  599. X$pack = $ENV{'pack'};
  600. X$path = $ENV{'path'};
  601. X
  602. X&read_dist;            # Read distributions
  603. X
  604. X$dest = shift;            # Who should the patches be sent to
  605. X$system = shift;        # Which system do patches belong
  606. X$version = shift;        # Which version it is
  607. X$theirpl = shift;        # which patchlevel they've got
  608. X$request = shift;        # what would they like to ask for
  609. X
  610. X# A single '-' as first argument stands for return path
  611. X$dest = $path if $dest eq '-';
  612. X
  613. X# A single '-' for version means "highest available" version.
  614. X$version = $Version{$system} if $version eq '-';
  615. X
  616. X# Convert empty pl to a dash
  617. X$theirpl = '-' if $theirpl eq '';
  618. X
  619. X# Full name of system for H table access
  620. X$pname = $system . "|" . $version;
  621. X
  622. X$maillist = "To obtain a list of what is available, send me the following mail:
  623. X
  624. X    Subject: Command
  625. X    @SH maillist $path
  626. X        ^ note the l";
  627. X
  628. Xif (!$System{$system}) {
  629. X    open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  630. X    print MAILER
  631. X"To: $path
  632. XSubject: No program called $system
  633. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  634. X
  635. XI have not heard of a program called $system.  Sorry.
  636. X
  637. X-- $prog_name speaking for $cf'user
  638. X";
  639. X    close MAILER;
  640. X    &add_log("FAILED (UNKNOWN SYSTEM)") if $loglvl > 1;
  641. X    exit 0;
  642. X}
  643. X
  644. Xif (!$Program{$pname}) {
  645. X    open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  646. X    print MAILER
  647. X"To: $path
  648. XSubject: No package $system version $version
  649. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  650. X
  651. XI don't know anything about version $version of $system.  Sorry.";
  652. X    if ($Version{$system} ne '') {
  653. X        print MAILER "
  654. X
  655. X[The highest version for $system is $Version{$system}.]";
  656. X        &add_log("MSG highest version is $Version{$system}")
  657. X            if $loglvl > 8;
  658. X    } else {
  659. X        print MAILER "
  660. X
  661. X[There is no version number for $system.]";
  662. X        &add_log("MSG no version number") if $loglvl > 8;
  663. X    }
  664. X    print MAILER "
  665. X
  666. X-- $prog_name speaking for $cf'user
  667. X";
  668. X    close MAILER;
  669. X    &add_log("FAILED (BAD SYSTEM NUMBER)") if $loglvl > 1;
  670. X    exit 0;
  671. X}
  672. X
  673. X# If the request is not for the most recent version, warn the user and abort.
  674. Xif ($version < $Version{$system}) {
  675. X    open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  676. X    print MAILER
  677. X"To: $path
  678. XSubject: Version $version of $system is out-of-date
  679. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  680. X
  681. XThis short note to warn you that $system version $version is not the
  682. Xlattest one available. If you have some interest in $system, I suggest
  683. Xyou upgrade by fetching version $Version{$system} as soon as possible.
  684. X
  685. X$maillist
  686. X
  687. XI did not record you as a $system user since your version is not the
  688. Xone currently maintained.
  689. X
  690. X-- $prog_name speaking for $cf'user
  691. X";
  692. X    close MAILER;
  693. X    &add_log("MSG old version $system $version") if $loglvl > 8;
  694. X    exit 0;
  695. X}
  696. X
  697. Xif (!($Maintained{$pname} || $Patches{$pname})) {
  698. X    open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  699. X    print MAILER
  700. X"To: $path
  701. XSubject: $system version $version is not maintained
  702. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  703. X
  704. XI can't keep you up to date on changes to version $version of $system, because
  705. Xthis code is not maintained by $cf'name.
  706. X
  707. X-- $prog_name speaking for $cf'user
  708. X";
  709. X    close MAILER;
  710. X    &add_log("FAILED (NOT MAINTAINED)") if $loglvl > 1;
  711. X    exit 0;
  712. X}
  713. X
  714. X# decode their request into a status letter.
  715. X# they may be asking to be left alone.
  716. Xif ((($theirpl eq '-') && $request eq '') ||
  717. X    $request eq 'leavealone')        { $leavealone = 1; $letter = 'L'; }
  718. Xelsif ($request eq '')            { $letter = 'U'; }    # just a user
  719. Xelsif ($request eq 'mailpatches')    { $letter = 'M'; }    # want patches
  720. Xelsif ($request eq 'notifypatches')    { $letter = 'N'; }    # notify only
  721. Xelse {
  722. X    open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  723. X    print MAILER
  724. X"To: $path
  725. XSubject: I didn't understand your package command
  726. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  727. X
  728. XYour package command requested `$request', and I don't know what that means.
  729. X
  730. X-- $prog_name speaking for $cf'user
  731. X";
  732. X    close MAILER;
  733. X    &add_log("FAILED (BAD REQUEST)") if $loglvl > 1;
  734. X    exit 0;
  735. X}
  736. X
  737. X# Go to the system directory.
  738. Xchdir "$Location{$pname}" || &abort("cannot go to $Location{$pname}");
  739. Xopen(PATCHLEVEL, "patchlevel.h") || &abort("cannot open patchlevel.h");
  740. X$maxnum = 0;
  741. Xwhile (<PATCHLEVEL>) {
  742. X    if (/.*PATCHLEVEL[ \t]*(\d+)/) {
  743. X        $maxnum = $1;
  744. X        last;
  745. X    }
  746. X}
  747. Xclose PATCHLEVEL;
  748. X
  749. X# if they have Configured a patchlevel which is not the latest, let them know.
  750. Xif (!$leavealone && $theirpl ne '-' && $maxnum ne $theirpl) {
  751. X    $upgrade = $theirpl + 1;
  752. X
  753. X    # In fact, if they've asked for patch mailing, send it directly. This
  754. X    # works because our environment, set up by mailagent, will be propagated
  755. X    # to the mailpatch command as-is.
  756. X    if ($letter eq 'M') {
  757. X        system('mailpatch', $dest, $system, $version, "$upgrade-");
  758. X        if ($? == 0) {
  759. X            &add_log("MAILED missing patches for $system $version to $dest")
  760. X                if $loglvl > 6;
  761. X        } else {
  762. X            &add_log("WARNING unable to mail patches for $system $version")
  763. X                if $loglvl > 1;
  764. X        }
  765. X    } else {
  766. X        open(MAILER, "|$cf'sendmail $cf'mailopt $path $cf'user");
  767. X        print MAILER
  768. X"To: $dest
  769. XSubject: The latest patchlevel for $system version $version is $maxnum
  770. XX-Mailer: mailagent [version $mversion PL$patchlevel]
  771. X
  772. XJust a quick note to let you know that the latest patchlevel for $system
  773. Xversion $version is $maxnum; if you are still at patchlevel $theirpl, I strongly
  774. Xsuggest you upgrade by applying the more recent patches.
  775. X
  776. XYou can fetch these automatically by sending me the following mail:
  777. X
  778. X    Subject: Command
  779. X    @SH mailpatch $dest $system $version $upgrade-
  780. X           ^ note the c
  781. X
  782. X-- $prog_name speaking for $cf'user
  783. X";
  784. X        close MAILER;
  785. X    }
  786. X}
  787. X
  788. X# look for them in the userlist file
  789. Xif (open(USERLIST,"<$userlist")) {
  790. X    while (<USERLIST>) {
  791. X        next if /^#/;
  792. X        chop if /\n$/;
  793. X        ($status, $pl, $name) = split;
  794. X
  795. X        # convert oldstyle user file format (dist 3.0 PL13).
  796. X        unless (defined $name) {
  797. X            $name = $pl;    # Shift left
  798. X            $pl = '-';
  799. X        }
  800. X
  801. X        # have we heard from them before?
  802. X        if ($name eq $dest) {
  803. X            $found = 1;
  804. X            $status = $letter;
  805. X            $pl = $theirpl if $theirpl ne '-';
  806. X        }
  807. X        push(@status, $status);
  808. X        push(@pl, $pl);
  809. X        push(@name, $name);
  810. X    }
  811. X    close USERLIST;
  812. X}
  813. X
  814. X# add them if they're new.
  815. Xif (!$found) {
  816. X    push(@name, $dest);
  817. X    push(@status, $letter);
  818. X    push(@pl, $theirpl);
  819. X}
  820. X
  821. X# write the file back out.
  822. Xopen(USERLIST,">$userlist.new") || &abort("can't open new $userlist file");
  823. X
  824. Xfor ($i = 0; $i <= $#name; $i++) {
  825. X    print USERLIST $status[$i], "\t", $pl[$i], "\t", $name[$i], "\n"
  826. X        || &abort("error writing new $userlist file");
  827. X}
  828. Xclose(USERLIST) || &abort("error closing new $userlist file");
  829. Xrename("$userlist.new", $userlist);
  830. X
  831. X# Emergency exit with clean-up
  832. Xsub abort {
  833. X    local($reason) = shift(@_);        # Why we are exiting
  834. X    &fatal($reason);
  835. X}
  836. X
  837. X!NO!SUBS!
  838. X$grep -v '^;#' pl/fatal.pl >>package
  839. X$grep -v '^;#' pl/add_log.pl >>package
  840. X$grep -v '^;#' pl/read_conf.pl >>package
  841. X$grep -v '^;#' pl/unpack.pl >>package
  842. X$grep -v '^;#' pl/rangeargs.pl >>package
  843. X$grep -v '^;#' pl/sendfile.pl >>package
  844. X$grep -v '^;#' pl/distribs.pl >>package
  845. X$grep -v '^;#' pl/secure.pl >>package
  846. Xchmod 755 package
  847. X$eunicefix package
  848. END_OF_FILE
  849.   if test 8539 -ne `wc -c <'agent/package.SH'`; then
  850.     echo shar: \"'agent/package.SH'\" unpacked with wrong size!
  851.   fi
  852.   # end of 'agent/package.SH'
  853. fi
  854. if test -f 'agent/pl/file_edit.pl' -a "${1}" != "-c" ; then 
  855.   echo shar: Will not clobber existing file \"'agent/pl/file_edit.pl'\"
  856. else
  857.   echo shar: Extracting \"'agent/pl/file_edit.pl'\" \(8294 characters\)
  858.   sed "s/^X//" >'agent/pl/file_edit.pl' <<'END_OF_FILE'
  859. X;# $Id: file_edit.pl,v 3.0 1993/11/29 13:48:46 ram Exp ram $
  860. X;#
  861. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  862. X;#  
  863. X;#  You may redistribute only under the terms of the Artistic License,
  864. X;#  as specified in the README file that comes with the distribution.
  865. X;#  You may reuse parts of this distribution only within the terms of
  866. X;#  that same Artistic License; a copy of which may be found at the root
  867. X;#  of the source tree for mailagent 3.0.
  868. X;#
  869. X;# $Log: file_edit.pl,v $
  870. X;# Revision 3.0  1993/11/29  13:48:46  ram
  871. X;# Baseline for mailagent 3.0 netwide release.
  872. X;#
  873. X;# 
  874. X;# Inplace file edition. The routine is called as follows:
  875. X;#
  876. X;#   &file_edit(name, description, search, replace)
  877. X;#
  878. X;# where
  879. X;#
  880. X;# name: the path to the file
  881. X;# description: a file description for logging purposes
  882. X;# search: pattern to search, line number or function, undef to append. A
  883. X;# pattern may be specified with // or with ??, in which case an insertion
  884. X;# will be done at the end of the file if the pattern was not found.
  885. X;# replace: string, undef to delete.
  886. X;#
  887. X;# To perform multiple edits simultaneously, use:
  888. X;#
  889. X;#    &file_edit(name, description, srch_1, rep_1, srch_2, rep_2, ...)
  890. X;#
  891. X;# followed by as many search/replace pairs as needed. The main advantage is
  892. X;# that the file is locked only once, then all the edits are performed.
  893. X;#
  894. X# Inplace file edition, with one letter backup file. The routine returns a
  895. X# success status, i.e. 1 if ok and 0 if anything went wrong.
  896. Xsub file_edit {
  897. X    local($name, $desc, @pairs) = @_;
  898. X    local(@backup) = ('~', '#', '@', '%', '=');
  899. X    local($bak);        # File used for backup
  900. X    local(*OLD, *NEW);    # Localize filehandles
  901. X    local($error) = 0;    # Error flag
  902. X
  903. X    return 1 unless @pairs;        # Nothing to do
  904. X
  905. X    if (-d $name) {
  906. X        &add_log("ERROR cannot edit a directory!! ($name)") if $loglvl;
  907. X        return 0;        # Failed
  908. X    }
  909. X
  910. X    # First, lock file to prevent concurrent access
  911. X    if (0 != &acs_rqst($name)) {
  912. X        &add_log("WARNING cannot lock $desc file $name") if $loglvl > 5;
  913. X    }
  914. X
  915. X    # If no search pattern are provided at all, then we only need to do some
  916. X    # appending, and therefore need only the NEW file.
  917. X    local($i);
  918. X    local($need_editing) = 0;
  919. X    for ($i = 0; $i < @pairs; $i += 2) {            # Scan only search items
  920. X        $need_editing = 1 if defined $pairs[$i];    # Search pattern defined?
  921. X        last if $need_editing;
  922. X    }
  923. X
  924. X    # Now try to find a suitable backup character, which is only needed when
  925. X    # we really need to search something for replacing. If we only append to
  926. X    # the file, no backup is necessary.
  927. X    if ($need_editing) {                # Not trying to append
  928. X        foreach $c (@backup) {            # Loop for suitable backup char
  929. X            unless (-e "$name$c") {        # No such file?
  930. X                $bak = "$name$c";        # Ok, grab this extension
  931. X                last;
  932. X            }
  933. X        }
  934. X        unless ($bak) {                    # Nothing found?
  935. X            &add_log("ERROR cannot create backup for $desc file $name")
  936. X                if $loglvl;
  937. X            &free_file($name);            # Release lock
  938. X            return 0;                    # Error
  939. X        }
  940. X    }
  941. X
  942. X    # Open the necessary files, only NEW for appending, or OLD and NEW for
  943. X    # editing (when a search pattern is provided).
  944. X    if ($need_editing) {            # Not trying to append -> needs backup
  945. X        unless (open(OLD, $name)) {
  946. X            &add_log("ERROR cannot open $desc file $name: $!") if $loglvl;
  947. X            &free_file($name);        # Release lock
  948. X            return 0;                # Error
  949. X        }
  950. X        unless (open(NEW, ">$bak")) {
  951. X            &add_log("ERROR cannot create backup for $desc file as $bak: $!")
  952. X                if $loglvl;
  953. X            close OLD;                # We won't need it anymore
  954. X            &free_file($name);        # Release lock
  955. X            return 0;                # Error
  956. X        }
  957. X    } else {                        # Merely trying to append to the old file
  958. X        unless (open(NEW, ">>$name")) {
  959. X            &add_log("ERROR cannot append to $desc file $name: $!")
  960. X                if $loglvl;
  961. X            &free_file($name);        # Release lock
  962. X            return 0;                # Error
  963. X        }
  964. X        for ($i = 1; $i < @pairs; $i += 2) {        # Scan only replace items
  965. X            next unless defined $pairs[$i];
  966. X            unless (print NEW $pairs[$i], "\n") {
  967. X                &add_log("SYSERR write: $!") if $loglvl;
  968. X                $error++;
  969. X            }
  970. X            last if $error;            # Abort immediately if error
  971. X        }
  972. X        unless (close NEW) {
  973. X            &add_log("SYSERR close: $!") if $loglvl;
  974. X            $error++;
  975. X        }
  976. X        &free_file($name);            # Release lock
  977. X        if ($error) {
  978. X            &add_log("WARNING $desc file $name may be corrupted")
  979. X                if $loglvl > 5;
  980. X        }
  981. X        return $error ? 0 : 1;        # Return success (1) if file not corrupted
  982. X    }
  983. X
  984. X    local(@search);            # Searching patterns
  985. X    local(@replace);        # Replacing strings
  986. X    local(@insert);            # Insertion flag for ?? patterns
  987. X    local(@type);            # Type of searching pattern
  988. X
  989. X    # Build the search and replacing arrays, a search/replace pair being
  990. X    # identified by entries at the same index
  991. X    for ($i = 0; $i < @pairs; $i++) {
  992. X        push(@search, $pairs[$i++]);
  993. X        push(@replace, $pairs[$i]);
  994. X    }
  995. X
  996. X    # Here, we must go through the line by line scanning of the OLD file until
  997. X    # a match occurs, at which time the replacing string is written (or the
  998. X    # record skipped when the replacing string is not defined). The search
  999. X    # string can be a verbatim string, a pattern, a numeric value understood as
  1000. X    # a line number or a function to call, giving the line as parameter, along
  1001. X    # with the current line number and understanding a true value as a match.
  1002. X    # If the search pattern is introduced by '?' instead of '/', then the
  1003. X    # replacement value is inserted at the end if no match occurred.
  1004. X
  1005. X    local($NUMBER, $STRING, $PATTERN, $SUB) = (0 .. 3);
  1006. X    local($_);
  1007. X
  1008. X    # Build type array and set up entries in @insert when ?? patterns are used
  1009. X    foreach (@search) {
  1010. X        unless (defined $_) {        # No search pattern means appending
  1011. X            push(@type, undef);
  1012. X            next;
  1013. X        }
  1014. X        if (/^\d+$/) {                # Plain value is a line number
  1015. X            push(@type, $NUMBER);
  1016. X            $_ = int($_);
  1017. X        } elsif (m|^([/?])|) {        # Looks like a pattern
  1018. X            push(@type, $PATTERN);
  1019. X            $insert[$#type] = 1 if $1 eq '?';
  1020. X            s|^[/?](.*)[/?]$|$1|;
  1021. X        } elsif (m|^&|) {        # Function to apply
  1022. X            push(@type, $SUB);
  1023. X            s/^&//;
  1024. X        } else {                            # Must be a verbatim string then
  1025. X            push(@type, $STRING);
  1026. X        }
  1027. X    }
  1028. X    local($.);
  1029. X    local($found);
  1030. X    local($val);        # Searching value
  1031. X    local($type);        # Current searching type
  1032. X    local($replace);    # Replacing value
  1033. X    local($studied);    # Was line studied?
  1034. X
  1035. X    # Now loop over the OLD file and write into NEW
  1036. X    while (<OLD>) {
  1037. X        chop;
  1038. X        $studied = @type < 3 ? 1 : 0;        # Do not study if small amount
  1039. X        $found = 0;
  1040. X        for ($i = 0; $i < @type; $i++) {
  1041. X            $type = $type[$i];
  1042. X            next unless defined $type;        # Already dealt with or no search
  1043. X            $val = $search[$i];                # Searching value
  1044. X            if ($type == $NUMBER && $. == $val) {
  1045. X                $type[$i] = undef;            # Avoid further inspection
  1046. X                $found++;
  1047. X            } elsif ($type == $STRING && $_ eq $val) {
  1048. X                $found++;
  1049. X            } elsif ($type == $PATTERN) {
  1050. X                study unless $studied++;    # Optimize pattern matching
  1051. X                ($found++, @insert[$i] = 0) if /$val/;
  1052. X            } elsif ($type == $SUB && &$val($_, $.)) {
  1053. X                $found++;
  1054. X            }
  1055. X            last if $found;
  1056. X        }
  1057. X        if ($found) {
  1058. X            $replace = $replace[$i];
  1059. X            if (defined $replace) {
  1060. X                (print NEW $replace, "\n") || $error++;
  1061. X            }
  1062. X        } else {
  1063. X            (print NEW $_, "\n") || $error++;
  1064. X        }
  1065. X        if ($error) {
  1066. X            &add_log("SYSERR write: $!") if $loglvl;
  1067. X            last;
  1068. X        }
  1069. X    }
  1070. X
  1071. X    # If insertion was wanted on no-match, and no error has ever occurred, then
  1072. X    # do the necessary insertions now. Also add all those replacing values
  1073. X    # associated with an undefined search string.
  1074. X
  1075. X    unless ($error) {
  1076. X        for ($i = 0; $i < @type; $i++) {
  1077. X            next unless $insert[$i] || !defined($type[$i]);
  1078. X            next unless defined $replace[$i];
  1079. X            (print NEW $replace[$i], "\n") || $error++;
  1080. X        }
  1081. X        &add_log("SYSERR write: $!") if $error && $loglvl;
  1082. X    }
  1083. X
  1084. X    # Edition is completed. Close files and make sure NEW is correctly flushed
  1085. X    # to disk by checking the return value from close.
  1086. X
  1087. X    close OLD;
  1088. X    unless (close NEW) {
  1089. X        &add_log("SYSERR close: $!") if $loglvl;
  1090. X        $error++;
  1091. X    }
  1092. X
  1093. X    # If no error has occurred so far, rename backup file as the original file
  1094. X    # name, in effect putting an end to the editing phase.
  1095. X
  1096. X    if ($error == 0 && !rename($bak, $name)) {
  1097. X        &add_log("SYSERR rename: $!") if $loglvl;
  1098. X        $error++;
  1099. X    }
  1100. X    &free_file($name);            # Lock may now safely be released
  1101. X
  1102. X    if ($error) {
  1103. X        &add_log("ERROR cannot inplace edit $desc file $name") if $loglvl;
  1104. X        unless (unlink $bak) {
  1105. X            &add_log("SYSERR unlink: $!") if $loglvl;
  1106. X            &add_log("ERROR cannot remove temporary file $bak") if $loglvl;
  1107. X        }
  1108. X        return 0;                # Editing failed
  1109. X    }
  1110. X
  1111. X    &add_log("edited $desc file $name") if $loglvl > 18;
  1112. X
  1113. X    1;        # Success
  1114. X}
  1115. X
  1116. END_OF_FILE
  1117.   if test 8294 -ne `wc -c <'agent/pl/file_edit.pl'`; then
  1118.     echo shar: \"'agent/pl/file_edit.pl'\" unpacked with wrong size!
  1119.   fi
  1120.   # end of 'agent/pl/file_edit.pl'
  1121. fi
  1122. if test -f 'agent/pl/mh.pl' -a "${1}" != "-c" ; then 
  1123.   echo shar: Will not clobber existing file \"'agent/pl/mh.pl'\"
  1124. else
  1125.   echo shar: Extracting \"'agent/pl/mh.pl'\" \(8777 characters\)
  1126.   sed "s/^X//" >'agent/pl/mh.pl' <<'END_OF_FILE'
  1127. X;# $Id: mh.pl,v 3.0 1993/11/29 13:49:02 ram Exp ram $
  1128. X;#
  1129. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  1130. X;#  
  1131. X;#  You may redistribute only under the terms of the Artistic License,
  1132. X;#  as specified in the README file that comes with the distribution.
  1133. X;#  You may reuse parts of this distribution only within the terms of
  1134. X;#  that same Artistic License; a copy of which may be found at the root
  1135. X;#  of the source tree for mailagent 3.0.
  1136. X;#
  1137. X;# $Log: mh.pl,v $
  1138. X;# Revision 3.0  1993/11/29  13:49:02  ram
  1139. X;# Baseline for mailagent 3.0 netwide release.
  1140. X;#
  1141. X;# 
  1142. X;# This set of routine handles MH-style folders, which differ from the
  1143. X;# traditional Unix-style folders by being directories, individual messages
  1144. X;# being stored in distinct files (numbers).
  1145. X;#
  1146. X;# Note: MH packed folders are simply MMDF-style mailboxes.
  1147. X;#
  1148. X#
  1149. X# MH-style saving routines
  1150. X#
  1151. X
  1152. Xpackage mh;
  1153. X
  1154. X# Attempt to save in a MH directory folder. Note that the profile entry
  1155. X# Msg-Protect is not honored (always 0600, mailagent's default).
  1156. Xsub save {
  1157. X    local($folder) = @_;        # MH folder name (without leading '+')
  1158. X    &profile;                    # Get MH profile, once and for all
  1159. X    $folder = "$cf'home/$Profile{'Path'}/$folder";
  1160. X    local($mode) = oct("0$Profile{'Folder-Protect'}" || '0700');
  1161. X    &'makedir($folder, $mode);    # Create folder dir with right permissions
  1162. X    &save_msg($folder, 'MH');    # Propagate failure status
  1163. X}
  1164. X    
  1165. X# Save in a directory, not really an MH folder.
  1166. Xsub savedir {
  1167. X    local($folder) = @_;        # Directory folder name
  1168. X    &save_msg($folder, 'DIR');    # Propagate failure status
  1169. X}
  1170. X
  1171. X# Common subroutine to &save and &savedir
  1172. Xsub save_msg {
  1173. X    local($folder, $mh) = @_;
  1174. X    unless (-d $folder) {
  1175. X        &'add_log("ERROR $mh folder $folder is not a directory")
  1176. X            if $'loglvl > 1;
  1177. X        return 1;    # Failed
  1178. X    }
  1179. X    local($name) = &new_msg($folder);
  1180. X    unless ($name) {
  1181. X        &'add_log("ERROR cannot get message number in $mh folder $folder")
  1182. X            if $'loglvl > 1;
  1183. X        return 1;    # Failed
  1184. X    }
  1185. X
  1186. X    # Now initiate saving by opening file for appending, then calling the
  1187. X    # MMDF-style saving routine with MH type (skips emission of ^A lines).
  1188. X
  1189. X    unless (open(MHMSG, ">>$name")) {
  1190. X        &'add_log("ERROR cannot reopen $name: $!") if $'loglvl > 1;
  1191. X        return 1;    # Failed, don't unlink message
  1192. X    }
  1193. X
  1194. X    # There is no need to lock the file here, since MH will never select an
  1195. X    # existing file when computing a new message number.
  1196. X
  1197. X    local($failed, $amount) = &mmdf'save_mmdf(*MHMSG, 'MH');
  1198. X
  1199. X    # Now the size of the message must be *exactly* the amount returned.
  1200. X    close MHMSG;
  1201. X    local($size) = -s $name;
  1202. X
  1203. X    &'add_log("ERROR $name has $size bytes (should have $amount)")
  1204. X        if $size != $amount && $loglvl;
  1205. X
  1206. X    $failed = 1 if $size != $amount;
  1207. X
  1208. X    # Update the unseen sequence, if needed and saving succeeded. An entry
  1209. X    # is also made in the logfile for easy grep'ing and locating of messages
  1210. X    # saved in directories.
  1211. X
  1212. X    &unseen($name)
  1213. X        if $mh eq 'MH' && $Profile{'Unseen-Sequence'} ne '' && !$failed;
  1214. X
  1215. X    &'add_log("UNSEEN $name") if $'loglvl > 6;    # Mark clearly in log
  1216. X    return $failed;        # Return failure status
  1217. X}
  1218. X
  1219. X#
  1220. X# MH profile and sequence management.
  1221. X#
  1222. X
  1223. X# Read MH profile, fill in %Profile entries.
  1224. Xsub profile {
  1225. X    return if defined %Profile;
  1226. X    # Make sure there is at least a valid Path entry, in case they made a
  1227. X    # mistake and asked for MH folder saving without a valid .mh_profile...
  1228. X    local($dflt) = defined($'XENV{'maildir'}) ? $'XENV{'maildir'} : 'Mail';
  1229. X    $dflt = &'tilda($dflt);        # Restore possible leading '~'
  1230. X    $dflt =~ s|^~/||;            # Strip down (relative path under ~)
  1231. X    $Profile{'Path'} = $dflt;
  1232. X    local($mhprofile) = &'tilda_expand($cf'mhprofile || '~/.mh_profile');
  1233. X    unless (open(PROFILE, $mhprofile)) {
  1234. X        &'add_log("ERROR cannot open MH profile '$mhprofile': $!")
  1235. X            if $'loglvl > 1;
  1236. X        return;
  1237. X    }
  1238. X    local($_);
  1239. X    while (<PROFILE>) {
  1240. X        next unless /^([^:]+):\s*(.*)/;
  1241. X        $Profile{$1} = $2;
  1242. X    }
  1243. X    close PROFILE;
  1244. X}
  1245. X
  1246. X# Compute new message number/name.
  1247. X# If true MH folder, get next available number. If directory, see if there is
  1248. X# a .msg_prefix file to use as a basename. Otherwise, select an MH message
  1249. X# number.
  1250. Xsub new_msg {
  1251. X    local($dir) = @_;
  1252. X    unless (opendir(DIR, $dir)) {
  1253. X        &'add_log("ERROR unable to open dir $dir: $!") if $'loglvl > 1;
  1254. X        return 0;        # Marks failure
  1255. X    }
  1256. X    if (0 != &'acs_rqst($dir)) {
  1257. X        &'add_log("WARNING could not lock dir $dir") if $'loglvl > 5;
  1258. X    }
  1259. X    local(@dir) = readdir DIR;        # Slurp it as a whole
  1260. X    closedir DIR;
  1261. X
  1262. X    # See if we have to use message prefix
  1263. X    local($prefix) = $cf'msgprefix || '.msg_prefix';
  1264. X    local($msg) = "$dir/$prefix";
  1265. X    local($msg_prefix) = '';
  1266. X    if (-f $msg) {                    # Not an MH folder it would seem
  1267. X        unless (open(PREFIX, $msg)) {
  1268. X            &'add_log("ERROR can't open msg prefix $msg: $!") if $'loglvl > 1;
  1269. X            # Continue, will use MH-style numbering then
  1270. X        } else {
  1271. X            chop($msg_prefix = <PREFIX>);    # First line gives prefix
  1272. X            close PREFIX;
  1273. X        }
  1274. X    }
  1275. X
  1276. X    # If prefix is used, keep only those messages starting with that prefix.
  1277. X    # Otherwise, keep only numbers.
  1278. X    local($pat) = $msg_prefix eq '' ? '/^\d+$/' : "s/^$msg_prefix(\\d+)\$/\$1/";
  1279. X    eval '@dir = grep(' . $pat . ', @dir)';
  1280. X
  1281. X    # Now sort in ascending order and get highest number
  1282. X    @dir = sort { $a <=> $b; } @dir;
  1283. X    local($highest) = pop(@dir);
  1284. X
  1285. X    # Now create new message before unlocking the directory. Use appending
  1286. X    # instead of plain creation in case our lock was not honoured for some
  1287. X    # reason.
  1288. X    $highest++;
  1289. X    local($new) = "$dir/$msg_prefix$highest";
  1290. X    unless (open(NEW, ">>$new")) {
  1291. X        &'add_log("ERROR cannot create $msg: $!") if $'loglvl > 1;
  1292. X        $new = 0;    # Signal no creation (directory still locked)
  1293. X    } else {
  1294. X        close NEW;    # File is now created
  1295. X    }
  1296. X
  1297. X    &'free_file($dir);        # Unlock directory
  1298. X    return $new;            # Return message name, or 0 if error
  1299. X}
  1300. X
  1301. X# Mark MH message as unseen by adding it to the sequences listed in the
  1302. X# profile entry Unseen-Sequence.
  1303. Xsub unseen {
  1304. X    local($name) = @_;        # Full path of unseen mail message
  1305. X    local($dir, $num) = $name =~ m|(.*)/(\d+)|;
  1306. X    unless ($num) {
  1307. X        &'add_log("WARNING cannot mark $name as unseen (not an MH message)")
  1308. X            if $'loglvl > 5;
  1309. X        return;
  1310. X    }
  1311. X    
  1312. X    # Lock the .mh_sequences file first. It's a pity MH does not itself lock
  1313. X    # this file when syncing it... (routine m_sync() in MH 6.8).
  1314. X
  1315. X    local($seqfile) = "$dir/.mh_sequences";
  1316. X    if (0 != &'acs_rqst($seqfile)) {
  1317. X        &'add_log("WARNING could not lock MH sequence in $dir")
  1318. X            if $'loglvl > 5;
  1319. X    }
  1320. X
  1321. X    # Create new .mh_sequences file
  1322. X    unless (open(MHSEQ, ">$seqfile.x")) {
  1323. X        &'add_log("ERROR cannot create new MH sequence file in $dir: $!")
  1324. X            if $'loglvl > 1;
  1325. X        &'free_file($seqfile);
  1326. X        return;
  1327. X    }
  1328. X
  1329. X    open(OLDSEQ, $seqfile);    # May not exist yet, so no error check
  1330. X
  1331. X    # Get the name of the sequences we need to update, save in %seq.
  1332. X    local(%seq);
  1333. X    foreach $seq (split(/,/, $Profile{'Unseen-Sequence'})) {
  1334. X        $seq =~ s/^\s*//;    # Remove leading and trailing spaces
  1335. X        $seq =~ s/\s*$//;
  1336. X        $seq{$seq}++;        # Record unseen sequence
  1337. X    }
  1338. X
  1339. X    # Now loop over the existing sequences in the old .mh_sequences file
  1340. X    # and update them. If some unseen sequences were not present yet, create
  1341. X    # them.
  1342. X
  1343. X    local($_);
  1344. X    local($seqname);
  1345. X
  1346. X    while (<OLDSEQ>) {
  1347. X        if (s/^(\S+)://) {    # Found a sequence
  1348. X            $seqname = $1;
  1349. X            unless (defined $seq{$seqname}) {
  1350. X                print MHSEQ "$seqname:", $_;
  1351. X                next;
  1352. X            }
  1353. X            # Ok, it's an useen sequence and we need to update it
  1354. X            chop;
  1355. X            print MHSEQ "$seqname: ", &seqadd($_, $num), "\n";
  1356. X            delete $seq{$seqname};
  1357. X        } else {
  1358. X            print MHSEQ $_;    # Whatever it was, propagate it
  1359. X        }
  1360. X    }
  1361. X    close OLDSEQ;
  1362. X
  1363. X    foreach $seq (keys %seq) {    # Create remaining sequences
  1364. X        print MHSEQ "$seq: $num\n";
  1365. X    }
  1366. X    close MHSEQ;
  1367. X
  1368. X    unless (rename("$seqfile.x", $seqfile)) {
  1369. X        &'add_log("ERROR cannot rename $seqfile.x as $seqfile: $!")
  1370. X            if $'loglvl > 1;
  1371. X    }
  1372. X
  1373. X    &'free_file($seqfile);
  1374. X}
  1375. X
  1376. X# Add a message to an MH sequence (sorted on input).
  1377. Xsub seqadd {
  1378. X    local($seq, $num) = @_;
  1379. X    local(@seq) = split(' ', $seq);
  1380. X    local($min, $max);    # Ranges in sequences are min-max
  1381. X    local($i);
  1382. X    local(@new);        # New sequence we are building
  1383. X    local($item);        # Current item
  1384. X    for ($i = 0; $i < @seq; $i++) {
  1385. X        $item = $seq[$i];
  1386. X        if ($num == 0) {    # Message already inserted
  1387. X            push(@new, $item);
  1388. X            next;            # Flush sequence
  1389. X        }
  1390. X        if ($item =~ /-/) {
  1391. X            ($min, $max) = $item =~ /(\d+)-(\d+)/;
  1392. X        } else {
  1393. X            $min = $max = $item;
  1394. X        }
  1395. X        if ($num > $max) {    # New message has to be inserted later on
  1396. X            if ($num == $max + 1) {
  1397. X                push(@new, "$min-$num");
  1398. X                $num = 0;    # Signals: inserted
  1399. X            } else {
  1400. X                push(@new, $item);
  1401. X            }
  1402. X            next;
  1403. X        }
  1404. X        # Here, $num <= $max
  1405. X        if ($num < $min) {    # Item to be inserted before
  1406. X            if ($num == $min - 1) {
  1407. X                push(@new, "$num-$max");
  1408. X            } else {
  1409. X                push(@new, $num);
  1410. X                push(@new, $item);
  1411. X            }
  1412. X        } else {
  1413. X            push(@new, $item);    # Item already within that range !?
  1414. X        }
  1415. X        $num = 0;                # Item was inserted
  1416. X    }
  1417. X    push(@new, $num) if $num;    # At sequence's tail if not inserted yet
  1418. X    return join(' ', @new);        # Return new sequence
  1419. X}
  1420. X
  1421. Xpackage main;
  1422. X
  1423. END_OF_FILE
  1424.   if test 8777 -ne `wc -c <'agent/pl/mh.pl'`; then
  1425.     echo shar: \"'agent/pl/mh.pl'\" unpacked with wrong size!
  1426.   fi
  1427.   # end of 'agent/pl/mh.pl'
  1428. fi
  1429. if test -f 'agent/pl/power.pl' -a "${1}" != "-c" ; then 
  1430.   echo shar: Will not clobber existing file \"'agent/pl/power.pl'\"
  1431. else
  1432.   echo shar: Extracting \"'agent/pl/power.pl'\" \(8058 characters\)
  1433.   sed "s/^X//" >'agent/pl/power.pl' <<'END_OF_FILE'
  1434. X;# $Id: power.pl,v 3.0 1993/11/29 13:49:08 ram Exp ram $
  1435. X;#
  1436. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  1437. X;#  
  1438. X;#  You may redistribute only under the terms of the Artistic License,
  1439. X;#  as specified in the README file that comes with the distribution.
  1440. X;#  You may reuse parts of this distribution only within the terms of
  1441. X;#  that same Artistic License; a copy of which may be found at the root
  1442. X;#  of the source tree for mailagent 3.0.
  1443. X;#
  1444. X;# $Log: power.pl,v $
  1445. X;# Revision 3.0  1993/11/29  13:49:08  ram
  1446. X;# Baseline for mailagent 3.0 netwide release.
  1447. X;#
  1448. X;# 
  1449. X;# Power manipulation package. Each power is stored in the 'passwd' file and
  1450. X;# is protected by a password. Additionally, a list of authorized e-mail
  1451. X;# addresses is stored in 'powedir'. When the power name is longer than 12
  1452. X;# characters, it is aliased in the 'powerlist' file. This is to ensure that
  1453. X;# no filesystem limit will get into the way, ever (2 characters are reserved
  1454. X;# at the end for temporary backup, hence the limit fixed to 12).
  1455. X;#
  1456. X#
  1457. X# Power control
  1458. X#
  1459. X
  1460. Xpackage power;
  1461. X
  1462. X# Grant power to user, returning 1 if ok, 0 if failed.
  1463. Xsub grant {
  1464. X    local($name, $clear_passwd, $user) = @_;
  1465. X    unless (&'file_secure($cf'passwd, 'password')) {
  1466. X        &add_log("WARNING cannot grant power '$name'") if $'loglvl > 5;
  1467. X        return 0;        # Failed
  1468. X    }
  1469. X    unless (&valid($name, $clear_passwd)) {
  1470. X        &add_log("ERROR user '$user' gave invalid password for power '$name'")
  1471. X            if $'loglvl > 1;
  1472. X        return 0;        # Power not granted
  1473. X    }
  1474. X    unless (&authorized($name, $user)) {
  1475. X        &add_log("ERROR user '$user' may not request power '$name'")
  1476. X            if $'loglvl > 1;
  1477. X        return 0;        # Power not granted
  1478. X    }
  1479. X    1;            # Power may be granted
  1480. X}
  1481. X
  1482. X# Check whether user is authorized to get this power or change its password.
  1483. X# Returns 1 if user may proceed, 0 otherwise.
  1484. Xsub authorized {
  1485. X    local($name, $user) = @_;
  1486. X    local($auth) = &authfile($name);
  1487. X    unless (&'file_secure($auth, 'authentication')) {
  1488. X        &add_log("WARNING cannot authenticate power '$name'") if $'loglvl > 5;
  1489. X        return 0;        # Failed
  1490. X    }
  1491. X    unless (open(AUTH, $auth)) {
  1492. X        &add_log("ERROR cannot open auth file $auth for power '$name': $!")
  1493. X            if $'loglvl > 1;
  1494. X        return 0;        # Cannot verify identity -> cannot grant power
  1495. X    }
  1496. X    local($_);
  1497. X    local($ok) = 0;
  1498. X    study $user;                # Various searches will be attempted
  1499. X    while (<AUTH>) {
  1500. X        chop;
  1501. X        $_ = &'perl_pattern($_);    # Shell style patterns may be used
  1502. X        if ($user =~ /^$_$/) {        # User may request for this power
  1503. X            $ok = 1;                # Ok, we found him
  1504. X            last;
  1505. X        }
  1506. X    }
  1507. X    close(AUTH);
  1508. X    $ok;            # Boolean status
  1509. X}
  1510. X
  1511. X# Check whether a power password is valid or not. Returns 0 if password is
  1512. X# invalid or the power is undefined, 1 when password is ok.
  1513. Xsub valid {
  1514. X    local($name, $clear_passwd) = @_;
  1515. X    unless (&'file_secure($cf'passwd, 'password')) {
  1516. X        &add_log("WARNING cannot verify password for power '$name'")
  1517. X            if $'loglvl > 5;
  1518. X        return 0;        # Failed
  1519. X    }
  1520. X    local($power, $passwd, $comment) = &getpwent($name);
  1521. X    return 0 unless defined $power;            # Unknown power -> illegal password
  1522. X    if ($passwd =~ s/^<(.*)>$/$1/) {        # Password given as <clear>
  1523. X        $clear_passwd eq $passwd;
  1524. X    } else {                                # Password encrypted
  1525. X        crypt($clear_passwd, $passwd) eq $passwd;
  1526. X    }
  1527. X}
  1528. X
  1529. X#
  1530. X# Power aliases
  1531. X#
  1532. X
  1533. X# Compute file name where list of authorized users is kept.
  1534. Xsub authfile {
  1535. X    local($name) = @_;
  1536. X    return $cf'powerdir . "/$name" if length($name) <= 12;
  1537. X    unless (open(ALIASES, $cf'powerlist)) {
  1538. X        &add_log("ERROR cannot open power list $cf'powerlist: $!")
  1539. X            if $'loglvl > 1;
  1540. X        return '/dev/null';
  1541. X    }
  1542. X    local($_);
  1543. X    local($power, $alias);
  1544. X    while (<ALIASES>) {
  1545. X        ($power, $alias) = split(' ');
  1546. X        if ($power eq $name) {
  1547. X            close ALIASES;
  1548. X            return $cf'powerdir . "/$alias"
  1549. X        }
  1550. X    }
  1551. X    close ALIASES;
  1552. X    return '/dev/null';
  1553. X}
  1554. X
  1555. X# Set clearance file, returning 1 for success, 0 for failure
  1556. Xsub set_auth {
  1557. X    local($name, *text) = @_;
  1558. X    local($file) = &authfile($name);
  1559. X    if (-e $file) {
  1560. X        unless (unlink $file) {
  1561. X            &add_log("SYSERR unlink: $!") if $'loglvl;
  1562. X            &add_log("WARNING appending to $file (should have replaced it)")
  1563. X                if $'loglvl > 5;
  1564. X        }
  1565. X    }
  1566. X    local($ok) =
  1567. X        &'file_edit($file, 'power clearance', undef, join("\n", @text));
  1568. X    $ok;
  1569. X}
  1570. X
  1571. X# Append users to clearance file, returning 1 on success and 0 on failure
  1572. Xsub add_auth {
  1573. X    local($name, *text) = @_;
  1574. X    local($file) = &authfile($name);
  1575. X    local($ok) =
  1576. X        &'file_edit($file, 'power clearance', undef, join("\n", @text));
  1577. X    $ok;
  1578. X}
  1579. X
  1580. X# Remove users from clearance file, returning 1 on success and 0 on failure
  1581. Xsub rem_auth {
  1582. X    local($name, *text) = @_;
  1583. X    local($file) = &authfile($name);
  1584. X    local(@pairs);    # Search/replace pairs for file_edit
  1585. X    foreach $addr (@text) {
  1586. X        push(@pairs, $addr, undef);
  1587. X    }
  1588. X    local($ok) = &'file_edit($file, 'power clearance', @pairs);
  1589. X    $ok;
  1590. X}
  1591. X
  1592. X# Is alias already used?
  1593. Xsub used_alias {
  1594. X    local($alias) = @_;
  1595. X    open(ALIAS, $cf'powerlist) || return 0;
  1596. X    local($_);
  1597. X    local($pow, $ali);
  1598. X    local($found) = 0;
  1599. X    while (<ALIAS>) {
  1600. X        ($pow, $ali) = split(' ');
  1601. X        $found = 1 if $ali eq $alias;
  1602. X        last if $found;
  1603. X    }
  1604. X    close ALIAS;
  1605. X    $found;        # Return true when alias already used
  1606. X}
  1607. X
  1608. X# Add new power alias, returning 1 for ok and 0 for failure.
  1609. Xsub add_alias {
  1610. X    local($power, $alias) = @_;
  1611. X    local($ok) =
  1612. X        &'file_edit($cf'powerlist, 'power aliases', undef, "$power $alias");
  1613. X    &add_log("aliased power '$power' into '$alias'") if $'loglvl > 6 && $ok;
  1614. X    $ok;
  1615. X}
  1616. X
  1617. X# Delete power from alias file, returning 1 for ok and 0 for failure.
  1618. Xsub del_alias {
  1619. X    local($power) = @_;
  1620. X    local($ok) =
  1621. X        &'file_edit($cf'powerlist, 'power aliases', "/^$power\\s/", undef);
  1622. X    &add_log("ERROR cannot delete power '$power' from aliases")
  1623. X        if $'loglvl > 1 && !$ok;
  1624. X    &add_log("deleted power '$power' from aliases")
  1625. X        if $'loglvl > 6 && $ok;
  1626. X    $ok;
  1627. X}
  1628. X
  1629. X#
  1630. X# Setting password information
  1631. X#
  1632. X
  1633. X# Set power password, returning 0 if ok, -1 for failure
  1634. Xsub set_passwd {
  1635. X    local($name, $clear_newpasswd) = @_;
  1636. X
  1637. X    # Make sure entry already exists (i.e. power is defined)
  1638. X    local($power, $passwd, $comment) = &getpwent($name);
  1639. X    return -1 unless defined $power;        # Unknown power
  1640. X
  1641. X    # Choose a salt randomly, using the two lowest bytes of current time stamp
  1642. X    local($t) = time;
  1643. X    local($c1, $c2) = ($t, $t & 0xffff);
  1644. X    $c1 -= ($t & 0xff) * ($c2 + (($t & 0xffff0000) >> 16));
  1645. X    $c1 = $c1 > 0 ? $c1 : -$c1;
  1646. X    local(@saltset) = ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '.', '/');
  1647. X    local($salt) = $saltset[$c1 % @saltset] . $saltset[$c2 % @saltset];
  1648. X    $passwd = crypt($clear_newpasswd, $salt);
  1649. X
  1650. X    # Set new password entry
  1651. X    &setpwent($power, $passwd, $comment);    # Propagate status
  1652. X}
  1653. X
  1654. X# Get password entry, and return ($power, $password, $comment) if found or
  1655. X# undef if error or not found.
  1656. Xsub getpwent {
  1657. X    local($wanted) = @_;        # Power entry wanted
  1658. X    unless (open(PASSWD, "$cf'passwd")) {
  1659. X        &add_log("ERROR cannot open password file: $!") if $'loglvl;
  1660. X        return undef;
  1661. X    }
  1662. X    local($power, $password, $comment);
  1663. X    local($_);
  1664. X    while (<PASSWD>) {
  1665. X        chop;
  1666. X        ($power, $password, $comment) = split(/:/);
  1667. X        if ($power eq $wanted) {
  1668. X            close PASSWD;
  1669. X            return ($power, $password, $comment);
  1670. X        }
  1671. X    }
  1672. X    close PASSWD;
  1673. X    undef;            # Not found
  1674. X}
  1675. X
  1676. X# Set password entry, given ($power, $password, $comment) and return 0 for
  1677. X# success, -1 on failure.
  1678. Xsub setpwent {
  1679. X    local($power, $password, $comment) = @_;
  1680. X    local($ok) = &'file_edit(
  1681. X        $cf'passwd, 'password',
  1682. X        "?^$power:?", "$power:$password:$comment"
  1683. X    );
  1684. X    &add_log("ERROR cannot set new password entry for '$power'")
  1685. X        if $'loglvl > 1 && !$ok;
  1686. X    $ok ? 0 : -1;
  1687. X}
  1688. X
  1689. X# Remove passoword entry, returning 0 for success and -1 on failure.
  1690. Xsub rempwent {
  1691. X    local($power) = @_;
  1692. X    local($ok) = &'file_edit(
  1693. X        $cf'passwd, 'password',
  1694. X        "/^$power:/", undef
  1695. X    );
  1696. X    &add_log("ERROR cannot remove password entry for '$power'")
  1697. X        if $'loglvl > 1 && !$ok;
  1698. X    $ok ? 0 : -1;
  1699. X}
  1700. X
  1701. X#
  1702. X# Logging control
  1703. X#
  1704. X
  1705. X# Replaces main'add_log by remapping to powerlog...
  1706. X# Opens new user-defined logfile 'powerlog' to extract power-related
  1707. X# messages there. If not defined in ~/.mailagent, messages will go to the
  1708. X# default log file. A copy of the log message is kept there anyway.
  1709. Xsub add_log {
  1710. X    local($msg) = @_;
  1711. X    &usrlog'new('powerlog', $cf'powerlog, 'COPY') if $cf'powerlog;
  1712. X    &'usr_log('powerlog', $msg);
  1713. X}
  1714. X
  1715. Xpackage main;
  1716. X
  1717. END_OF_FILE
  1718.   if test 8058 -ne `wc -c <'agent/pl/power.pl'`; then
  1719.     echo shar: \"'agent/pl/power.pl'\" unpacked with wrong size!
  1720.   fi
  1721.   # end of 'agent/pl/power.pl'
  1722. fi
  1723. if test -f 'misc/README' -a "${1}" != "-c" ; then 
  1724.   echo shar: Will not clobber existing file \"'misc/README'\"
  1725. else
  1726.   echo shar: Extracting \"'misc/README'\" \(192 characters\)
  1727.   sed "s/^X//" >'misc/README' <<'END_OF_FILE'
  1728. XThis directory contains:
  1729. X
  1730. X    - unkit: an example of filtering command extension
  1731. X    - shell: an example of server command
  1732. X
  1733. XThose files are not installed, they are only provided as living examples.
  1734. END_OF_FILE
  1735.   if test 192 -ne `wc -c <'misc/README'`; then
  1736.     echo shar: \"'misc/README'\" unpacked with wrong size!
  1737.   fi
  1738.   # end of 'misc/README'
  1739. fi
  1740. echo shar: End of archive 15 \(of 26\).
  1741. cp /dev/null ark15isdone
  1742. MISSING=""
  1743. 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
  1744.     if test ! -f ark${I}isdone ; then
  1745.     MISSING="${MISSING} ${I}"
  1746.     fi
  1747. done
  1748. if test "${MISSING}" = "" ; then
  1749.     echo You have unpacked all 26 archives.
  1750.     echo "Now run 'sh PACKNOTES', then read README and type Configure.'"
  1751.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1752. else
  1753.     echo You still must unpack the following archives:
  1754.     echo "        " ${MISSING}
  1755. fi
  1756. exit 0
  1757.  
  1758. exit 0 # Just in case...
  1759.