home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume33 / mailagnt / part08 < prev    next >
Encoding:
Text File  |  1992-11-19  |  54.5 KB  |  1,409 lines

  1. Newsgroups: comp.sources.misc
  2. From: ram@eiffel.com (Raphael Manfredi)
  3. Subject:  v33i100:  mailagent - Rule Based Mail Filtering, Part08/17
  4. Message-ID: <1992Nov20.050610.14170@sparky.imd.sterling.com>
  5. X-Md4-Signature: df29022f0cc212b372fd291c70a57438
  6. Date: Fri, 20 Nov 1992 05:06:10 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: ram@eiffel.com (Raphael Manfredi)
  10. Posting-number: Volume 33, Issue 100
  11. Archive-name: mailagent/part08
  12. Environment: Perl, Sendmail, UNIX
  13.  
  14. #! /bin/sh
  15. # This is a shell archive.  Remove anything before this line, then feed it
  16. # into a shell via "sh file" or similar.  To overwrite existing files,
  17. # type "sh file -c".
  18. # Contents:  Copying agent/magent.SH agent/pl/analyze.pl
  19. #   agent/test/filter/list.t
  20. # Wrapped by kent@sparky on Wed Nov 18 22:42:24 1992
  21. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  22. echo If this archive is complete, you will see the following message:
  23. echo '          "shar: End of archive 8 (of 17)."'
  24. if test -f 'Copying' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'Copying'\"
  26. else
  27.   echo shar: Extracting \"'Copying'\" \(18090 characters\)
  28.   sed "s/^X//" >'Copying' <<'END_OF_FILE'
  29. X
  30. X                    GNU GENERAL PUBLIC LICENSE
  31. X                       Version 2, June 1991
  32. X
  33. X Copyright (C) 1989, 1991 Free Software Foundation, Inc.
  34. X                          675 Mass Ave, Cambridge, MA 02139, USA
  35. X
  36. X Everyone is permitted to copy and distribute verbatim copies
  37. X of this license document, but changing it is not allowed.
  38. X
  39. X                            Preamble
  40. X
  41. X  The licenses for most software are designed to take away your
  42. Xfreedom to share and change it.  By contrast, the GNU General Public
  43. XLicense is intended to guarantee your freedom to share and change free
  44. Xsoftware--to make sure the software is free for all its users.  This
  45. XGeneral Public License applies to most of the Free Software
  46. XFoundation's software and to any other program whose authors commit to
  47. Xusing it.  (Some other Free Software Foundation software is covered by
  48. Xthe GNU Library General Public License instead.)  You can apply it to
  49. Xyour programs, too.
  50. X
  51. X  When we speak of free software, we are referring to freedom, not
  52. Xprice.  Our General Public Licenses are designed to make sure that you
  53. Xhave the freedom to distribute copies of free software (and charge for
  54. Xthis service if you wish), that you receive source code or can get it
  55. Xif you want it, that you can change the software or use pieces of it
  56. Xin new free programs; and that you know you can do these things.
  57. X
  58. X  To protect your rights, we need to make restrictions that forbid
  59. Xanyone to deny you these rights or to ask you to surrender the rights.
  60. XThese restrictions translate to certain responsibilities for you if you
  61. Xdistribute copies of the software, or if you modify it.
  62. X
  63. X  For example, if you distribute copies of such a program, whether
  64. Xgratis or for a fee, you must give the recipients all the rights that
  65. Xyou have.  You must make sure that they, too, receive or can get the
  66. Xsource code.  And you must show them these terms so they know their
  67. Xrights.
  68. X
  69. X  We protect your rights with two steps: (1) copyright the software, and
  70. X(2) offer you this license which gives you legal permission to copy,
  71. Xdistribute and/or modify the software.
  72. X
  73. X  Also, for each author's protection and ours, we want to make certain
  74. Xthat everyone understands that there is no warranty for this free
  75. Xsoftware.  If the software is modified by someone else and passed on, we
  76. Xwant its recipients to know that what they have is not the original, so
  77. Xthat any problems introduced by others will not reflect on the original
  78. Xauthors' reputations.
  79. X
  80. X  Finally, any free program is threatened constantly by software
  81. Xpatents.  We wish to avoid the danger that redistributors of a free
  82. Xprogram will individually obtain patent licenses, in effect making the
  83. Xprogram proprietary.  To prevent this, we have made it clear that any
  84. Xpatent must be licensed for everyone's free use or not licensed at all.
  85. X
  86. X  The precise terms and conditions for copying, distribution and
  87. Xmodification follow.
  88. X
  89. X                    GNU GENERAL PUBLIC LICENSE
  90. X   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
  91. X
  92. X  0. This License applies to any program or other work which contains
  93. Xa notice placed by the copyright holder saying it may be distributed
  94. Xunder the terms of this General Public License.  The "Program", below,
  95. Xrefers to any such program or work, and a "work based on the Program"
  96. Xmeans either the Program or any derivative work under copyright law:
  97. Xthat is to say, a work containing the Program or a portion of it,
  98. Xeither verbatim or with modifications and/or translated into another
  99. Xlanguage.  (Hereinafter, translation is included without limitation in
  100. Xthe term "modification".)  Each licensee is addressed as "you".
  101. X
  102. XActivities other than copying, distribution and modification are not
  103. Xcovered by this License; they are outside its scope.  The act of
  104. Xrunning the Program is not restricted, and the output from the Program
  105. Xis covered only if its contents constitute a work based on the
  106. XProgram (independent of having been made by running the Program).
  107. XWhether that is true depends on what the Program does.
  108. X
  109. X  1. You may copy and distribute verbatim copies of the Program's
  110. Xsource code as you receive it, in any medium, provided that you
  111. Xconspicuously and appropriately publish on each copy an appropriate
  112. Xcopyright notice and disclaimer of warranty; keep intact all the
  113. Xnotices that refer to this License and to the absence of any warranty;
  114. Xand give any other recipients of the Program a copy of this License
  115. Xalong with the Program.
  116. X
  117. XYou may charge a fee for the physical act of transferring a copy, and
  118. Xyou may at your option offer warranty protection in exchange for a fee.
  119. X
  120. X  2. You may modify your copy or copies of the Program or any portion
  121. Xof it, thus forming a work based on the Program, and copy and
  122. Xdistribute such modifications or work under the terms of Section 1
  123. Xabove, provided that you also meet all of these conditions:
  124. X
  125. X    a) You must cause the modified files to carry prominent notices
  126. X    stating that you changed the files and the date of any change.
  127. X
  128. X    b) You must cause any work that you distribute or publish, that in
  129. X    whole or in part contains or is derived from the Program or any
  130. X    part thereof, to be licensed as a whole at no charge to all third
  131. X    parties under the terms of this License.
  132. X
  133. X    c) If the modified program normally reads commands interactively
  134. X    when run, you must cause it, when started running for such
  135. X    interactive use in the most ordinary way, to print or display an
  136. X    announcement including an appropriate copyright notice and a
  137. X    notice that there is no warranty (or else, saying that you provide
  138. X    a warranty) and that users may redistribute the program under
  139. X    these conditions, and telling the user how to view a copy of this
  140. X    License.  (Exception: if the Program itself is interactive but
  141. X    does not normally print such an announcement, your work based on
  142. X    the Program is not required to print an announcement.)
  143. X
  144. XThese requirements apply to the modified work as a whole.  If
  145. Xidentifiable sections of that work are not derived from the Program,
  146. Xand can be reasonably considered independent and separate works in
  147. Xthemselves, then this License, and its terms, do not apply to those
  148. Xsections when you distribute them as separate works.  But when you
  149. Xdistribute the same sections as part of a whole which is a work based
  150. Xon the Program, the distribution of the whole must be on the terms of
  151. Xthis License, whose permissions for other licensees extend to the
  152. Xentire whole, and thus to each and every part regardless of who wrote it.
  153. X
  154. XThus, it is not the intent of this section to claim rights or contest
  155. Xyour rights to work written entirely by you; rather, the intent is to
  156. Xexercise the right to control the distribution of derivative or
  157. Xcollective works based on the Program.
  158. X
  159. XIn addition, mere aggregation of another work not based on the Program
  160. Xwith the Program (or with a work based on the Program) on a volume of
  161. Xa storage or distribution medium does not bring the other work under
  162. Xthe scope of this License.
  163. X
  164. X  3. You may copy and distribute the Program (or a work based on it,
  165. Xunder Section 2) in object code or executable form under the terms of
  166. XSections 1 and 2 above provided that you also do one of the following:
  167. X
  168. X    a) Accompany it with the complete corresponding machine-readable
  169. X    source code, which must be distributed under the terms of Sections
  170. X    1 and 2 above on a medium customarily used for software interchange; or,
  171. X
  172. X    b) Accompany it with a written offer, valid for at least three
  173. X    years, to give any third party, for a charge no more than your
  174. X    cost of physically performing source distribution, a complete
  175. X    machine-readable copy of the corresponding source code, to be
  176. X    distributed under the terms of Sections 1 and 2 above on a medium
  177. X    customarily used for software interchange; or,
  178. X
  179. X    c) Accompany it with the information you received as to the offer
  180. X    to distribute corresponding source code.  (This alternative is
  181. X    allowed only for noncommercial distribution and only if you
  182. X    received the program in object code or executable form with such
  183. X    an offer, in accord with Subsection b above.)
  184. X
  185. XThe source code for a work means the preferred form of the work for
  186. Xmaking modifications to it.  For an executable work, complete source
  187. Xcode means all the source code for all modules it contains, plus any
  188. Xassociated interface definition files, plus the scripts used to
  189. Xcontrol compilation and installation of the executable.  However, as a
  190. Xspecial exception, the source code distributed need not include
  191. Xanything that is normally distributed (in either source or binary
  192. Xform) with the major components (compiler, kernel, and so on) of the
  193. Xoperating system on which the executable runs, unless that component
  194. Xitself accompanies the executable.
  195. X
  196. XIf distribution of executable or object code is made by offering
  197. Xaccess to copy from a designated place, then offering equivalent
  198. Xaccess to copy the source code from the same place counts as
  199. Xdistribution of the source code, even though third parties are not
  200. Xcompelled to copy the source along with the object code.
  201. X
  202. X  4. You may not copy, modify, sublicense, or distribute the Program
  203. Xexcept as expressly provided under this License.  Any attempt
  204. Xotherwise to copy, modify, sublicense or distribute the Program is
  205. Xvoid, and will automatically terminate your rights under this License.
  206. XHowever, parties who have received copies, or rights, from you under
  207. Xthis License will not have their licenses terminated so long as such
  208. Xparties remain in full compliance.
  209. X
  210. X  5. You are not required to accept this License, since you have not
  211. Xsigned it.  However, nothing else grants you permission to modify or
  212. Xdistribute the Program or its derivative works.  These actions are
  213. Xprohibited by law if you do not accept this License.  Therefore, by
  214. Xmodifying or distributing the Program (or any work based on the
  215. XProgram), you indicate your acceptance of this License to do so, and
  216. Xall its terms and conditions for copying, distributing or modifying
  217. Xthe Program or works based on it.
  218. X
  219. X  6. Each time you redistribute the Program (or any work based on the
  220. XProgram), the recipient automatically receives a license from the
  221. Xoriginal licensor to copy, distribute or modify the Program subject to
  222. Xthese terms and conditions.  You may not impose any further
  223. Xrestrictions on the recipients' exercise of the rights granted herein.
  224. XYou are not responsible for enforcing compliance by third parties to
  225. Xthis License.
  226. X
  227. X  7. If, as a consequence of a court judgment or allegation of patent
  228. Xinfringement or for any other reason (not limited to patent issues),
  229. Xconditions are imposed on you (whether by court order, agreement or
  230. Xotherwise) that contradict the conditions of this License, they do not
  231. Xexcuse you from the conditions of this License.  If you cannot
  232. Xdistribute so as to satisfy simultaneously your obligations under this
  233. XLicense and any other pertinent obligations, then as a consequence you
  234. Xmay not distribute the Program at all.  For example, if a patent
  235. Xlicense would not permit royalty-free redistribution of the Program by
  236. Xall those who receive copies directly or indirectly through you, then
  237. Xthe only way you could satisfy both it and this License would be to
  238. Xrefrain entirely from distribution of the Program.
  239. X
  240. XIf any portion of this section is held invalid or unenforceable under
  241. Xany particular circumstance, the balance of the section is intended to
  242. Xapply and the section as a whole is intended to apply in other
  243. Xcircumstances.
  244. X
  245. XIt is not the purpose of this section to induce you to infringe any
  246. Xpatents or other property right claims or to contest validity of any
  247. Xsuch claims; this section has the sole purpose of protecting the
  248. Xintegrity of the free software distribution system, which is
  249. Ximplemented by public license practices.  Many people have made
  250. Xgenerous contributions to the wide range of software distributed
  251. Xthrough that system in reliance on consistent application of that
  252. Xsystem; it is up to the author/donor to decide if he or she is willing
  253. Xto distribute software through any other system and a licensee cannot
  254. Ximpose that choice.
  255. X
  256. XThis section is intended to make thoroughly clear what is believed to
  257. Xbe a consequence of the rest of this License.
  258. X
  259. X  8. If the distribution and/or use of the Program is restricted in
  260. Xcertain countries either by patents or by copyrighted interfaces, the
  261. Xoriginal copyright holder who places the Program under this License
  262. Xmay add an explicit geographical distribution limitation excluding
  263. Xthose countries, so that distribution is permitted only in or among
  264. Xcountries not thus excluded.  In such case, this License incorporates
  265. Xthe limitation as if written in the body of this License.
  266. X
  267. X  9. The Free Software Foundation may publish revised and/or new versions
  268. Xof the General Public License from time to time.  Such new versions will
  269. Xbe similar in spirit to the present version, but may differ in detail to
  270. Xaddress new problems or concerns.
  271. X
  272. XEach version is given a distinguishing version number.  If the Program
  273. Xspecifies a version number of this License which applies to it and "any
  274. Xlater version", you have the option of following the terms and conditions
  275. Xeither of that version or of any later version published by the Free
  276. XSoftware Foundation.  If the Program does not specify a version number of
  277. Xthis License, you may choose any version ever published by the Free Software
  278. XFoundation.
  279. X
  280. X  10. If you wish to incorporate parts of the Program into other free
  281. Xprograms whose distribution conditions are different, write to the author
  282. Xto ask for permission.  For software which is copyrighted by the Free
  283. XSoftware Foundation, write to the Free Software Foundation; we sometimes
  284. Xmake exceptions for this.  Our decision will be guided by the two goals
  285. Xof preserving the free status of all derivatives of our free software and
  286. Xof promoting the sharing and reuse of software generally.
  287. X
  288. X                            NO WARRANTY
  289. X
  290. X  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
  291. XFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
  292. XOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
  293. XPROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
  294. XOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  295. XMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
  296. XTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
  297. XPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
  298. XREPAIR OR CORRECTION.
  299. X
  300. X  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
  301. XWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
  302. XREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
  303. XINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
  304. XOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
  305. XTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
  306. XYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
  307. XPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
  308. XPOSSIBILITY OF SUCH DAMAGES.
  309. X
  310. X                     END OF TERMS AND CONDITIONS
  311. X
  312. X        Appendix: How to Apply These Terms to Your New Programs
  313. X
  314. X  If you develop a new program, and you want it to be of the greatest
  315. Xpossible use to the public, the best way to achieve this is to make it
  316. Xfree software which everyone can redistribute and change under these terms.
  317. X
  318. X  To do so, attach the following notices to the program.  It is safest
  319. Xto attach them to the start of each source file to most effectively
  320. Xconvey the exclusion of warranty; and each file should have at least
  321. Xthe "copyright" line and a pointer to where the full notice is found.
  322. X
  323. X    <one line to give the program's name and a brief idea of what it does.>
  324. X    Copyright (C) 19yy  <name of author>
  325. X
  326. X    This program is free software; you can redistribute it and/or modify
  327. X    it under the terms of the GNU General Public License as published by
  328. X    the Free Software Foundation; either version 2 of the License, or
  329. X    (at your option) any later version.
  330. X
  331. X    This program is distributed in the hope that it will be useful,
  332. X    but WITHOUT ANY WARRANTY; without even the implied warranty of
  333. X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  334. X    GNU General Public License for more details.
  335. X
  336. X    You should have received a copy of the GNU General Public License
  337. X    along with this program; if not, write to the Free Software
  338. X    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  339. X
  340. XAlso add information on how to contact you by electronic and paper mail.
  341. X
  342. XIf the program is interactive, make it output a short notice like this
  343. Xwhen it starts in an interactive mode:
  344. X
  345. X    Gnomovision version 69, Copyright (C) 19yy name of author
  346. X    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
  347. X    This is free software, and you are welcome to redistribute it
  348. X    under certain conditions; type `show c' for details.
  349. X
  350. XThe hypothetical commands `show w' and `show c' should show the appropriate
  351. Xparts of the General Public License.  Of course, the commands you use may
  352. Xbe called something other than `show w' and `show c'; they could even be
  353. Xmouse-clicks or menu items--whatever suits your program.
  354. X
  355. XYou should also get your employer (if you work as a programmer) or your
  356. Xschool, if any, to sign a "copyright disclaimer" for the program, if
  357. Xnecessary.  Here is a sample; alter the names:
  358. X
  359. X  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  360. X  `Gnomovision' (which makes passes at compilers) written by James Hacker.
  361. X
  362. X  <signature of Ty Coon>, 1 April 1989
  363. X  Ty Coon, President of Vice
  364. X
  365. XThis General Public License does not permit incorporating your program into
  366. Xproprietary programs.  If your program is a subroutine library, you may
  367. Xconsider it more useful to permit linking proprietary applications with the
  368. Xlibrary.  If this is what you want to do, use the GNU Library General
  369. XPublic License instead of this License.
  370. X
  371. END_OF_FILE
  372.   if test 18090 -ne `wc -c <'Copying'`; then
  373.     echo shar: \"'Copying'\" unpacked with wrong size!
  374.   fi
  375.   # end of 'Copying'
  376. fi
  377. if test -f 'agent/magent.SH' -a "${1}" != "-c" ; then 
  378.   echo shar: Will not clobber existing file \"'agent/magent.SH'\"
  379. else
  380.   echo shar: Extracting \"'agent/magent.SH'\" \(18483 characters\)
  381.   sed "s/^X//" >'agent/magent.SH' <<'END_OF_FILE'
  382. Xcase $CONFIG in
  383. X'')
  384. X    if test ! -f config.sh; then
  385. X        ln ../config.sh . || \
  386. X        ln ../../config.sh . || \
  387. X        ln ../../../config.sh . || \
  388. X        (echo "Can't find config.sh."; exit 1)
  389. X    fi 2>/dev/null
  390. X    . config.sh
  391. X    ;;
  392. Xesac
  393. Xcase "$0" in
  394. X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
  395. Xesac
  396. Xecho "Extracting agent/magent (with variable substitutions)"
  397. X$spitshell >magent <<!GROK!THIS!
  398. X# feed this into perl
  399. X    eval 'exec perl -S \$0 "\$@"'
  400. X        if \$running_under_some_shell;
  401. X
  402. X# You'll need to set up a .forward file that feeds your mail to this script,
  403. X# via the filter. Mine looks like this:
  404. X#   "|exec /users/ram/mail/filter >>/users/ram/.bak 2>&1"
  405. X
  406. X# $Id: magent.SH,v 2.9.1.2 92/08/26 12:41:27 ram Exp $
  407. X#
  408. X#  Copyright (c) 1991, 1992, Raphael Manfredi
  409. X#
  410. X#  You may redistribute only under the terms of the GNU General Public
  411. X#  Licence as specified in the README file that comes with dist.
  412. X#
  413. X# $Log:    magent.SH,v $
  414. X# Revision 2.9.1.2  92/08/26  12:41:27  ram
  415. X# patch8: better no-lock handling
  416. X# patch8: now maintains the notion of private library directory
  417. X# patch8: job number computation code moved to library
  418. X# patch8: added persisted variable handling
  419. X# patch8: added mailhook and interface functions with filtering actions
  420. X# 
  421. X# Revision 2.9.1.1  92/08/02  15:53:40  ram
  422. X# patch2: added undocumented -TEST flag to skip queue processing
  423. X# patch2: eval_error now returns 1 if error was detected
  424. X# 
  425. X# Revision 2.9  92/07/14  16:48:48  ram
  426. X# 3.0 beta baseline.
  427. X# 
  428. X
  429. X# Perload ON
  430. X
  431. X# The following were determined by Configure
  432. X\$phostname = '$phostname';            # Command used to compute hostname
  433. X\$mydomain = '$mydomain';            # Our domain name
  434. X\$hiddennet = '$hiddennet';            # Hidden network (advertised host)
  435. X\$maildir = '$maildir';                # Directory where mail is spooled
  436. X\$mailfile = '$mailfile';            # File in which mail is stored
  437. X\$mversion = '$VERSION';            # Current version number
  438. X\$patchlevel = '$PATCHLEVEL';        # And patchlevel from patchlevel.h
  439. X\$lock_by_flock = '$lock_by_flock';    # Want to lock mailboxes with flock ?
  440. X\$flock_only = '$flock_only';        # Only use flock() and no .lock file
  441. X\$orgname = '$orgname';                # Our organization name
  442. X\$privlib = '$privlib';                # Private mailagent library
  443. X\$inews = '$inews';
  444. X!GROK!THIS!
  445. X
  446. X$spitshell >>magent <<'!NO!SUBS!'
  447. X
  448. X$prog_name = $0;                # Who I am
  449. X$prog_name =~ s|^.*/(.*)|$1|;    # Keep only base name
  450. X$has_option = 0;                # True if invoked with options
  451. X$nolock = 0;                    # Do we need to get a lock file?
  452. X$config_file = '~/.mailagent';    # Default configuration file
  453. X$log_level = -1;                # Changed by -L option
  454. X
  455. X# Calling the mailagent as 'mailqueue' lists the queue
  456. Xif ($prog_name eq 'mailqueue') {
  457. X    ++$list_queue;
  458. X    ++$has_option;
  459. X}
  460. X
  461. X# Parse options
  462. Xwhile ($ARGV[0] =~ /^-/) {
  463. X    $_ = shift;
  464. X    last if /--/;
  465. X    if ($_ eq '-c') {        # Specify alternate configuration file
  466. X        ++$nolock;            # Immediate processing wanted
  467. X        $config_file = shift;
  468. X    }
  469. X    elsif ($_ eq '-d') {    # Dump rules
  470. X        ++$has_option;        # Incompatible with other special options
  471. X        ++$dump_rule;
  472. X    }
  473. X    elsif ($_ eq '-e') {    # Rule supplied on command line
  474. X        local($*) = 1;
  475. X        $_ = shift;
  476. X        s/\n/ /g;
  477. X        push(@Linerules, $_);
  478. X        ++$edited_rules;    # Signals rules came from command line
  479. X        ++$nolock;            # Immediate processing wanted
  480. X    }
  481. X    elsif ($_ eq '-f') {    # Take messages from UNIX mailbox
  482. X        ++$nolock;            # Immediate processing wanted
  483. X        ++$mbox_mail;
  484. X        $mbox_file = shift;    # -f followed by file name
  485. X    }
  486. X    elsif ($_ eq '-h') {    # Usage help
  487. X        &usage;
  488. X    }
  489. X    elsif ($_ eq '-i') {    # Interactive mode: log messages also on stderr
  490. X        *add_log = *stderr_log;
  491. X    }
  492. X    elsif ($_ eq '-l') {    # List queue
  493. X        ++$has_option;        # Incompatible with other special options
  494. X        ++$list_queue;
  495. X        ++$norule;            # No need to compile rules
  496. X    }
  497. X    elsif ($_ eq '-o') {    # Overwrite configuration variable
  498. X        ++$nolock;            # Immediate processing wanted
  499. X        $over_config .= "\n" . shift;
  500. X    }
  501. X    elsif ($_ eq '-q') {    # Process the queue
  502. X        ++$has_option;        # Incompatible with other special options
  503. X        ++$run_queue;
  504. X    }
  505. X    elsif ($_ eq '-r') {    # Specify alternate rule file
  506. X        ++$nolock;            # Immediate processing wanted
  507. X        $rule_file = shift;
  508. X    }
  509. X    elsif (/^-s(\S*)/) {    # Print statistics
  510. X        ++$has_option;        # Incompatible with other special options
  511. X        ++$stats;
  512. X        ++$norule;            # No need to compile rules
  513. X        $stats_opt = $1;
  514. X    }
  515. X    elsif ($_ eq '-t') {    # Track rule matches on stdout
  516. X        ++$track_all;
  517. X    }
  518. X    elsif ($_ eq '-L') {    # Specify new logging level
  519. X        $log_level = int(shift);
  520. X    }
  521. X    elsif ($_ eq '-V') {    # Version number
  522. X        print STDERR "$prog_name $mversion PL$patchlevel\n";
  523. X        exit 0;
  524. X    }
  525. X    elsif ($_ eq '-TEST') {    # Mailagent run via TEST (undocumented feature)
  526. X        ++$test_mode;
  527. X    }
  528. X    else {
  529. X        print STDERR "$prog_name: unknown option: $_\n";
  530. X        &usage;
  531. X    }
  532. X}
  533. X
  534. X++$nolock if $has_option;        # No need to take a lock with special options
  535. X
  536. X# Only one option at a time (among those options which change our goal)
  537. Xif ($has_option > 1) {
  538. X    print STDERR "$prog_name: at most one special option may be specified.\n";
  539. X    exit 1;
  540. X}
  541. X
  542. X$file_name = shift;                # File name to be processed (null if stdin)
  543. X$ENV{'IFS'}='' if $ENV{'IFS'};    # Shell separation field
  544. X&get_configuration;                # Get a suitable configuration package (cf)
  545. Xselect(STDOUT);                    # Because the -t option writes on STDOUT,
  546. X$| = 1;                            # make sure it is flushed before we fork()
  547. X$agent_wait = "agent.wait";        # Waiting file for out-of-the-queue mails
  548. X$privlib = "$cf'home/../.." if $test_mode;    # Tests ran from test/out
  549. X
  550. Xif ($orgname =~ m|^/|) {        # Name of organization kept in file
  551. X    unless (open(ORG, $orgname)) {
  552. X        &add_log("ERROR cannot read $orgname") if $loglvl;
  553. X    } else {
  554. X        chop($orgname = <ORG>);
  555. X        close ORG;
  556. X    }
  557. X}
  558. X
  559. X$ENV{'HOME'} = $cf'home;
  560. X$ENV{'USER'} = $cf'user;
  561. X$ENV{'NAME'} = $cf'name;
  562. X$baselock = "$cf'spool/perl";    # This file does not exist
  563. X$lockext = ".lock";                # Extension used by lock routines
  564. X$lockfile = $baselock . $lockext;
  565. X
  566. Xumask(077);                        # Files we create are private ones
  567. X$jobnum = &jobnum;                # Compute a job number
  568. X
  569. X# Allow only ONE mailagent at a time (resource consumming)
  570. Xdo checklock($baselock);        # Make sure old locks do not remain
  571. Xunless (-f $lockfile) {
  572. X    # Try to get the lock file (acting as a token). We do not need locking if
  573. X    # we have been invoked with an option and that option is not -q.
  574. X    if ($nolock && !$run_queue) {
  575. X        &add_log("no need to get a lock") if $loglvl > 19;
  576. X    } elsif (0 == &acs_rqst($baselock)) {
  577. X        &add_log("got the right to process mail") if $loglvl > 19;
  578. X        ++$locked;
  579. X    } else {
  580. X        &add_log("denied right to process mail") if $loglvl > 19;
  581. X    }
  582. X}
  583. X
  584. Xif (!$locked && !$nolock) {
  585. X    # Another mailagent is running somewhere
  586. X    &queue_mail($file_name);
  587. X    exit 0;
  588. X}
  589. X
  590. X# Initialize mail filtering and compile filter rule if necessary
  591. X&init_all;
  592. X&compile_rules unless $norule;
  593. X
  594. X# If rules are to be dumped, this is the only action
  595. Xif ($dump_rule) {
  596. X    &dump_rules(*print_rule_number, *void_func);
  597. X    unlink $lockfile if $locked;
  598. X    exit 0;
  599. X}
  600. X
  601. X# Likewise, statistics dumping is the only option
  602. Xif ($stats) {
  603. X    &report_stats($stats_opt);
  604. X    unlink $lockfile if $locked;
  605. X    exit 0;
  606. X}
  607. X
  608. X# Listing the queue is also the only performed action
  609. Xif ($list_queue) {
  610. X    &list_queue;
  611. X    unlink $lockfile if $locked;
  612. X    exit 0;
  613. X}
  614. X
  615. X# Taking messages from mailbox file
  616. Xif ($mbox_mail) {
  617. X    ++$run_queue if 0 == &mbox_mail($mbox_file);
  618. X    unless ($run_queue) {
  619. X        unlink $lockfile if $locked;
  620. X        exit 1;        # -f failed
  621. X    }
  622. X    &add_log("processing queued mails") if $loglvl > 15;
  623. X}
  624. X
  625. X# Suppress statistics when mailagent invoked manually (in not in test mode)
  626. X&no_stats if $nolock && !$test_mode;
  627. X
  628. X&read_stats;            # Get statistics, so that we may update them in memory
  629. X
  630. Xif (!$run_queue) {                # Do not enter here if -q
  631. X    if (0 != &analyze_mail($file_name)) {    # Analyze the mail
  632. X        do add_log("ERROR while processing main message--queing it")
  633. X            if ($loglvl > 0);
  634. X        do queue_mail($file_name);
  635. X        unlink $lockfile;
  636. X        exit 0;                    # Do not continue
  637. X    } else {
  638. X        $file = $file_name;        # Never corrupt $file_name
  639. X        $file =~ s|.*/(.*)|$1|;    # Keep only basename
  640. X        $file = "<stdin>" if $file eq '';
  641. X        do add_log("FILTERED [$file] $Header{'Length'} bytes") if $loglvl > 4;
  642. X    }
  643. X}
  644. X
  645. Xunless ($test_mode) {
  646. X    # Fork a child: we have to take care of the filter script which is waiting
  647. X    # for us to finish processing of the delivered mail.
  648. X    &fork_child() unless $run_queue;
  649. X
  650. X    # From now on, we are in the child process... Don't sleep at all if logging
  651. X    # level is greater that 11 or if $run_queue is true. Logging level of 12
  652. X    # and higher are for debugging and should not be used on a permanent basis
  653. X    # anyway.
  654. X
  655. X    $sleep = 1;                    # Give others a chance to queue their mail
  656. X    $sleep = 0 if $loglvl > 11 || $run_queue;
  657. X
  658. X    while (&pqueue) {            # Eventually process the queue
  659. X        sleep 30 if $sleep;        # Wait in case new mail arrives
  660. X    }
  661. X} else {
  662. X    &pqueue;                    # Process the queue once in test mode
  663. X}
  664. X
  665. X# End of mailagent processing
  666. X&write_stats;                    # Resynchronizes the statistics file
  667. X&contextual_operations;            # Perform all the contextual operations
  668. X&add_log("mailagent exits") if $loglvl > 17;
  669. Xunlink $lockfile if $locked;
  670. Xexit 0;
  671. X
  672. X# Print usage and exit
  673. Xsub usage {
  674. X    print STDERR <<EOF;
  675. XUsage: $prog_name [-dhilqtV] [-s{umary}] [-f file] [-e rules] [-c config]
  676. X       [-L level] [-r file] [-o def] [mailfile]
  677. X  -c : specify alternate configuration file.
  678. X  -d : dump filter rules (special).
  679. X  -e : enter rules to be applied.
  680. X  -f : get messages from UNIX-style mailbox file.
  681. X  -h : print this help message and exits.
  682. X  -i : interactive usage -- print log messages on stderr.
  683. X  -l : list message queue (special).
  684. X  -L : force logging level.
  685. X  -o : overwrite config file with supplied definition.
  686. X  -q : process the queue (special).
  687. X  -r : sepcify alternate rule file.
  688. X  -s : report gathered statistics (special).
  689. X  -t : track rules on stdout.
  690. X  -V : print version number and exits.
  691. XEOF
  692. X    exit 1;
  693. X}
  694. X
  695. X# Read configuration file and alter it with the values specified via -o.
  696. X# Then apply -r and -t by modifying suitable configuration parameters.
  697. Xsub get_configuration {
  698. X    &read_config($config_file);        # Read configuration file and set vars
  699. X    &cf'parse($over_config);        # Overwrite with command line options
  700. X    $cf'rules = $rule_file if $rule_file;        # -r overwrites rule file
  701. X    $loglvl = $log_level if $log_level >= 0;    # -L overwrites logging level
  702. X}
  703. X
  704. X#
  705. X# The filtering routines
  706. X#
  707. X
  708. X# Start-up initializations
  709. Xsub init_all {
  710. X    do init_signals();        # Trap common signals
  711. X    do init_constants();    # Constants definitions
  712. X    do init_interpreter();    # Initialize tables %Priority, %Function, ...
  713. X    do init_env();            # Initialize the %XENV array
  714. X    do init_matcher();        # Initialize special matching functions
  715. X    do init_pseudokey();    # Initialize the pseudo header keys for H table
  716. X    do init_builtins();        # Initialize built-in commands like @RR
  717. X    do init_filter();        # Initialize filter commands
  718. X    do init_special();        # Initialize special user table %Special
  719. X}
  720. X
  721. X# Protect ourselves (trap common signals)
  722. Xsub init_signals {
  723. X    $SIG{'HUP'} = 'emergency';
  724. X    $SIG{'INT'} = 'emergency';
  725. X    $SIG{'QUIT'} = 'emergency';
  726. X    $SIG{'PIPE'} = 'emergency';
  727. X    $SIG{'IO'} = 'emergency';
  728. X    $SIG{'BUS'} = 'emergency';
  729. X    $SIG{'ILL'} = 'emergency';
  730. X    $SIG{'SEGV'} = 'emergency';
  731. X    $SIG{'ALRM'} = 'emergency';
  732. X    $SIG{'TERM'} = 'emergency';
  733. X}
  734. X
  735. X# Constants definitions
  736. Xsub init_constants {
  737. X    require 'ctime.pl';
  738. X    # Values for flock(), usually in <sys/file.h>
  739. X    $LOCK_SH = 1;                # Request a shared lock on file
  740. X    $LOCK_EX = 2;                # Request an exclusive lock
  741. X    $LOCK_NB = 4;                # Make a non-blocking lock request
  742. X    $LOCK_UN = 8;                # Unlock the file
  743. X
  744. X    # Status used by filter
  745. X    $FT_RESTART = 0;            # Abort current action, restart from scratch
  746. X    $FT_CONT = 1;                # Continue execution
  747. X    $FT_REJECT = 2;                # Abort current action, continue filtering
  748. X    $FT_ABORT = 3;                # Abort filtering process
  749. X
  750. X    # Shall we append or remove folder?
  751. X    $FOLDER_APPEND = 0;            # Append in folder
  752. X    $FOLDER_REMOVE = 1;            # Remove folder
  753. X
  754. X    # Used by shell_command and children
  755. X    $NO_INPUT = 0;                # No input (stdin is closed)
  756. X    $BODY_INPUT = 1;            # Give body of mail as stdin
  757. X    $MAIL_INPUT = 2;            # Pipe the whole mail
  758. X    $HEADER_INPUT = 3;            # Pipe the header only
  759. X    $NO_FEEDBACK = 0;            # No feedback wanted
  760. X    $FEEDBACK = 1;                # Feed result of command back into %Header
  761. X    
  762. X    # The filter message
  763. X    local($address) = &email_addr;
  764. X    $FILTER =
  765. X        "X-Filter: mailagent [version $mversion PL$patchlevel] for $address";
  766. X
  767. X    # For header fields alteration
  768. X    $HD_STRIP = 0;                # Strip header fields
  769. X    $HD_KEEP = 1;                # Keep header fields
  770. X
  771. X    # Faked leading From line (used for digest items, by SPLIT)
  772. X    local($now) = &ctime(time);
  773. X    chop($now);
  774. X    $FAKE_FROM = "From mailagent " . $now;
  775. X}
  776. X
  777. X# Initializes environment. All the variables are initialized in XENV array
  778. X# The sole purpose of XENV is to be able to know what changes wrt the invoking
  779. X# environment when dumping the rules. It also avoid modifying the environment
  780. X# for our children.
  781. Xsub init_env {
  782. X    foreach (keys(%ENV)) {
  783. X        $XENV{$_} = $ENV{$_};
  784. X    }
  785. X}
  786. X
  787. X# List of special header keys which do not represent a true header field.
  788. Xsub init_pseudokey {
  789. X    %Pseudokey = (
  790. X        'Body', 1,
  791. X        'Head', 1,
  792. X        'All', 1
  793. X    );
  794. X}
  795. X
  796. X#
  797. X# Miscellaneous utilities
  798. X#
  799. X
  800. X# Attempts a mailbox locking. The argument is the name of the file, the file
  801. X# descriptor is the global MBOX, opened for appending.
  802. Xsub mbox_lock {
  803. X    local($file) = @_;                # File name
  804. X    unless ($flock_only) {            # Lock with .lock
  805. X        if (0 != &acs_rqst($file)) {
  806. X            do add_log("WARNING could not lock $file") if $loglvl > 5;
  807. X        }
  808. X    }
  809. X    # Make sure the file is still there and as not been removed while we were
  810. X    # waiting for the lock (in which case our MBOX file descriptor would be
  811. X    # useless: we would write in a ghost file!). This could happen when 'elm'
  812. X    # (or other mail user agent) resynchronizes the mailbox.
  813. X    close MBOX;
  814. X    if (open(MBOX, ">>$file")) {
  815. X        if ($lock_by_flock) {
  816. X            unless (eval 'flock(MBOX, $LOCK_EX)') {    # Ask for exclusive lock
  817. X                do add_log("WARNING could not flock $file: $!") if $loglvl > 5;
  818. X            }
  819. X        }
  820. X    } else {
  821. X        do fatal("could not reopen $file");
  822. X    }
  823. X    seek(MBOX, 0, 2);                # Someone may have appended something
  824. X}
  825. X
  826. X# Remove lock on mailbox
  827. Xsub mbox_unlock {
  828. X    local($file) = @_;                # File name
  829. X    close MBOX;                        # Closing will remove flock lock
  830. X    &free_file($file) unless $flock_only;        # Remove the .lock
  831. X}
  832. X
  833. X# Computes the e-mail address of the user
  834. Xsub email_addr {
  835. X    $cf'user . '@' . &domain_addr;        # E-mail address in internet format
  836. X}
  837. X
  838. X# Domain name address for current host
  839. Xsub domain_addr {
  840. X    local($_);                            # Our host name
  841. X    $_ = $hiddennet if $hiddennet ne '';
  842. X    if ($_ eq '') {
  843. X        chop($_ = `$phostname`);        # Must fork to get hostname, grr...
  844. X        $_ .= $mydomain unless /\./;    # We want something fully qualified
  845. X    }
  846. X    $_;
  847. X}
  848. X
  849. X# Compute the system mailbox file name
  850. Xsub mailbox_name {
  851. X    # If ~/.mailagent provides us with a mail directory, use it and possibly
  852. X    # override value computed by Configure.
  853. X    $maildir = $cf'maildrop if $cf'maildrop ne '';
  854. X    # If Configure gave a valid 'maildir', use it. Otherwise compute one now.
  855. X    unless ($maildir ne '' && -d "$maildir") {
  856. X        $maildir = "/usr/spool/mail";        # Default spooling area
  857. X        -d "/usr/mail" && ($maildir = "/usr/mail");
  858. X        -d "$maildir" || ($maildir = "$cf'home");
  859. X    }
  860. X    local($mbox) = $cf'user;                    # Default mailbox file name
  861. X    $mbox = $cf'mailbox if $cf'mailbox ne '';    # Priority to config variable
  862. X    $mailbox = "$maildir/$mbox";                # Full mailbox path
  863. X    if (! -f "$mailbox" && ! -w "$maildir") {
  864. X        # No mailbox already exists and we can't write in the spool directory.
  865. X        # Use mailfile then, and if we can't write in the directory and the
  866. X        # mail file does not exist either, use ~/mbox.$cf'user as mailbox.
  867. X        $mailbox = $mailfile;        # Determined by configure (%~ and %L form)
  868. X        $mailbox =~ s/%~/$cf'home/go;    # %~ stands for the user directory
  869. X        $mailbox =~ s/%L/$cf'user/go;    # %L stands for the user login name
  870. X        $mailbox =~ m|(.*)/.*|;            # Extract dirname
  871. X        $mailbox = "$cf'home/mbox.$cf'user" unless (-f "mailbox" || -w "$1");
  872. X        do add_log("WARNING using $mailbox for mailbox") if $loglvl > 5;
  873. X    }
  874. X    $mailbox;
  875. X}
  876. X
  877. X# Fork a new mailagent and update the pid in the perl.lock file. The parent
  878. X# then exits and the child continues. This enables the filter which invoked
  879. X# us to finally exit.
  880. Xsub fork_child {
  881. X    local($pid) = fork;
  882. X    if ($pid == -1) {                # We cannot fork, exit.
  883. X        do add_log("ERROR couldn't fork to process the queue") if $loglvl > 5;
  884. X        unlink $lockfile if $locked;
  885. X        exit 0;
  886. X    } elsif ($pid == 0) {            # The child process
  887. X        # Update the pid in the perl.lock file, so that any process which will
  888. X        # use the kill(pid, 0) feature to check whether we are alive or not will
  889. X        # get a meaningful status.
  890. X        if ($locked) {
  891. X            chmod 0644, $lockfile;
  892. X            open(LOCK, ">$lockfile");    # Ignore errors
  893. X            chmod 0444, $lockfile;        # Now it's open, so we may restore mode
  894. X            print LOCK "$$\n";            # Write child's PID
  895. X            close LOCK;
  896. X        }
  897. X        sleep(2);                    # Give filter time to clean up
  898. X    } else {                        # Parent process
  899. X        exit 0;                        # Exit without removing lock, of course
  900. X    }
  901. X    # Only the child comes here and returns
  902. X    do add_log("mailagent continues") if $loglvl > 17;
  903. X}
  904. X
  905. X# Report any eval error and returns 1 if error detected.
  906. Xsub eval_error {
  907. X    if ($@ ne '') {
  908. X        $@ =~ s/ in file \(eval\) at line \d+//;
  909. X        chop($@);
  910. X        &add_log("ERROR $@") if $loglvl > 1;
  911. X    }
  912. X    $@ eq '' ? 0 : 1;
  913. X}
  914. X
  915. X!NO!SUBS!
  916. X$grep -v '^;#' pl/jobnum.pl >>magent
  917. X$grep -v '^;#' pl/read_conf.pl >>magent
  918. X$grep -v '^;#' pl/acs_rqst.pl >>magent
  919. X$grep -v '^;#' pl/free_file.pl >>magent
  920. X$grep -v '^;#' pl/add_log.pl >>magent
  921. X$grep -v '^;#' pl/checklock.pl >>magent
  922. X$grep -v '^;#' pl/lexical.pl >>magent
  923. X$grep -v '^;#' pl/parse.pl >>magent
  924. X$grep -v '^;#' pl/analyze.pl >>magent
  925. X$grep -v '^;#' pl/runcmd.pl >>magent
  926. X$grep -v '^;#' pl/filter.pl >>magent
  927. X$grep -v '^;#' pl/matching.pl >>magent
  928. X$grep -v '^;#' pl/locate.pl >>magent
  929. X$grep -v '^;#' pl/rfc822.pl >>magent
  930. X$grep -v '^;#' pl/macros.pl >>magent
  931. X$grep -v '^;#' pl/header.pl >>magent
  932. X$grep -v '^;#' pl/actions.pl >>magent
  933. X$grep -v '^;#' pl/stats.pl >>magent
  934. X$grep -v '^;#' pl/queue_mail.pl >>magent
  935. X$grep -v '^;#' pl/pqueue.pl >>magent
  936. X$grep -v '^;#' pl/builtins.pl >>magent
  937. X$grep -v '^;#' pl/rules.pl >>magent
  938. X$grep -v '^;#' pl/period.pl >>magent
  939. X$grep -v '^;#' pl/eval.pl >>magent
  940. X$grep -v '^;#' pl/dbr.pl >>magent
  941. X$grep -v '^;#' pl/history.pl >>magent
  942. X$grep -v '^;#' pl/once.pl >>magent
  943. X$grep -v '^;#' pl/makedir.pl >>magent
  944. X$grep -v '^;#' pl/emergency.pl >>magent
  945. X$grep -v '^;#' pl/listqueue.pl >>magent
  946. X$grep -v '^;#' pl/mbox.pl >>magent
  947. X$grep -v '^;#' pl/context.pl >>magent
  948. X$grep -v '^;#' pl/extern.pl >>magent
  949. X$grep -v '^;#' pl/mailhook.pl >>magent
  950. X$grep -v '^;#' pl/interface.pl >>magent
  951. X$grep -v '^;#' pl/getdate.pl >>magent
  952. Xchmod 755 magent
  953. X$eunicefix magent
  954. END_OF_FILE
  955.   if test 18483 -ne `wc -c <'agent/magent.SH'`; then
  956.     echo shar: \"'agent/magent.SH'\" unpacked with wrong size!
  957.   fi
  958.   chmod +x 'agent/magent.SH'
  959.   # end of 'agent/magent.SH'
  960. fi
  961. if test -f 'agent/pl/analyze.pl' -a "${1}" != "-c" ; then 
  962.   echo shar: Will not clobber existing file \"'agent/pl/analyze.pl'\"
  963. else
  964.   echo shar: Extracting \"'agent/pl/analyze.pl'\" \(13269 characters\)
  965.   sed "s/^X//" >'agent/pl/analyze.pl' <<'END_OF_FILE'
  966. X;# $Id: analyze.pl,v 2.9.1.4 92/11/01 15:45:26 ram Exp $
  967. X;#
  968. X;#  Copyright (c) 1992, Raphael Manfredi
  969. X;#
  970. X;#  You may redistribute only under the terms of the GNU General Public
  971. X;#  Licence as specified in the README file that comes with dist.
  972. X;#
  973. X;# $Log:    analyze.pl,v $
  974. X;# Revision 2.9.1.4  92/11/01  15:45:26  ram
  975. X;# patch11: added some blank lines for easier reading
  976. X;# 
  977. X;# Revision 2.9.1.3  92/08/26  13:08:27  ram
  978. X;# patch8: parsing code moved to a separate library file
  979. X;# 
  980. X;# Revision 2.9.1.2  92/08/02  16:08:39  ram
  981. X;# patch2: added support for negated selectors
  982. X;# patch2: header field names are now case-normalized (to or TO -> To)
  983. X;# 
  984. X;# Revision 2.9.1.1  92/07/25  12:37:06  ram
  985. X;# patch1: did not correctly escape first From line within body
  986. X;# 
  987. X;# Revision 2.9  92/07/14  16:49:36  ram
  988. X;# 3.0 beta baseline.
  989. X;# 
  990. X;# 
  991. X#
  992. X# Analyzing mail
  993. X#
  994. X
  995. X# Special users. Note that as login name matches are done in a case-insensitive
  996. X# manner, there is no need to upper-case any of the followings.
  997. Xsub init_special {
  998. X    %Special = (
  999. X        'root', 1,                # Super-user
  1000. X        'uucp', 1,                # Unix to Unix copy
  1001. X        'daemon', 1,            # Not a real user, hopefully
  1002. X        'news', 1,                # News daemon
  1003. X        'postmaster', 1,        # X-400 mailer-daemon name
  1004. X        'newsmaster', 1,        # My convention for news administrator--RAM
  1005. X        'usenet', 1,            # Aka newsmaster
  1006. X        'mailer-daemon', 1,        # Sendmail
  1007. X        'nobody', 1                # Nobody we've heard of
  1008. X    );
  1009. X}
  1010. X
  1011. X# Initialize global variables, before analyzing each mail
  1012. Xsub init_global_state {
  1013. X    undef %Variable;            # User-defined variables
  1014. X}
  1015. X
  1016. X# This is the heart of the mail agent
  1017. X# Scan the message in $file_name and apply the filtering rules
  1018. Xsub analyze_mail {
  1019. X    local($file) = shift(@_);    # Mail file to be parsed
  1020. X    local($mode);                # Mode (optional)
  1021. X    local($wmode)= "INITIAL";    # Working mode (the mode we are in)
  1022. X    local($selector);            # Selector (mandatory)
  1023. X    local($rulentry);            # Entry in rule H table
  1024. X    local($pattern);            # Pattern for selection, as written in rules
  1025. X    local($action);                # Related action
  1026. X    local($last_selector);        # Last used selector
  1027. X    local($rules);                # A copy of the rules
  1028. X    local($matched);            # Flag set to true if a rule is matched
  1029. X    local(%Matched);            # Records the selectors which have been matched
  1030. X    local($header);                # Header entry name to look for in Header table
  1031. X    local($status);                # Status returned by xeqte
  1032. X    local($ever_matched) = 0;    # Did we ever matched a single saving rule ?
  1033. X    local($ever_saved) = 0;        # Did we ever saved a message ?
  1034. X    local($ever_seen) = 0;        # Did we ever enter seen mode ?
  1035. X    local($vacation) = 1;        # Vacation message allowed a priori
  1036. X    local(@Executed);            # Records already executed rules
  1037. X    local($selist);                # Key used to detect identical selector lists
  1038. X    local(%Inverted);            # Records inverted '!' selectors which matched
  1039. X
  1040. X    # First parse the mail and fill in the %Header table
  1041. X
  1042. X    &init_global_state;            # Initializes global variables
  1043. X    &parse_mail($file);            # Parse the mail and fill-in H tables
  1044. X    return 0 unless defined $Header{'All'};
  1045. X    &reception if $loglvl > 8;    # Log mail reception
  1046. X    &run_builtins;                # Execute builtins, if any were found
  1047. X
  1048. X    # Now analyze the mail. If there is already a X-Filter header, then the
  1049. X    # mail has already been processed. In that case, the default action is
  1050. X    # performed: leave it in the incomming mailbox with no further action.
  1051. X    # This should prevent nasty loops.
  1052. X
  1053. X    do add_log ("analyzing mail") if $loglvl > 18;
  1054. X    $header = $Header{'X-Filter'};                # Mulitple occurences possible
  1055. X    if ($header ne '') {                        # Hmm... already filtered...
  1056. X        local(@filter) = split(/\n/, $header);    # Look for each X-Filter
  1057. X        local($address) = &email_addr;            # Our e-mail address
  1058. X        local($done) = 0;                        # Already processed ?
  1059. X        foreach (@filter) {                        # Maybe we'll find ourselves
  1060. X            if (/mailagent.*for (\S+)/) {        # Mark left by us ?
  1061. X                $done = 1 if $1 eq $address;    # Yes, we did that
  1062. X                $* = 1;
  1063. X                # Remove that X-Filter line, LEAVE will add one anyway
  1064. X                $Header{'Head'} =~ s/^X-Filter:\s*mailagent.*for $address\n//;
  1065. X                $* = 0;
  1066. X                last;
  1067. X            }
  1068. X        }
  1069. X        if ($done) {            # We already processed that message
  1070. X            do add_log("NOTICE already filtered, entering seen mode")
  1071. X                if $loglvl > 5;
  1072. X            $wmode = '_SEEN_';    # This is a special mode
  1073. X            $ever_seen = 1;        # This will prevent vacation messages
  1074. X            &s_seen;            # Update statistics
  1075. X        }
  1076. X    }
  1077. X
  1078. X    # The @Executed array records whether a specified action for a rule was
  1079. X    # executed. Loops are possible via the RESTART action, and as there is
  1080. X    # almost no way to exit from such a loop (there is one with FEED and RESYNC)
  1081. X    # I decided to prohibit them. Hence a given action is allowed to be executed
  1082. X    # only once during a mail analysis (modulo each possible working mode).
  1083. X    # For a rule number n, $Executed[n] is a collection of modes in which the
  1084. X    # rule was executed, comma separated.
  1085. X
  1086. X    $Executed[$#Rules] = '';        # Pre-extend array
  1087. X
  1088. X    # Order wrt the one in the rule file is guaranteed. I use a for construct
  1089. X    # with indexed access to be able to restart from the beginning upon
  1090. X    # execution of RESTART. This also helps filling in the @Executed array.
  1091. X
  1092. X    local($i, $j);            # Indexes within rule array
  1093. X
  1094. X    rule: for ($i = 0; $i <= $#Rules; $i++) {
  1095. X        $j = $i + 1;
  1096. X        $_ = $Rules[$i];
  1097. X
  1098. X        # The %Matched array records the boolean value associated with each
  1099. X        # possible selector. If two identical selector are found, the values
  1100. X        # are OR'ed (and we stop evaluating as soon as one is true). Otherwise,
  1101. X        # the values are AND'ed (for different selectors, but all are evaluated
  1102. X        # in case we later find another identical selectors -- no sort is done).
  1103. X        # The %Inverted which records '!' selector matches has all the above
  1104. X        # rules inverted according to De Morgan's Law.
  1105. X
  1106. X        undef %Matched;                            # Reset matching patterns
  1107. X        undef %Inverted;                        # Reset negated patterns
  1108. X        $rules = $_;                            # Work on a copy
  1109. X        $rules =~ s/^(.*){// && ($mode = $1);        # First word is the mode
  1110. X        $rules =~ s/\s*(.*)}// && ($action = $1);    # Followed by action
  1111. X        $mode =~ s/\s*$//;                            # Remove trailing spaces
  1112. X        $rules =~ s/^\s+//;                        # Remove leading spaces
  1113. X        $last_selector = "";                    # Last selector used
  1114. X
  1115. X        # Make sure we are in the correct mode. The $mode variable holds a
  1116. X        # list of comma-separated modes. If the working mode is found in it
  1117. X        # then the rules apply. Otherwise, skip them.
  1118. X
  1119. X        do add_log ("in mode '$wmode' for $mode") if $loglvl > 19;
  1120. X        $mode = "," . $mode . ",";
  1121. X
  1122. X        # The special ALL mode matches anything but the other sepcial mode
  1123. X        # for already filtered messages.
  1124. X
  1125. X        unless ($mode =~ /,ALL,/) {
  1126. X            next rule unless $mode =~ /,$wmode,/;
  1127. X        } else {
  1128. X            next rule if $wmode eq '_SEEN_' && $mode !~ /,_SEEN_,/;
  1129. X        }
  1130. X
  1131. X        # Now loop over all the keys and apply the patterns in turn
  1132. X
  1133. X        &reset_backref;                        # Reset backreferences
  1134. X        foreach $key (split(/ /, $rules)) {
  1135. X            $rulentry = $Rule{$key};
  1136. X            $rulentry =~ s/^\s*([^\/]*:)// && ($selector = $1);
  1137. X            $rulentry =~ s/^\s*//;
  1138. X            $pattern = $rulentry;
  1139. X            if ($last_selector ne $selector) {    # Update last selector
  1140. X                $last_selector = $selector;
  1141. X            }
  1142. X            $selector =~ s/:$//;            # Remove final ':' on selector
  1143. X            $* = 1;                            # There can be multi-line matching
  1144. X            do add_log ("selector '$selector', pattern '$pattern'")
  1145. X                if $loglvl > 19;
  1146. X
  1147. X            # Identical (lists of) selectors are logically OR'ed. To make sure
  1148. X            # 'To Cc:' and 'Cc To:' are correctly OR'ed, the selector list is
  1149. X            # alphabetically sorted.
  1150. X
  1151. X            $selist = join(',', sort split(' ', $selector));
  1152. X
  1153. X            # Direct selectors and negated selectors (starting with a !) are
  1154. X            # kept separately, because the rules are dual:
  1155. X            # For normal selectors (kept in %Matched):
  1156. X            #  - Identical are OR'ed
  1157. X            #  - Different are AND'ed
  1158. X            # For inverted selectors (kept in %Inverted):
  1159. X            #  - Identical are AND'ed
  1160. X            #  - Different are OR'ed
  1161. X            # Multiple selectors like 'To Cc' are sorted according to the first
  1162. X            # selector on the list, i.e. 'To !Cc' is normal but '!To Cc' is
  1163. X            # inverted.
  1164. X
  1165. X            if ($selector =~ /^!/) {        # Inverted selector
  1166. X                # In order to guarantee an optimized AND, we first check that
  1167. X                # no previous failure has been reported for the current set of
  1168. X                # selectors.
  1169. X                unless (defined $Inverted{$selist} && !$Inverted{$selist}) {
  1170. X                    $Inverted{$selist} = &match($selector, $pattern);
  1171. X                }
  1172. X            } else {                        # Normal selector
  1173. X                # Here it is the OR which is guaranteed to be optimized. Do
  1174. X                # not attempt the match if an identical selector already
  1175. X                # matched sucessfully.
  1176. X                unless ($Matched{$selist}) {
  1177. X                    $Matched{$selist} = &match($selector, $pattern);
  1178. X                }
  1179. X            }
  1180. X        }
  1181. X
  1182. X        # Both groups recorded in %Matched and %Inverted are globally AND'ed
  1183. X        # However, only one match is necessary within %Inverted whilst all
  1184. X        # must have matched within %Matched...
  1185. X
  1186. X        $matched = 1;                        # Assume everything matched
  1187. X        foreach $key (keys %Matched) {        # All entries must have matched
  1188. X            $matched = 0 unless $Matched{$key};
  1189. X        }
  1190. X        if ($matched) {                        # If %Matched failed, all failed!
  1191. X            foreach $key (keys %Inverted) {    # Only one entry needs to match
  1192. X                $matched = 0 unless $Inverted{$key};
  1193. X                last if $matched;
  1194. X            }
  1195. X        }
  1196. X
  1197. X        if ($matched) {                        # Execute action if pattern matched
  1198. X            # Make sure the rule has not already been executed in that mode
  1199. X            if ($Executed[$i] =~ /,$wmode,/) {
  1200. X                do add_log("NOTICE loop detected, rule $j, state $wmode")
  1201. X                    if $loglvl > 5;
  1202. X                last rule;                    # Processing ends here
  1203. X            } else {                        # Rule was never executed
  1204. X                $Executed[$i] = ',' unless $Executed[$i];
  1205. X                $Executed[$i] .= "$wmode,";
  1206. X            }
  1207. X            $ever_matched = 1;                # At least one match
  1208. X            &add_log("MATCH on rule #$j in mode $wmode") if $loglvl > 8;
  1209. X            &track_rule($j, $wmode) if $track_all;
  1210. X            &s_match($j, $wmode);            # Record match for statistics
  1211. X            $status = do xeqte($action);
  1212. X            last rule if $status == $FT_CONT;
  1213. X            $ever_matched = 0;                # No match if REJECT or RESTART
  1214. X            next rule if $status == $FT_REJECT;
  1215. X            $i = -1;        # Restart analysis from the beginning ($FT_RESTART)
  1216. X        }
  1217. X    }
  1218. X
  1219. X    # Deal with vacation mode. It applies only on mail not previously seen.
  1220. X    # The vacation mode must be turned on in the configuration file. The
  1221. X    # conditions for a vacation message to be sent are:
  1222. X    #   - Message was directly sent to the user.
  1223. X    #   - Message does not come from a special user like root.
  1224. X    #   - Vacation message was not disabled via a VACATION command
  1225. X
  1226. X    if (!$ever_seen && $cf'vacation =~ /on/i && $vacation) {
  1227. X        unless (&special_user) {    # Not from special user and sent to me
  1228. X            # Send vacation message only once per address per period
  1229. X            &xeqte("ONCE (%r,vacation,$cf'vacperiod) MESSAGE $cf'vacfile");
  1230. X            &s_vacation;        # Message received while in vacation
  1231. X        }
  1232. X    }
  1233. X
  1234. X    # Default action if no rule ever matched
  1235. X
  1236. X    unless ($ever_matched) {
  1237. X        do add_log("NOTICE no match, leaving in mailbox") if $loglvl > 5;
  1238. X        do xeqte("LEAVE");            # Default action anyway
  1239. X        &s_default;                    # One more application of default rule
  1240. X    } else {
  1241. X        unless ($ever_saved) {
  1242. X            do add_log("NOTICE not saved, leaving in mailbox") if $loglvl > 5;
  1243. X            do xeqte("LEAVE");        # Leave if message not saved
  1244. X            &s_saved;                # Message saved by default rule
  1245. X        }
  1246. X    }
  1247. X    &s_filtered($Header{'Length'});        # Update statistics
  1248. X    0;                                    # Ok status
  1249. X}
  1250. X
  1251. X# Return true if the mail was from a special user (root, uucp...) or if the
  1252. X# mail was not directly mailed to the user (i.e. it comes from a distribution
  1253. X# list or has bounced somewhere).
  1254. Xsub special_user {
  1255. X    # Before sending the vacation message, we have to make sure the mail
  1256. X    # was sent to the user directly, through a 'To:' or a 'Cc:'. Otherwise,
  1257. X    # it must be from a mailing list or a 'Bcc:' and we don't want to
  1258. X    # send something back in that case.
  1259. X    local($matched) = do match_list("To", $cf'user);
  1260. X    $matched = do match_list("Cc", $cf'user) unless $matched;
  1261. X    unless ($matched) {
  1262. X        do add_log("mail was not directly sent to $cf'user") if $loglvl > 8;
  1263. X        return 1;
  1264. X    }
  1265. X    # If there is a Precedence: header set to either 'bulk' or 'junk', then
  1266. X    # we do not reply either.
  1267. X    local($prec) = $Header{'Precedence'};
  1268. X    if ($prec =~ /^bulk|junk/i) {
  1269. X        do add_log("mail was tagged with a '$prec' precedence") if $loglvl > 8;
  1270. X        return 1;
  1271. X    }
  1272. X    # Make sure the mail does not come from a "special" user, as listed in
  1273. X    # the %Special array (root, uucp...)
  1274. X    $matched = 0;
  1275. X    local($matched_login);
  1276. X    foreach $login (keys %Special) {
  1277. X        $matched = do match_single("From", $login);
  1278. X        $matched_login = $login if $matched;
  1279. X        last if $matched;
  1280. X    }
  1281. X    if ($matched) {
  1282. X        do add_log("mail was from special user $matched_login")
  1283. X            if $loglvl > 8;
  1284. X        return 1;
  1285. X    }
  1286. X}
  1287. X
  1288. X# Log reception of mail (sender and subject fields). This is mainly intended
  1289. X# for people like me who parse the logfile once in a while to do more 
  1290. X# statistics about mail reception. Hence the another distinction between
  1291. X# original mails and answers.
  1292. Xsub reception {
  1293. X    local($subject) = $Header{'Subject'};
  1294. X    local($sender) = $Header{'Sender'};
  1295. X    local($from) = $Header{'From'};
  1296. X    &add_log("FROM $from");
  1297. X    &add_log("VIA $sender")
  1298. X        if $sender ne '' && $sender ne (&parse_address($from))[0];
  1299. X    if ($subject ne '') {
  1300. X        if ($subject =~ s/^Re:\s*//) {
  1301. X            &add_log("REPLY $subject");
  1302. X        } else {
  1303. X            &add_log("ABOUT $subject");
  1304. X        }
  1305. X    }
  1306. X    print "-------- From $from\n" if $track_all;
  1307. X}
  1308. X
  1309. X# Print match on STDOUT when -t option is used
  1310. Xsub track_rule {
  1311. X    local($number, $mode) = @_;
  1312. X    print "*** Match on rule $number in mode $mode ***\n";
  1313. X    &print_rule($number);
  1314. X}
  1315. X
  1316. END_OF_FILE
  1317.   if test 13269 -ne `wc -c <'agent/pl/analyze.pl'`; then
  1318.     echo shar: \"'agent/pl/analyze.pl'\" unpacked with wrong size!
  1319.   fi
  1320.   # end of 'agent/pl/analyze.pl'
  1321. fi
  1322. if test -f 'agent/test/filter/list.t' -a "${1}" != "-c" ; then 
  1323.   echo shar: Will not clobber existing file \"'agent/test/filter/list.t'\"
  1324. else
  1325.   echo shar: Extracting \"'agent/test/filter/list.t'\" \(1475 characters\)
  1326.   sed "s/^X//" >'agent/test/filter/list.t' <<'END_OF_FILE'
  1327. X# This tests mathching on list selectors like To or Newsgroups.
  1328. Xdo '../pl/filter.pl';
  1329. X
  1330. Xfor ($i = 1; $i <= 8; $i++) {
  1331. X    unlink "$user.$i";
  1332. X}
  1333. X
  1334. X&add_header('X-Tag: list');
  1335. X`$cmd`;
  1336. X$? == 0 || print "1\n";
  1337. X-f "$user.1" || print "2\n";
  1338. Xunlink "$user.1";
  1339. X
  1340. X&replace_header('To: uunet!eiffel.com!max, other@max.com');
  1341. X`$cmd`;
  1342. X$? == 0 || print "3\n";
  1343. X-f "$user.2" || print "4\n";
  1344. Xunlink "$user.2";
  1345. X
  1346. X&replace_header('To: root@eiffel.com (Super User), max <other@max.com>');
  1347. X`$cmd`;
  1348. X$? == 0 || print "5\n";
  1349. X-f "$user.3" || print "6\n";
  1350. Xunlink "$user.3";
  1351. X
  1352. X# Following is illeaal in RFC-822: should be "root@eiffel.com" <maxime>
  1353. X&replace_header('To: riot@eiffel.com (Riot Manager), root@eiffel.com <maxime>');
  1354. X`$cmd`;
  1355. X$? == 0 || print "7\n";
  1356. X-f "$user.4" || print "8\n";
  1357. Xunlink "$user.4";
  1358. X
  1359. X&replace_header('To: other, me, riotintin@eiffel.com, and, so, on');
  1360. X`$cmd`;
  1361. X$? == 0 || print "9\n";
  1362. X-f "$user.5" || print "10\n";
  1363. Xunlink "$user.5";
  1364. X
  1365. X&replace_header('To: other, me, chariot@eiffel.com, and, so, on');
  1366. X`$cmd`;
  1367. X$? == 0 || print "11\n";
  1368. X-f "$user.6" || print "12\n";
  1369. Xunlink "$user.6";
  1370. X
  1371. X&replace_header('To: other, me, abricot@eiffel.com, and, so, on');
  1372. X&add_header('Newsgroups: comp.lang.perl, news.groups, news.lists');
  1373. X`$cmd`;
  1374. X$? == 0 || print "13\n";
  1375. X-f "$user.7" || print "14\n";
  1376. Xunlink "$user.7";
  1377. X
  1378. X&replace_header('Newsgroups: comp.lang.perl, news.groups, news.answers');
  1379. X`$cmd`;
  1380. X$? == 0 || print "15\n";
  1381. X-f "$user.8" || print "16\n";
  1382. Xunlink "$user.8";
  1383. X
  1384. Xunlink 'mail';
  1385. Xprint "0\n";
  1386. END_OF_FILE
  1387.   if test 1475 -ne `wc -c <'agent/test/filter/list.t'`; then
  1388.     echo shar: \"'agent/test/filter/list.t'\" unpacked with wrong size!
  1389.   fi
  1390.   # end of 'agent/test/filter/list.t'
  1391. fi
  1392. echo shar: End of archive 8 \(of 17\).
  1393. cp /dev/null ark8isdone
  1394. MISSING=""
  1395. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ; do
  1396.     if test ! -f ark${I}isdone ; then
  1397.     MISSING="${MISSING} ${I}"
  1398.     fi
  1399. done
  1400. if test "${MISSING}" = "" ; then
  1401.     echo You have unpacked all 17 archives.
  1402.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1403. else
  1404.     echo You still must unpack the following archives:
  1405.     echo "        " ${MISSING}
  1406. fi
  1407. exit 0
  1408. exit 0 # Just in case...
  1409.