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

  1. Newsgroups: comp.sources.misc
  2. From: Raphael Manfredi <ram@acri.fr>
  3. Subject: v41i001:  mailagent - Flexible mail filtering and processing package, v3.0, Part01/26
  4. Message-ID: <csm-v41i001=mailagent.073303@sparky.Sterling.COM>
  5. X-Md4-Signature: 104b515e8cb1c79995e2798cf7c1ac51
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Advanced Computer Research Institute, Lyon, France.
  8. Date: Thu, 2 Dec 1993 13:33:32 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: Raphael Manfredi <ram@acri.fr>
  12. Posting-number: Volume 41, Issue 1
  13. Archive-name: mailagent/part01
  14. Environment: UNIX, Perl
  15. Supersedes: mailagent: Volume 33, Issue 93-109
  16.  
  17. This is a mailagent program, and it will take care of all your incoming
  18. mail by applying a set of rules and trying to figure out what to do with
  19. it. A message can be saved in a folder, left in the main mailbox, posted
  20. to a newsgroup, forwarded to other people, split if it is a digest,
  21. etc... You may even delete all those mails you do not wish to see, ever.
  22.  
  23. Mailagent is easily extensible if you speak perl fluently enough and have
  24. the courage to dive into the manual page. It also provides you with all
  25. the tools to turn it into a mail server, which can be remotely managed.
  26.  
  27. Here is a summary of the changes that occurred since mailagent 2.9 PL19:
  28.  
  29. . Mailhook disappears. Folder hooks are now handled without the need for an
  30.   extra process.
  31.  
  32. . NOTIFY now takes its FIRST argument to indicate the message file,
  33.   instead of its LAST as in the 2.9 release. This change in order to make
  34.   it compatible with MESSAGE.
  35.  
  36. . Mailagent secure configuration checks. Impossible to use mailagent if the
  37.   ~/.mailagent file or the rule file are not correctly protected.
  38.  
  39. . Dynamic loading interface (dynload.pl) available for perl commands.
  40.  
  41. . Added a generic command server. Mailagent provides the server engine and
  42.   users write their own commands, with special provision for perl scripts
  43.   which can be directly loaded and executed within mailagent itself.
  44.  
  45. . User-defined macro support %-(x) and perl interface.
  46.  
  47. . New APPLY, REQUIRE, SERVER, MACRO commands.
  48.  
  49. . Support for rule caching. This avoids recompiling large rule files at every
  50.   mailagent run, but speed has never never been a main concern in this program
  51.   anyway.
  52.  
  53. . Negated mode support <!MODE>. Rule is not executed if in the specified
  54.   negated mode. This supersedes normal modes, i.e. <MODE, !MODE> is never
  55.   executed.
  56.  
  57. . Can now configure sendmail process and inews, with options, from ~/.mailagent.
  58.   If your sendmail behaves strangely or want to have interactive delivery
  59.   instead of queuing, this is the place to look at.
  60.  
  61. . New usr_log facility, enabling user-defined logfiles. Available for your
  62.   own commands and used internally by mailagent.
  63.  
  64. . Saving operations now check on the size of the produced folder for NFS.
  65.  
  66. . Can now access ~/.mailagent config params via %=var
  67.  
  68. . Fixed bug in agent queue parsing. This happened mainly on SUN systems, and
  69.   was apparently a perl fileglob bug (or is it a /bin/csh bug?). Anyway, I
  70.   now use readdir() to access the queue, which suppresses forking of an extra
  71.   process.
  72.  
  73. . Improved RFC822 address parsing. Now understands group names as login names.
  74.  
  75. . Output for mailagent -d formatted differently.
  76.  
  77. . Selector range Body <1,4>: available. This example selects body lines 1 to
  78.   4 (inclusive) for matching.
  79.  
  80. . Can now deliver to MH folders (without the need for an extra process). Use
  81.   'SAVE +foo' to deliver to the MH folder foo. Unseen sequences specified in
  82.   your ~/.mh_profile are correctly updated.
  83.  
  84. . Minimal support for directory hooks (only behaves like MH folders currently).
  85.  
  86. . New @SH package command for dist-3.0 MailAuthor.U support. That metaconfig
  87.   units sends a mail in specific format to record users of some package, and
  88.   the package command is there to automate the process.
  89.  
  90. #! /bin/sh
  91. # This is a shell archive.  Remove anything before this line, then feed it
  92. # into a shell via "sh file" or similar.  To overwrite existing files,
  93. # type "sh file -c".
  94. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  95. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  96. # Contents:  PACKNOTES README PACKLIST agent/ agent/examples/
  97. #   agent/files/ agent/files/help agent/files/server agent/filter/
  98. #   agent/man/ agent/pl/ agent/pl/stats.pl agent/test/
  99. #   agent/test/basic/ agent/test/cmd/ agent/test/filter/
  100. #   agent/test/misc/ agent/test/option/ agent/test/pl/ bin/ misc/
  101. #   misc/shell/ misc/unkit/
  102. # Wrapped by ram@soft208 on Mon Nov 29 16:49:54 1993
  103. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  104. echo If this archive is complete, you will see the following message:
  105. echo '          "shar: End of archive 1 (of 26)."'
  106. if test -f 'PACKNOTES' -a "${1}" != "-c" ; then 
  107.   echo shar: Will not clobber existing file \"'PACKNOTES'\"
  108. else
  109.   echo shar: Extracting \"'PACKNOTES'\" \(389 characters\)
  110.   sed "s/^X//" >'PACKNOTES' <<'END_OF_FILE'
  111. X
  112. X# "Configure" was split into 2 parts; to create it, do
  113. X    cat Configure.0[1-9] >Configure
  114. X    chmod +x Configure
  115. X
  116. X# "agent/man/mailagent.SH" was split into 4 parts; to create it, do
  117. X    cat agent/man/mailagent.SH.0[1-9] >agent/man/mailagent.SH
  118. X    chmod +x agent/man/mailagent.SH
  119. X
  120. X# "agent/pl/actions.pl" was split into 2 parts; to create it, do
  121. X    cat agent/pl/actions.pl.0[1-9] >agent/pl/actions.pl
  122. END_OF_FILE
  123.   if test 389 -ne `wc -c <'PACKNOTES'`; then
  124.     echo shar: \"'PACKNOTES'\" unpacked with wrong size!
  125.   fi
  126.   # end of 'PACKNOTES'
  127. fi
  128. if test -f 'README' -a "${1}" != "-c" ; then 
  129.   echo shar: Will not clobber existing file \"'README'\"
  130. else
  131.   echo shar: Extracting \"'README'\" \(6197 characters\)
  132.   sed "s/^X//" >'README' <<'END_OF_FILE'
  133. X                           mailagent 3.0
  134. X
  135. X              Copyright (c) 1990-1993, Raphael Manfredi
  136. X
  137. X------------------------------------------------------------------------
  138. X    This program is free software; you can redistribute it and/or modify
  139. X    it under the terms of the Artistic License, a copy of which can be
  140. X    found with this package.
  141. X
  142. X    This program is distributed in the hope that it will be useful,
  143. X    but WITHOUT ANY WARRANTY; without even the implied warranty of
  144. X    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  145. X    Artistic License for more details.
  146. X------------------------------------------------------------------------
  147. X
  148. XPlease read all the directions below before you proceed any further, and
  149. Xthen follow them carefully.
  150. X
  151. XAfter you have unpacked your kit, you should have all the files listed
  152. Xin MANIFEST.
  153. X========================================================================
  154. X
  155. XThis is a mailagent program, and it will take care of all your incoming
  156. Xmail by applying a set of rules and trying to figure out what to do with
  157. Xit. A message can be saved in a folder, left in the main mailbox, posted
  158. Xto a newsgroup, forwarded to other people, split if it is a digest,
  159. Xetc... You may even delete all those mails you do not wish to see, ever.
  160. X
  161. XFiltering rules are specified using lex-style rules, i.e. they have
  162. Xa set of patterns in the left hand-side (lhs) and a set of actions within
  163. X{} braces on the right hand-side (rhs). Pattern on the lhs are applied
  164. Xin sequence until one match occurs, at which time the rhs is executed.
  165. XNormally the first match stops the processing, but that may be changed.
  166. X
  167. XAs in lex, the filtering automaton supports the notion of modes, each
  168. Xrule belonging to a set of modes and being applied only when the current
  169. Xworking mode matches one of the modes associated with the rule.
  170. X
  171. XIf you do not install any filtering rules, then some default hardwired
  172. Xrules apply. Those simply leave all the messages in your mailbox, but
  173. Xprocess mails whose Subject line is Command (@SH hooks). You may
  174. Xoverride this default behavior by writing your own set of rules,
  175. Xand maybe disable this processing entirely.
  176. X
  177. XI have included in the subdirectory 'examples' a set of files which are
  178. Xpart of my own mail environment, in the hope that they will be useful.
  179. XIn particular, there is a heavily documented rule file, which is a copy
  180. Xof the one I am currently using, comments excepted...
  181. X
  182. X(This paragraph only matters if you decide to use the PROCESS command.)
  183. XThe mailhelp, maillist, mailpatch and maillist programs are *old* and
  184. Xwould need some clean up. They require you to have the kit program
  185. Xand cshar; those two programs have been posted to comp.sources.unix.
  186. XYou may want to retrieve them via my mailagent if you can't find them.
  187. X
  188. XAny feedback on this program will be appreciated. However, please make
  189. Xsure to introduce the word 'mailagent' in the subject of your message,
  190. Xso that the new rule I am about to add to my ~/.rules may correctly
  191. Xredirect your message into a high priority folder :-)
  192. X
  193. X
  194. XThere is a mailing list hosted in Japan and set up by Shigeya Suzuki
  195. X<shigeya@foretune.co.jp>, for discussion about the mailagent package as
  196. Xa whole. It's a good place to ask questions (or answer them) and to
  197. Xsend your patches. I will post official patches to the net, as well
  198. Xas to the agent-users list.
  199. X
  200. XTo send a mail to the list, address it to <agent-users@foretune.co.jp>.
  201. XTo subscribe, send a mail to <majordomo@foretune.co.jp>. If you don't
  202. Xknow how to use majordomo, the syntax of the subscribe command is:
  203. X
  204. X    subscribe agent-users [address]
  205. X
  206. Xwhere the address part is optional. You may unsubscribe automatically
  207. Xat any time by sending:
  208. X
  209. X    unsubscribe agent-users
  210. X
  211. XIf you have a problem with this version of mailagent, it is recommended
  212. Xthat you subscribe to the list, then send a description of your problem to
  213. Xit. If you send mail to me personally, I may not be able to answer in a
  214. Xtimely fashion.
  215. X
  216. XThis mailing list has low traffic (a few articles per week, typically),
  217. Xand it is expected to remain so, with a high signal/noise ratio.
  218. X
  219. XNotes:
  220. X    Raphael Manfredi <ram@acri.fr>
  221. X    Lyon, France, December 1st 1993
  222. X
  223. X========================================================================
  224. X
  225. XINSTALLATION
  226. X
  227. X1) Run Configure. This will figure out various things about your
  228. Xsystem. After it has completed, it will produce config.h and config.sh.
  229. X
  230. XYou might possibly have to trim # comments from the front of Configure
  231. Xif your shell doesn't handle them, but all other comments will be taken
  232. Xcare of.
  233. X
  234. X2) Run make.
  235. X
  236. X3) If make succeeded, you may wish to do "make install install.man". Be
  237. Xsure your rights are correct (if you install manual pages, you may need
  238. Xsuper-user privileges). By not running "make install.man", you avoid the
  239. Xinstallation of the manual pages.
  240. X
  241. X4) Read the manual entry before running.
  242. X
  243. X5) IMPORTANT!  Communicate any problem and suggested patches to me,
  244. Xram@acri.fr (Raphael Manfredi), so we can keep this distribution in
  245. Xsync.  If you have a problem, there will be someone else who had it or
  246. Xwill have it too...
  247. X
  248. XIf possible, send me patches such that the patch program will apply
  249. Xthem.  Context diffs are the best, then normal diffs.  Do not send ed
  250. Xscripts, I have probably changed my copy since the version you got.
  251. X
  252. X6) After everything is installed, you can do make clobber. This will
  253. Xclean up everything and let you re-distribute this kit, without
  254. Xcarrying useless files. You should keep this distribution intact, so
  255. Xthat future patches will be applyable.
  256. X
  257. X7) I have an automatic patch sender. Send me the following mail:
  258. X
  259. X    Subject: Command
  260. X    @SH mailhelp PATH
  261. X
  262. Xand you'll get instructions (PATH stands for YOUR e-mail address, either
  263. Xin INTERNET or in bang notation). I would recommend you to get all the
  264. Xissued patches before you start making some modifications on this
  265. Xpackage.
  266. X
  267. X8) If you wish to de-install the package, you may run "make deinstall".
  268. XA separate "make deinstall.man" will remove the manual pages. Be sure
  269. Xthe makefiles are correctly set before running any deinstall target.
  270. XOn USG systems, some executable have a chance to remain despite the
  271. Xdeinstall (text file busy...).
  272. X
  273. X    Raphael Manfredi <ram@acri.fr>
  274. X
  275. END_OF_FILE
  276.   if test 6197 -ne `wc -c <'README'`; then
  277.     echo shar: \"'README'\" unpacked with wrong size!
  278.   fi
  279.   # end of 'README'
  280. fi
  281. if test -f 'PACKLIST' -a "${1}" != "-c" ; then 
  282.   echo shar: Will not clobber existing file \"'PACKLIST'\"
  283. else
  284.   echo shar: Extracting \"'PACKLIST'\" \(16462 characters\)
  285.   sed "s/^X//" >'PACKLIST' <<'END_OF_FILE'
  286. X   File Name        Archive #    Description
  287. X----------------------------------------------------------
  288. XPACKNOTES                  1    Warnings about long lines, etc
  289. XREADME                     1    Basic instructions
  290. XMANIFEST                  12    This list of files
  291. XArtistic                  18    The Artistic Licence
  292. XChanges                   20    User-visible changes between 2.9 and 3.0
  293. XConfigure.01               6    Portability tool (part 1)
  294. XConfigure.02               7    Portability tool (part 2)
  295. XCredits                   17    Traditional "thank you" list
  296. XJmakefile                 22    Description of the main Makefile
  297. XMakefile.SH               18    A makefile to run subsidiary makefiles
  298. XPACKLIST                   1    This shipping list
  299. Xagent/                     1    Where mailagent support files are located
  300. Xagent/Jmakefile           22    High level description of Makefile
  301. Xagent/Makefile.SH         16    Makefile which builds and installs mailagent
  302. Xagent/README              25    Welcome to mailagent
  303. Xagent/examples/            1    A set of files from my own environment
  304. Xagent/examples/README     23    Explains what the examples are
  305. Xagent/examples/daemon     25    Rules for "vacation" emulation
  306. Xagent/examples/mailfolders 25    A copy of my ~/.mailfolders
  307. Xagent/examples/mchk       24    Checks for new mail
  308. Xagent/examples/mhinc      25    Call the MH inc command to incorporate new mail
  309. Xagent/examples/nocmds     25    Message you currently get if you send me a command
  310. Xagent/examples/profile    23    What I added to my onw ~/.profile
  311. Xagent/examples/rules      15    The rules I am currently using
  312. Xagent/examples/vacation   25    A sample vacation message
  313. Xagent/files/               1    Mailagent's configuration files
  314. Xagent/files/Jmakefile     25    High level description for Makefile
  315. Xagent/files/Makefile.SH   18    Makefile for subsidiary files
  316. Xagent/files/README        23    Notes about files found in this directory
  317. Xagent/files/agenthelp     17    Help file used by mailhelp
  318. Xagent/files/chkagent.sh   20    Cron script to spot problems in the mailagent system
  319. Xagent/files/commands      25    Allowed commands for mailagent
  320. Xagent/files/distribs      25    Example of distribution list
  321. Xagent/files/filter.sh     16    Shell script version of the mail filter
  322. Xagent/files/help           1    Directory holding SERVER help files
  323. Xagent/files/help/Jmakefile 24    Generic makefile for help directory
  324. Xagent/files/help/Makefile.SH 19    Generated makefile
  325. Xagent/files/help/README   25    States what this directory is about
  326. Xagent/files/help/addauth.SH 22    Help file for addauth
  327. Xagent/files/help/approve.SH 22    Help file for approve
  328. Xagent/files/help/delpower.SH 22    Help file for delpower
  329. Xagent/files/help/end.SH   22    Help file for end
  330. Xagent/files/help/getauth.SH 22    Help file for getauth
  331. Xagent/files/help/help.SH  23    Help file for help
  332. Xagent/files/help/newpower.SH 21    Help file for newpower
  333. Xagent/files/help/passwd.SH 23    Help file for passwd
  334. Xagent/files/help/password.SH 22    Help file for password
  335. Xagent/files/help/power.SH 22    Help file for power
  336. Xagent/files/help/release.SH 21    Help file for release
  337. Xagent/files/help/remauth.SH 22    Help file for remauth
  338. Xagent/files/help/set.SH   23    Help file for set
  339. Xagent/files/help/setauth.SH 22    Help file for setauth
  340. Xagent/files/help/user.SH  21    Help file for user
  341. Xagent/files/mailagent.cf  19    Example of configuration file
  342. Xagent/files/passwd        25    An example for power password file
  343. Xagent/files/proglist       3    Example of description file
  344. Xagent/files/server         1    An example for server command file
  345. Xagent/filter/              1    The C version of the mail filter
  346. Xagent/filter/Jmakefile    24    Generic makefile template
  347. Xagent/filter/Makefile.SH  18    Makefile for C filter
  348. Xagent/filter/README        4    Introduction to filter
  349. Xagent/filter/environ.c    17    Environment management routines
  350. Xagent/filter/environ.h    22    Declarations for environment management routines
  351. Xagent/filter/hash.c        8    Symbol table handling
  352. Xagent/filter/hash.h       21    Declarations for symbol table
  353. Xagent/filter/io.c         11    I/O routines
  354. Xagent/filter/io.h         24    Header for I/O routines
  355. Xagent/filter/lock.c       18    File locking
  356. Xagent/filter/lock.h       24    Declarations for file locking routines
  357. Xagent/filter/logfile.c    18    Logging facilities
  358. Xagent/filter/logfile.h    22    Header for logging routines
  359. Xagent/filter/main.c       19    The main entry point for filter
  360. Xagent/filter/misc.c       23    Miscellaneous routines
  361. Xagent/filter/msg.c        21    Handles fatal messages
  362. Xagent/filter/msg.h        25    Declarations for user messages
  363. Xagent/filter/parser.c     11    Parse the config file with variable substitutions
  364. Xagent/filter/parser.h     23    About config file parsing
  365. Xagent/filter/portable.h   22    Portable declarations
  366. Xagent/filter/sysexits.h   23    Standard exit codes
  367. Xagent/filter/user.c       21    To get login name from user
  368. Xagent/magent.SH           10    The main processor
  369. Xagent/maildist.SH         13    Mails a whole distribution
  370. Xagent/mailhelp.SH         21    Mails some help
  371. Xagent/maillist.SH         15    Mails a list of available distributions
  372. Xagent/mailpatch.SH        14    Mails patches for a given distribution
  373. Xagent/man/                 1    Manual pages for mailagent
  374. Xagent/man/Jmakefile       25    Makefile description for jmake
  375. Xagent/man/Makefile.SH     19    Makefile for manual pages extraction
  376. Xagent/man/mailagent.SH.01  4    Produces a manual page for mailagent (part 1)
  377. Xagent/man/mailagent.SH.02  2    Produces a manual page for mailagent (part 2)
  378. Xagent/man/mailagent.SH.03  3    Produces a manual page for mailagent (part 3)
  379. Xagent/man/mailagent.SH.04 13    Produces a manual page for mailagent (part 4)
  380. Xagent/man/maildist.SH     24    Produces a manual page for maildist
  381. Xagent/man/mailhelp.SH     18    Produces a manual page for mailhelp
  382. Xagent/man/maillist.SH     24    Produces a manual page for maillist
  383. Xagent/man/mailpatch.SH    24    Produces a manual page for mailpatch
  384. Xagent/man/package.SH      20    Produces a manual page for package
  385. Xagent/package.SH          15    Records users of a PD package (cf dist-3.0)
  386. Xagent/pl/                  1    Perl files used by mailagent scripts
  387. Xagent/pl/acs_rqst.pl      20    Perl library to ask for private file access
  388. Xagent/pl/actions.pl.01     5    Implementation of mailagent's actions (part 1)
  389. Xagent/pl/actions.pl.02    25    Implementation of mailagent's actions (part 2)
  390. Xagent/pl/add_log.pl        7    Perl library to add logs to logfile
  391. Xagent/pl/analyze.pl       12    Perl library analyzing the incoming mail
  392. Xagent/pl/builtins.pl      19    Perl library dealing with builtins
  393. Xagent/pl/checklock.pl     23    Perl library to check for long lasting locks
  394. Xagent/pl/cmdserv.pl        8    Implements generic mail server
  395. Xagent/pl/compress.pl      16    Folder compression library
  396. Xagent/pl/context.pl       20    Mailagent context file handling
  397. Xagent/pl/dbr.pl           10    Internal database management
  398. Xagent/pl/distribs.pl      20    Perl library to scan the distribs file
  399. Xagent/pl/dynload.pl       20    Dynamically loads perl code into mailagent
  400. Xagent/pl/emergency.pl     18    Perl library dealing with emergencies
  401. Xagent/pl/eval.pl          18    A little expression interpreter
  402. Xagent/pl/extern.pl        23    Perl library to handle persistent variables
  403. Xagent/pl/fatal.pl         25    Perl library to deal with fatal errors
  404. Xagent/pl/file_edit.pl     15    File edition with extensive error checking
  405. Xagent/pl/filter.pl         9    Running the filtering commands
  406. Xagent/pl/free_file.pl     23    Perl library to free file access
  407. Xagent/pl/gensym.pl        25    Dynamic symbol generator
  408. Xagent/pl/getdate.pl        9    Richard Ohnemus's getdate package
  409. Xagent/pl/header.pl        17    Header-related routines
  410. Xagent/pl/history.pl       20    Perl library to implement history mechanism
  411. Xagent/pl/hook.pl          16    Mail hook wrapping functions
  412. Xagent/pl/hostname.pl      24    Perl library to compute hostname
  413. Xagent/pl/include.pl       21    Processing of "include file" requests
  414. Xagent/pl/interface.pl     12    Perl interface with filter commands
  415. Xagent/pl/jobnum.pl        24    Perl library to compute a job number
  416. Xagent/pl/lexical.pl       17    Perl library for lexical analysis
  417. Xagent/pl/listqueue.pl     13    Perl library to list the queue
  418. Xagent/pl/locate.pl        23    Perl library to locate loaded patterns/addresses
  419. Xagent/pl/macros.pl        16    Perl library for macros expansion
  420. Xagent/pl/mailhook.pl      21    Initializing and running hooks
  421. Xagent/pl/makedir.pl       24    Perl library for making a directory
  422. Xagent/pl/matching.pl      11    Matching routines used by filter
  423. Xagent/pl/mbox.pl          20    Getting mails from a mailbox file
  424. Xagent/pl/mh.pl            15    Handles MH-style folder delivery
  425. Xagent/pl/mmdf.pl          19    MMDF-style mailbox handling
  426. Xagent/pl/newcmd.pl        17    Filter command extension driver
  427. Xagent/pl/once.pl          21    Dealing with once commands
  428. Xagent/pl/parse.pl         16    Perl library to parse a mail message
  429. Xagent/pl/period.pl        22    Perl library to compute periods
  430. Xagent/pl/plsave.pl        14    Perl library to handle the plsave cache file
  431. Xagent/pl/plural.pl        24    Perl library to pluralize words
  432. Xagent/pl/power.pl         15    Power management for mail server
  433. Xagent/pl/pqueue.pl        20    Processing the queued mails
  434. Xagent/pl/q.pl             25    Quote removal function
  435. Xagent/pl/queue_mail.pl    14    Queuing mails
  436. Xagent/pl/rangeargs.pl     23    Perl library to expand a list of patches
  437. Xagent/pl/read_conf.pl     19    Perl library to read configuration file
  438. Xagent/pl/rfc822.pl        19    Perl library to parse RFC822 addresses
  439. Xagent/pl/rules.pl         12    Compiles the filtering rules
  440. Xagent/pl/runcmd.pl        13    Filter commands ran from here
  441. Xagent/pl/secure.pl        20    Make sure a file is "secure" and can be trusted
  442. Xagent/pl/sendfile.pl      14    Perl library to send files in shar / kit mode
  443. Xagent/pl/stats.pl          1    Mailagent's statistics recording and printing
  444. Xagent/pl/tilde.pl         25    Perl library to perform ~name expansion
  445. Xagent/pl/unpack.pl         2    Perl library to unpack archive files
  446. Xagent/pl/usrmac.pl        14    User-defined macros
  447. Xagent/test/                1    Regression test suite
  448. Xagent/test/Jmakefile      23    Generic makefile for test suite
  449. Xagent/test/Makefile.SH    17    Makefile for test suite
  450. Xagent/test/README         20    About the regression tests
  451. Xagent/test/TEST           19    Runs the full test suite
  452. Xagent/test/actions        17    Rule file for cmd tests
  453. Xagent/test/basic/          1    Basic tests
  454. Xagent/test/basic/config.t 20    Main test initialization and sanity checks
  455. Xagent/test/basic/filter.t 21    Make sure C filter works
  456. Xagent/test/basic/mailagent.t  9    Make sure mailagent basically works
  457. Xagent/test/cmd/            1    Tests of mailagent's filtering commands
  458. Xagent/test/cmd/abort.t    25    Test ABORT command
  459. Xagent/test/cmd/annotate.t 24    Test ANNOTATE command
  460. Xagent/test/cmd/apply.t    22    Test APPLY command
  461. Xagent/test/cmd/assign.t   24    Test ASSIGN command
  462. Xagent/test/cmd/back.t     24    Test BACK command
  463. Xagent/test/cmd/begin.t    24    Test BEGIN command
  464. Xagent/test/cmd/bounce.t   23    Test BOUNCE command
  465. Xagent/test/cmd/delete.t   25    Test DELETE command
  466. Xagent/test/cmd/feed.t     25    Test FEED command
  467. Xagent/test/cmd/forward.t  22    Test FORWARD command
  468. Xagent/test/cmd/give.t     24    Test GIVE command
  469. Xagent/test/cmd/keep.t     21    Test KEEP command
  470. Xagent/test/cmd/leave.t    22    Test LEAVE command
  471. Xagent/test/cmd/macro.t    25    Test MACRO command
  472. Xagent/test/cmd/message.t  23    Test MESSAGE command
  473. Xagent/test/cmd/nop.t      25    Test NOP command
  474. Xagent/test/cmd/notify.t   22    Test NOTIFY command
  475. Xagent/test/cmd/once.t     21    Test ONCE command
  476. Xagent/test/cmd/pass.t     24    Test PASS command
  477. Xagent/test/cmd/perl.t     23    Test PERL command
  478. Xagent/test/cmd/pipe.t     24    Test PIPE command
  479. Xagent/test/cmd/post.t     23    Test POST command
  480. Xagent/test/cmd/process.t  25    Test PROCESS command
  481. Xagent/test/cmd/purify.t   24    Test PURIFY command
  482. Xagent/test/cmd/queue.t    25    Test QUEUE command
  483. Xagent/test/cmd/record.t   21    Test RECORD command
  484. Xagent/test/cmd/reject.t   24    Test REJECT command
  485. Xagent/test/cmd/require.t  24    Test REQUIRE command
  486. Xagent/test/cmd/restart.t  24    Test RESTART command
  487. Xagent/test/cmd/resync.t   25    Test RESYNC command
  488. Xagent/test/cmd/run.t      25    Test RUN command
  489. Xagent/test/cmd/save.t     22    Test SAVE command
  490. Xagent/test/cmd/select.t   24    Test SELECT command
  491. Xagent/test/cmd/server.t   25    Test SERVER command
  492. Xagent/test/cmd/split.t    16    Test SPLIT command
  493. Xagent/test/cmd/store.t    21    Test STORE command
  494. Xagent/test/cmd/strip.t    21    Test STRIP command
  495. Xagent/test/cmd/subst.t    25    Test SUBST command
  496. Xagent/test/cmd/tr.t       25    Test TR command
  497. Xagent/test/cmd/unique.t   21    Test UNIQUE command
  498. Xagent/test/cmd/unknown.t  24    Make sure unknown command defaults correctly
  499. Xagent/test/cmd/vacation.t 22    Test VACATION command
  500. Xagent/test/cmd/write.t    21    Test WRITE command
  501. Xagent/test/filter/         1    Testing the filtering capabilities
  502. Xagent/test/filter/backref.t 23    Check backreferences
  503. Xagent/test/filter/case.t  24    Normalized header case tests
  504. Xagent/test/filter/default.t 23    Check default behaviour when mail not saved
  505. Xagent/test/filter/escape.t 25    Escape sequences within actions
  506. Xagent/test/filter/group.t 24    Selector combination tests
  507. Xagent/test/filter/hook.t  20    Ensure hooks are correctly invoked
  508. Xagent/test/filter/list.t   5    Check matching on lists like To and Newsgroups
  509. Xagent/test/filter/loop.t  24    Check loop detection
  510. Xagent/test/filter/mode.t  24    Make sure mode selection logic works
  511. Xagent/test/filter/multiple.t 23    Check multiple selectors
  512. Xagent/test/filter/not.t   23    Negated pattern tests
  513. Xagent/test/filter/pattern.t 24    Check patterns specification and loading
  514. Xagent/test/filter/range.t 22    Selector range tests
  515. Xagent/test/filter/status.t 24    Action status updating tests
  516. Xagent/test/level          19    Default logging level for tests
  517. Xagent/test/mail           22    The mail used by testing routines
  518. Xagent/test/misc/           1    Directory for miscellaneous tests
  519. Xagent/test/misc/compress.t  6    Folder compression checks
  520. Xagent/test/misc/mh.t      21    MH-style folder checks
  521. Xagent/test/misc/mmdf.t    23    MMDF-style mailbox checks
  522. Xagent/test/misc/newcmd.t  21    Filter command extension tests
  523. Xagent/test/misc/usrmac.t  20    User-defined macros checks
  524. Xagent/test/option/         1    Tests the options to the mailagent program
  525. Xagent/test/option/L.t     25    Test -L option
  526. Xagent/test/option/V.t     25    Test -V option
  527. Xagent/test/option/c.t     24    Test -c option
  528. Xagent/test/option/d.t     23    Test -d option
  529. Xagent/test/option/e.t     22    Test -e option
  530. Xagent/test/option/f.t     22    Test -f option
  531. Xagent/test/option/h.t     25    Test -h option
  532. Xagent/test/option/i.t     24    Test -i option
  533. Xagent/test/option/l.t     22    Test -l option
  534. Xagent/test/option/o.t     25    Test -o option
  535. Xagent/test/option/q.t     23    Test -q option
  536. Xagent/test/option/r.t     25    Test -r option
  537. Xagent/test/option/s.t     21    Test -s option
  538. Xagent/test/option/t.t     23    Test -t option
  539. Xagent/test/option/what.t  25    Ensure good behaviour with unknown option
  540. Xagent/test/pl/             1    Perl libraries for the regression test suite
  541. Xagent/test/pl/cmd.pl      24    Initializes command paths
  542. Xagent/test/pl/filter.pl   24    Set up environment for filter tests
  543. Xagent/test/pl/init.pl     25    Variable initializations
  544. Xagent/test/pl/logfile.pl  23    Logging file checking
  545. Xagent/test/pl/mail.pl     23    Modifies mail components
  546. Xagent/test/pl/misc.pl     25    Set up for miscellaneous tests
  547. Xagent/test/pl/mta.pl      25    Trivial MTA and NTA for tests
  548. Xagent/test/rules          19    Rules used by filtering tests
  549. Xbin/                       1    Directory for uninstalled binaries
  550. Xbin/perload               10    The dataloading/autoloading perl translator
  551. Xconfig_h.SH               13    Produces config.h
  552. Xconfmagic.h               25    Magic symbol remapping
  553. Xinstall.SH                19    Installation script
  554. Xmisc/                      1    Miscellaneous server commands
  555. Xmisc/README               15    Introduction to the misc directory
  556. Xmisc/shell/                1    Command to run arbitrary shell commands
  557. Xmisc/shell/README         25    Warning, should be read carefully
  558. Xmisc/shell/server.cf      16    Configuration of this server command
  559. Xmisc/shell/shell          23    The shell command itself
  560. Xmisc/unkit/                1    Command to automatically unkit messages
  561. Xmisc/unkit/README         25    Some notes about the UNKIT command
  562. Xmisc/unkit/kitok.msg      10    An example of message to be sent when kit received
  563. Xmisc/unkit/mailagent.cf   25    Template for inclusion into your ~/.mailagent
  564. Xmisc/unkit/newcmd.cf      26    Configuration of the new command
  565. Xmisc/unkit/rules          21    Rules to be added to handle kit messages
  566. Xmisc/unkit/unkit.pl       14    Implementation of the user-defined UNKIT command
  567. Xpatchlevel.h               8    Current version number and patch level
  568. END_OF_FILE
  569.   if test 16462 -ne `wc -c <'PACKLIST'`; then
  570.     echo shar: \"'PACKLIST'\" unpacked with wrong size!
  571.   fi
  572.   # end of 'PACKLIST'
  573. fi
  574. if test ! -d 'agent' ; then
  575.     echo shar: Creating directory \"'agent'\"
  576.     mkdir 'agent'
  577. fi
  578. if test ! -d 'agent/examples' ; then
  579.     echo shar: Creating directory \"'agent/examples'\"
  580.     mkdir 'agent/examples'
  581. fi
  582. if test ! -d 'agent/files' ; then
  583.     echo shar: Creating directory \"'agent/files'\"
  584.     mkdir 'agent/files'
  585. fi
  586. if test ! -d 'agent/files/help' ; then
  587.     echo shar: Creating directory \"'agent/files/help'\"
  588.     mkdir 'agent/files/help'
  589. fi
  590. if test -f 'agent/files/server' -a "${1}" != "-c" ; then 
  591.   echo shar: Will not clobber existing file \"'agent/files/server'\"
  592. else
  593.   echo shar: Extracting \"'agent/files/server'\" \(263 characters\)
  594.   sed "s/^X//" >'agent/files/server' <<'END_OF_FILE'
  595. X# Commands allowed by the server (file formatted with tabstops=4)
  596. X
  597. X# command name
  598. X#             type (shell/perl/end/help)
  599. X#                    concealed arguments
  600. X#                        collect data
  601. X#                            file name
  602. X#                                [function name (perl) / options (shell)]
  603. X
  604. Xend            end        -    -    -
  605. Xhelp        help    -    -    -
  606. END_OF_FILE
  607.   if test 263 -ne `wc -c <'agent/files/server'`; then
  608.     echo shar: \"'agent/files/server'\" unpacked with wrong size!
  609.   fi
  610.   # end of 'agent/files/server'
  611. fi
  612. if test ! -d 'agent/filter' ; then
  613.     echo shar: Creating directory \"'agent/filter'\"
  614.     mkdir 'agent/filter'
  615. fi
  616. if test ! -d 'agent/man' ; then
  617.     echo shar: Creating directory \"'agent/man'\"
  618.     mkdir 'agent/man'
  619. fi
  620. if test ! -d 'agent/pl' ; then
  621.     echo shar: Creating directory \"'agent/pl'\"
  622.     mkdir 'agent/pl'
  623. fi
  624. if test -f 'agent/pl/stats.pl' -a "${1}" != "-c" ; then 
  625.   echo shar: Will not clobber existing file \"'agent/pl/stats.pl'\"
  626. else
  627.   echo shar: Extracting \"'agent/pl/stats.pl'\" \(23279 characters\)
  628.   sed "s/^X//" >'agent/pl/stats.pl' <<'END_OF_FILE'
  629. X;# $Id: stats.pl,v 3.0 1993/11/29 13:49:17 ram Exp ram $
  630. X;#
  631. X;#  Copyright (c) 1990-1993, Raphael Manfredi
  632. X;#  
  633. X;#  You may redistribute only under the terms of the Artistic License,
  634. X;#  as specified in the README file that comes with the distribution.
  635. X;#  You may reuse parts of this distribution only within the terms of
  636. X;#  that same Artistic License; a copy of which may be found at the root
  637. X;#  of the source tree for mailagent 3.0.
  638. X;#
  639. X;# $Log: stats.pl,v $
  640. X;# Revision 3.0  1993/11/29  13:49:17  ram
  641. X;# Baseline for mailagent 3.0 netwide release.
  642. X;#
  643. X;# 
  644. X;# Handle the mailagent statistics file. This file is known as the statfile
  645. X;# in the configuration file (typically mailagent.st in the spool directory).
  646. X;# This file contains a summary of the action taken by the mailagent. The very
  647. X;# first line contains: mailstat: <timestamp> which is the date the statistics
  648. X;# started.
  649. X;#
  650. X;# The following format is used for each records:
  651. X;#
  652. X;#    <timestamp> 0 0
  653. X;#    <# of mails processed> <# commands run> <# of failures> <# of bytes>
  654. X;#    <rule number> <mode> <number of matches>
  655. X;#    "default" <number of matches>
  656. X;#    "vacation" <number of vaction messages sent>
  657. X;#    "seen" <number of messages already seen>
  658. X;#    "saved" <number of messages saved by default>
  659. X;#    <command name> <mode> <number of execution>
  660. X;#    !<command name> <mode> <number of failures>
  661. X;#    @<command name> <mode> <tag> <number of execution>
  662. X;#    %@<command name> <mode> <tag> <number of non-executed commands>
  663. X;#    --------
  664. X;#    <output of rule dumping>
  665. X;#    ++++++++
  666. X;#
  667. X;# The leading timestamp records the stamp on the rule file, followed by two
  668. X;# zeros (currently unused locations, reserved for future use, as they say).
  669. X;#
  670. X;# The number of mails processed is only stored to check the consistency of the
  671. X;# statistics file. Likewise, the number of commands run and the number of
  672. X;# failed commands are used to check the logging accuracy.
  673. X;#
  674. X;# Lines starting with a number indicate a match for a particular rule, in
  675. X;# a given mode. The "default", "vacation" and "seen" lines record the activity
  676. X;# of the default action, the vacation mode or the messages already processed
  677. X;# which come back.
  678. X;#
  679. X;# Commands are also logged. They are always spelled upper-cased. If the line
  680. X;# starts with a '!', it indicates a failure. If the character '@' is found
  681. X;# before the command name, it indicates a ONCE command. The tag part of the
  682. X;# identification is logged, but not the name (which is likely to be an e-mail
  683. X;# address anyway, whereas the tag identifies the command itself). The lines
  684. X;# starting with '%' also give the number of ONCE commands which were not
  685. X;# executed because the retry time was not reached.
  686. X;#
  687. X;# Below the dashed line, all the rules are dumped in order, and are separated
  688. X;# by a blank line. These are the rules listed in the rule file and they are
  689. X;# given for information purposes only, when reporting statistics. It ends with
  690. X;# a plus line.
  691. X;#
  692. X;# Whenever the rule file is updated, another record is started after having
  693. X;# been diffing the rules we have parsed with the rules dumped in the statistics
  694. X;# file.
  695. X;#
  696. X;# In order to improve performances, the statistics file is cached in memory.
  697. X;# Only the last record is read, up to the dashed-line. The data structures
  698. X;# used are:
  699. X;#
  700. X;#     @stats'Top: the top seven fields of the record:
  701. X;#         (time, 0, 0, processed, run, failed, bytes)
  702. X;#     %stats'Rule: indexed by <N>+mode, the number of matches
  703. X;#     %stats'Special: indexed by "default", "vacation", "saved" or "seen"
  704. X;#     %stats'Command: indexed by name+mode, the total number of runs
  705. X;#         this accounts for ONCE commands as well.
  706. X;#     %stats'FCommand: indexed by name+mode, the number of failures
  707. X;#         this accounts for ONCE commands as well.
  708. X;#     %stats'Once: indexed by name+mode+tag, the number of succesful runs
  709. X;#     %stats'ROnce: indexed by name+mode+tag, number of non-executed comands
  710. X;#
  711. Xpackage stats;
  712. X
  713. X$stats_wanted = 0;                # No statistics wanted by default
  714. X$new_record = 0;                # True when a new record is to be started
  715. X$start_date = 0;                # When statistics started
  716. X$suppressed = 0;                # Statistics suppressed by higher authority
  717. X
  718. X# Suppress statistics. This function is called when options like -r or -e are
  719. X# used. Those usually specify one time rules and thus are not entitled to be
  720. X# recorded into the statistics.
  721. Xsub main'no_stats { $suppressed = 1; }
  722. X
  723. X# Read the statistics file and fill in the hash tables
  724. Xsub main'read_stats {
  725. X    local($statfile) = $cf'statfile;    # Extract value from config package
  726. X    local($loglvl) = $main'loglvl;
  727. X    local($_, $.);
  728. X    $stats_wanted = 1 if ($statfile ne '' && -f $statfile);
  729. X    $stats_wanted = 0 if $suppressed;
  730. X    return unless $stats_wanted;
  731. X    # Do not come here unless statistics are really wanted
  732. X    unless (open(STATS, "$statfile")) {
  733. X        &'add_log("ERROR could not open statistics file $statfile: $!")
  734. X            if $loglvl > 0;
  735. X        $stats_wanted = 0;        # Cannot keep track of statistics
  736. X        return;
  737. X    }
  738. X    local($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime, $mtime,
  739. X        $ctime,$blksize,$blocks) = stat($cf'rules);
  740. X    # A null size means we have to start over again
  741. X    unless (-s $statfile) {
  742. X        &'add_log("starting new statistics") if $loglvl > 6;
  743. X        $start_date = time;
  744. X        close STATS;
  745. X        @Top = ($mtime, 0, 0, 0, 0, 0, 0);
  746. X        return;
  747. X    }
  748. X    $_ = <STATS>;
  749. X    unless (/^mailstat: (\d+)/) {
  750. X        &'add_log("ERROR corrupted statistics file $statfile") if $loglvl;
  751. X        close STATS;
  752. X        $stats_wanted = 0;
  753. X        return;
  754. X    } else {
  755. X        $start_date = $1;
  756. X    }
  757. X    # The first record is always the active one. Check the timestamp. If the
  758. X    # rule file has changed, check the sums.
  759. X    $_ = <STATS>;
  760. X    local($timestamp, $unused_1, $unused_2) = split(' ', $_);
  761. X    if ($main'edited_rules || $mtime > $timestamp) {    # File was modified?
  762. X        # Reset timestamp for next time if rule come from a file.
  763. X        $timestamp = $mtime;
  764. X        $timestamp = 0 if $main'edited_rules;
  765. X        &'add_log("rule file may have changed") if $loglvl > 18;
  766. X        $new_record = &diff_rules($statfile);        # Run the full diff then
  767. X        if ($new_record) {
  768. X            &'add_log("rule file has changed") if $loglvl > 6;
  769. X            @Top = ($mtime, 0, 0, 0, 0, 0, 0);
  770. X            close STATS;
  771. X            $start_date = time;
  772. X            return;
  773. X        }
  774. X        &'add_log("rule file has not changed") if $loglvl > 6;
  775. X    }
  776. X    # Read second line and build the @Top array
  777. X    $_ = <STATS>;
  778. X    local($processed, $run, $failed, $bytes) = split(' ', $_);
  779. X    @Top =
  780. X        ($timestamp, $unused_1, $unused_2, $processed, $run, $failed, $bytes);
  781. X    local($valid) = 0;            # Set to true when a valid record was found
  782. X    &fill_stats;                # Fill in data structures
  783. X    close STATS;
  784. X    &'add_log('statistics initialized and loaded') if $loglvl > 18;
  785. X}
  786. X
  787. X# Write the statistics file
  788. Xsub main'write_stats {
  789. X    local($statfile) = $cf'statfile;    # Extract value from config package
  790. X    local($loglvl) = $main'loglvl;
  791. X    return unless $stats_wanted;
  792. X    local($oldstat) = -f $statfile;
  793. X    if ($oldstat) {
  794. X        unlink("$statfile.b") if -f "$statfile.b";
  795. X        unless (rename($statfile, "$statfile.b")) {
  796. X            &'add_log("ERROR cannot rename $statfile as $statfile.b: $!")
  797. X                if $loglvl;
  798. X            return;
  799. X        }
  800. X    }
  801. X    unless (open(STATS, ">$statfile")) {
  802. X        &'add_log("ERROR cannot create $statfile: $!") if $loglvl;
  803. X        return;
  804. X    }
  805. X    # If a new record is to be created, do it at the top of the file, then
  806. X    # append the old statistics file at the end of it. Otherwise, the first
  807. X    # record of the old statistics file is removed and the remaining is
  808. X    # appended.
  809. X    print STATS "mailstat: $start_date\n";        # Magic line
  810. X    print STATS join(' ', @Top[0..2]), "\n";
  811. X    print STATS join(' ', @Top[3..$#Top]), "\n";
  812. X    &print_array(*Rule, "");            # Print rule matches statistics
  813. X    &print_array(*Special, "");            # Print special stats
  814. X    &print_array(*Command, "");            # Print actions executions
  815. X    &print_array(*FCommand, "!");        # Print failed actions
  816. X    &print_array(*Once, "@");            # Print once commands done
  817. X    &print_array(*ROncem, "%@");        # Print once commands not retried
  818. X    print STATS "------\n";
  819. X    &rules'write_fd("stats'STATS");        # Append internal form of rules
  820. X    # If there was no previous statistics file, it's done!
  821. X    unless ($oldstat) {
  822. X        close STATS;
  823. X        return;
  824. X    }
  825. X    unless (open(OLD, "$statfile.b")) {
  826. X        &'add_log("ERROR cannot open old statistics file") if $loglvl;
  827. X        close STATS;
  828. X        return;
  829. X    }
  830. X    # If no new record was created, we have to skip the first record of the old
  831. X    # statistics file before appending.
  832. X    unless ($new_record) {
  833. X        while (<OLD>) {
  834. X            last if /^\+\+\+\+\+\+/;
  835. X        }
  836. X    }
  837. X    # It's fine to only check the return status of print right now. If there is
  838. X    # not enough space on the device, we won't be able to append the whole
  839. X    # backup file, but then we have to discard previously saved statistics
  840. X    # anyway...
  841. X    # Note: 'print STATS <OLD>' would cause an excessive memory consumption
  842. X    # given that a statistics file can be several hundred Kbytes long.
  843. X    local($status) = 1;                    # Printing status
  844. X    while (<OLD>) {
  845. X        $status &= (print STATS);        # Status remains to 1 while successful
  846. X    }
  847. X    close OLD;
  848. X    close STATS;
  849. X    if ($status) {                        # Print ran ok
  850. X        unlink("$statfile.b");
  851. X    } else {                            # Print failed
  852. X        &'add_log("ERROR could not update statistics: $!") if $loglvl;
  853. X        unless (rename("$statfile.b", $statfile)) {
  854. X            &'add_log("ERROR could not restore old statistics file: $!")
  855. X                if $loglvl;
  856. X        }
  857. X    }
  858. X}
  859. X
  860. X# Print the hash table array in STATS file
  861. Xsub print_array {
  862. X    local(*name, $leader) = @_;
  863. X    local(@keys);
  864. X    foreach (sort keys %name) {
  865. X        @keys = split(/:/);
  866. X        print STATS $leader . join(' ', @keys) . ' ' . $name{$_}, "\n";
  867. X    }
  868. X}
  869. X
  870. X#
  871. X# Accounting routines
  872. X#
  873. X
  874. X# Record a mail processing
  875. Xsub main's_filtered {
  876. X    return unless $stats_wanted;
  877. X    local($length) = @_;
  878. X    $Top[3]++;
  879. X    $Top[6] += $length;
  880. X}
  881. X
  882. X# Record a rule match
  883. Xsub main's_match {
  884. X    return unless $stats_wanted;
  885. X    local($number, $mode) = @_;
  886. X    $Rule{"$number:$mode"}++;
  887. X}
  888. X
  889. X# Record a default rule
  890. Xsub main's_default {
  891. X    return unless $stats_wanted;
  892. X    $Special{'default'}++;
  893. X}
  894. X
  895. X# Record a vacation message sent in vacation mode
  896. Xsub main's_vacation {
  897. X    return unless $stats_wanted;
  898. X    $Special{'vacation'}++;
  899. X}
  900. X
  901. X# Record a message saved by the default action
  902. Xsub main's_saved {
  903. X    return unless $stats_wanted;
  904. X    $Special{'saved'}++;
  905. X}
  906. X
  907. X# Record an already processed message
  908. Xsub main's_seen {
  909. X    return unless $stats_wanted;
  910. X    $Special{'seen'}++;
  911. X}
  912. X
  913. X# Record a successful execution
  914. Xsub main's_action {
  915. X    return unless $stats_wanted;
  916. X    local($name, $mode) = @_;
  917. X    $Command{"$name:$mode"}++;
  918. X    $Top[4]++;
  919. X}
  920. X
  921. X# Record a failed execution
  922. Xsub main's_failed {
  923. X    return unless $stats_wanted;
  924. X    local($name, $mode) = @_;
  925. X    $Command{"$name:$mode"}++;
  926. X    $FCommand{"$name:$mode"}++;
  927. X    $Top[4]++;
  928. X    $Top[5]++;
  929. X}
  930. X
  931. X# Record a successful once
  932. Xsub main's_once {
  933. X    return unless $stats_wanted;
  934. X    local($name, $mode, $tag) = @_;
  935. X    $Once{"$name:$mode:$tag"}++;
  936. X}
  937. X
  938. X# Record a non-retried once
  939. Xsub main's_noretry {
  940. X    return unless $stats_wanted;
  941. X    local($name, $mode, $tag) = @_;
  942. X    $ROnce{"$name:$mode:$tag"}++;
  943. X}
  944. X
  945. X#
  946. X# Low-level routines
  947. X#
  948. X
  949. X# Establish a difference between the rules we have in memory and the rules
  950. X# that has been dumped at the end of the active record. Return the difference
  951. X# status, true or false.
  952. Xsub diff_rules {
  953. X    local($file) = @_;                    # Statistics file where dump is stored
  954. X    local(*loglvl) = *main'loglvl;
  955. X    local($_, $.);
  956. X    open(FILE, "$file") || return 1;    # Changed if we cannot re-open file
  957. X    # Go past the first dashed line, where the dumped rules begin
  958. X    while (<FILE>) {
  959. X        last if /^------/;
  960. X    }
  961. X    # The difference is done on the internal representation of the rules,
  962. X    # which gives us a uniform and easy way to make sure the rules did not
  963. X    # change.
  964. X    local(*Rules) = *main'Rules;        # The @Rules array
  965. X    local($i) = 0;                        # Index in the rules
  966. X    while (<FILE>) {
  967. X        last if /^\+\+\+\+\+\+/;        # End of dumped rules
  968. X        last if $i > $#Rules;
  969. X        chop;
  970. X        last unless $_ eq $Rules[$i];    # Compare rule with internal form
  971. X        $i++;                            # Index in the @Rules array
  972. X    }
  973. X    if ($i <= $#Rules) {                # If one rule did not match
  974. X        close FILE;
  975. X        ++$i;
  976. X        &'add_log("rule $i did not match") if $loglvl > 11;
  977. X        return 1;                        # Rule file has changed
  978. X    }
  979. X    # Now check the hash table entries
  980. X    local(*Rule) = *main'Rule;            # The %Rule array
  981. X    local(@keys) =
  982. X        sort rules'hashkey keys(%Rule);    # Sorted keys H0, H1, etc...
  983. X    $i = 0;                                # Reset index
  984. X    while (<FILE>) {                    # Swallow blank line
  985. X        last if /^\+\+\+\+\+\+/;        # End of dumped rules
  986. X        last if $i > $#keys;
  987. X        chop;
  988. X        last unless $_ eq $Rule{$keys[$i]};
  989. X        $i++;                            # Index in @keys
  990. X    }
  991. X    if ($i <= $#keys) {                    # Changed if one rule did not match
  992. X        close FILE;
  993. X        ++$i;
  994. X        &'add_log("hrule $i did not match") if $loglvl > 11;
  995. X        return 1;                        # Rule file has changed
  996. X    }
  997. X    close FILE;
  998. X    return 1 unless /^\+\+\+\+\+\+/;    # More rules to come
  999. X    0;                                    # Rule file did not change
  1000. X}
  1001. X
  1002. X# Read pre-opened STATS file descriptor and fill in the statistics arrays
  1003. Xsub fill_stats {
  1004. X    while (<STATS>) {
  1005. X        last if /^------/;        # Reached end of statistics
  1006. X        if (/^(\d+)\s+(\w+)\s+(\d+)/) {                # <rule> <mode> <# match>
  1007. X            $Rule{"$1:$2"} = int($3);
  1008. X        } elsif (/^([a-z]+)\s+(\d+)/) {                # <special> <# match>
  1009. X            $Special{$1} = $2;                        # first token is the key
  1010. X        } elsif (/^([A-Z]+)\s+(\w+)\s+(\d+)/) {        # <cmd> <mode> <# succes>
  1011. X            $Command{"$1:$2"} = int($3);
  1012. X        } elsif (/^!([A-Z]+)\s+(\w+)\s+(\d+)/) {    # <cmd> <mode> <# fail>
  1013. X            $FCommand{"$1:$2"} = int($3);
  1014. X        } elsif (/^@([A-Z]+)\s+(\w+)\s+(\S+)\s+(\d+)/) {    # Once run
  1015. X            $Once{"$1:$2:$3"} = int($4);
  1016. X        } elsif (/^%@([A-Z]+)\s+(\w+)\s+(\S+)\s+(\d+)/) {    # Once not retried
  1017. X            $ROnce{"$1:$2:$3"} = int($4);
  1018. X        } else {
  1019. X            &'add_log("ERROR corrupted line $. in statistics file") if $loglvl;
  1020. X            &'add_log("ERROR line $. was: $_") if $loglvl > 1;
  1021. X        }
  1022. X    }
  1023. X}
  1024. X
  1025. X#
  1026. X# Reporting statistics
  1027. X#
  1028. X
  1029. X# Dump the statistics on the standard output.
  1030. X# Here are the possible options:
  1031. X#   u: print only used rules
  1032. X#   m: merge all the statistics at the end
  1033. X#   a: all mode reported
  1034. X#   r: rule-based statistics, on a per-state basis
  1035. X#   y: USELESS if -m, but kept for nice mnemonic
  1036. Xsub main'report_stats {
  1037. X    require 'ctime.pl';
  1038. X    local($option) = @_;                # Options from command line
  1039. X    local($opt_u) = $option =~ /u/;        # Only used rules
  1040. X    local($opt_m) = $option =~ /m/;        # Merge all statistics at the end
  1041. X    local($opt_a) = $option =~ /a/;        # Print mode-related statistics
  1042. X    local($opt_r) = $option =~ /r/;        # Print rule-based statistics
  1043. X    local($opt_y) = $option =~ /y/;        # Yield rule-based summary
  1044. X    local($statfile) = $cf'statfile;
  1045. X    local(*loglvl) = *main'loglvl;
  1046. X    local($_, $.);
  1047. X    select(STDOUT);
  1048. X    unless ($statfile ne '' && -f "$statfile") {
  1049. X        print "No statistics available.\n";
  1050. X        return;
  1051. X    }
  1052. X    unless (open(STATS, "$statfile")) {
  1053. X        print "Can't open $statfile: $!\n";
  1054. X        return;
  1055. X    }
  1056. X    unless (-s $statfile) {
  1057. X        print "Statistics file is empty.\n";
  1058. X        close STATS;
  1059. X        return;
  1060. X    }
  1061. X    local($lasttime) = time;    # End of last dumped period
  1062. X    local($start) = $lasttime;    # Save current time
  1063. X    local($amount);                # Number of mails processed
  1064. X    local($bytes);                # Bytes processed
  1065. X    local($actions);            # Number of actions
  1066. X    local($failures);            # Failures reported
  1067. X    local(%Cmds);                # Execution / action
  1068. X    local(%FCmds);                # Failures / action
  1069. X    local(%Spec);                # Summary of special actions
  1070. X    local(%Mrule);                # For merged rules statistics
  1071. X    local($in_summary);            # True when in summary
  1072. X    1 while &print_stats;        # Print statistics for each record
  1073. X    close STATS;
  1074. X    if ($opt_m) {
  1075. X        $in_summary = 1;                # Signal in summary part
  1076. X        $Top[3] = $amount;                # Number of mails processed
  1077. X        $Top[4] = $actions;                # Number of mails processed
  1078. X        $Top[5] = $failures;            # Failures reported
  1079. X        $Top[6] = $bytes;                # Bytes processed
  1080. X        $current_time = $lasttime;
  1081. X        $lasttime = $start;
  1082. X        local(*Special) = *Spec;        # Alias %Spec into %Special
  1083. X        &print_general("Summary");
  1084. X        local(*Command) = *Cmds;        # Alias %Cmds into %Command
  1085. X        local(*FCommand) = *FCmds;        # Alias %FCmds into %FCommand
  1086. X        &print_commands;                # Commands summary
  1087. X        &print_rules_summary;            # Print rules summary
  1088. X    }
  1089. X}
  1090. X
  1091. X# Print statistics for one record. This subroutine exectues in the context
  1092. X# built by report_stats. I heavily used dynamic scope hereafter to avoid code
  1093. X# duplication.
  1094. Xsub print_stats {
  1095. X    return 0 if eof(STATS);
  1096. X    $_ = <STATS>;
  1097. X    unless (/^mailstat: (\d+)/) {
  1098. X        print "Statistics file is corrupted, line $.\n";
  1099. X        return 0;
  1100. X    }
  1101. X    local($current_time) = $1;
  1102. X    # Build a valid context for data structures fill-in
  1103. X    local(@Top, %Rule, %Special, %Command, %FCommand, %Once, %ROnce);
  1104. X    # The two first line are the @Top array
  1105. X    $_ = <STATS>;
  1106. X    $_ .= <STATS>;
  1107. X    chop;
  1108. X    @Top = split(/\s+/);
  1109. X    &fill_stats;                        # Fill in local data structures
  1110. X    &print_summary;                        # Print local summary
  1111. X    # Now build a valid context for rule dumping
  1112. X    local(@main'Rules, %main'Rule);
  1113. X    local($i) = 0;                        # Force numeric context
  1114. X    local($hash);                        # True when entering %Rule section
  1115. X    while (<STATS>) {
  1116. X        last if /^\+\+\+\+\+\+/;
  1117. X        chop;
  1118. X        if (/^$/) {
  1119. X            $hash = 1;                    # Separator between @Rules and %Rule
  1120. X            next;
  1121. X        }
  1122. X        unless ($hash) {
  1123. X            push(@main'Rules, $_);
  1124. X        } else {
  1125. X            $main'Rule{"H$i"} = $_;
  1126. X            $i++;
  1127. X        }
  1128. X    }
  1129. X    &main'dump_rules(*print_header, *rule_stats);
  1130. X    print '=' x 79, "\n";
  1131. X    $lasttime = $current_time;
  1132. X}
  1133. X
  1134. X# Print a summary from a given record
  1135. Xsub print_summary {
  1136. X    &print_general("Statistics");
  1137. X    &print_commands;                        # Commands summary
  1138. X    $amount += $Top[3];                        # Number of mails processed
  1139. X    $bytes += $Top[6];                        # Bytes processed
  1140. X    $actions += $Top[4];                    # Actions exectuted
  1141. X    $failures += $Top[5];                    # Failures reported
  1142. X    foreach (keys %Special) {                # Special statistics
  1143. X        $Spec{$_} += $Special{$_};
  1144. X    }
  1145. X    foreach (keys %Command) {                # Commands ececuted
  1146. X        $Cmds{$_} += $Command{$_};
  1147. X    }
  1148. X    foreach (keys %FCommand) {                # Failed commands
  1149. X        $FCmds{$_} += $FCommand{$_};
  1150. X    }
  1151. X}
  1152. X
  1153. X# Print general informations, as found in @Top.
  1154. Xsub print_general {
  1155. X    local($what) = @_;
  1156. X    local($last) = &'ctime($lasttime);
  1157. X    local($now) = &'ctime($current_time);
  1158. X    local($n, $s);
  1159. X    chop $now;
  1160. X    chop $last;
  1161. X    # Header of statistics
  1162. X    print "$what from $now to $last:\n";
  1163. X    print '~' x 79, "\n";
  1164. X    print "Processed $Top[3] mail";
  1165. X    print "s" unless $Top[3] == 1;
  1166. X    print " for a total of $Top[6] bytes";
  1167. X    $n = $Special{'seen'};
  1168. X    $s = $n == 1 ? '' : 's';
  1169. X    print " ($n mail$s already seen)" if $n;
  1170. X    print ".\n";
  1171. X    print "Executed $Top[4] action";
  1172. X    print "s" unless $Top[4] == 1;
  1173. X    local($failed) = $Top[5];
  1174. X    unless ($failed) {
  1175. X        print " with no failure.\n";
  1176. X    } else {
  1177. X        print ", $failed of which failed.\n";
  1178. X    }
  1179. X    $n = $Special{'default'};
  1180. X    $s = $n == 1 ? '' : 's';
  1181. X    print "The default rule was applied $n time$s";
  1182. X    $n = $Special{'saved'};
  1183. X    $s = $n == 1 ? '' : 's';
  1184. X    local($was) = $n == 1 ? 'was' : 'were';
  1185. X    print " and $n message$s $was implicitely saved" if $n;
  1186. X    print ".\n";
  1187. X    $n = $Special{'vacation'};
  1188. X    $s = $n == 1 ? '' : 's';
  1189. X    print "Received $n message$s in vacation mode with no rule match.\n" if $n;
  1190. X}
  1191. X
  1192. X# Print the commands executed, as found in %Command and @Top.
  1193. Xsub print_commands {
  1194. X    print '~' x 79, "\n";
  1195. X    local($cmd, $mode);
  1196. X    local(%states, %fstates);
  1197. X    local(%cmds, %fcmds);
  1198. X    local(@kstates, @fkstates);
  1199. X    local($n, $s);
  1200. X    foreach (keys %Command) {
  1201. X        ($cmd, $mode) = /^(\w+):(\w+)/;
  1202. X        $n = $Command{$_};
  1203. X        $cmds{$cmd} += $n;
  1204. X        $states{"$cmd:$mode"} += $n;
  1205. X    }
  1206. X    foreach (keys %FCommand) {
  1207. X        ($cmd, $mode) = /^(\w+):(\w+)/;
  1208. X        $n = $FCommand{$_};
  1209. X        $fcmds{$cmd} += $n;
  1210. X        $fstates{"$cmd:$mode"} += $n;
  1211. X    }
  1212. X    local($total) = $Top[4];
  1213. X    local($percentage);
  1214. X    local($cmd_total);
  1215. X    foreach $key (sort keys %cmds) {
  1216. X        @kstates = sort grep(/^$key:/, keys %states);
  1217. X        $cmd_total = $n = $cmds{$key};
  1218. X        $s = $n == 1 ? '' : 's';
  1219. X        $percentage = '0.00';
  1220. X        $percentage = sprintf("%.2f", ($n / $total) * 100) if $total;
  1221. X        print "$key run $n time$s ($percentage %)";
  1222. X        if (@kstates == 1) {
  1223. X            ($mode) = $kstates[0] =~ /^\w+:(\w+)/;
  1224. X            print " in state $mode";
  1225. X        } else {
  1226. X            $n = @kstates;
  1227. X            print " in $n states";
  1228. X        }
  1229. X        if (defined($fcmds{$key}) && ($n = $fcmds{$key})) {
  1230. X            $s = $n == 1 ? '' : 's';
  1231. X            $percentage = sprintf("%.2f", ($n / $cmd_total) * 100);
  1232. X            print " and failed $n time$s ($percentage %)";
  1233. X        }
  1234. X        if (@kstates == 1 || !$opt_a) {
  1235. X            print ".\n";
  1236. X        } else {
  1237. X            print ":\n";
  1238. X            @fkstates = sort grep(/^$key:/, keys %states);
  1239. X            foreach (@kstates) {
  1240. X                ($mode) = /^\w+:(\w+)/;
  1241. X                $n = $states{$_};
  1242. X                $s = $n == 1 ? '' : 's';
  1243. X                $percentage = sprintf("%.2f", ($n / $cmd_total) * 100);
  1244. X                print "    state $mode: $n time$s ($percentage %)";
  1245. X                $n = $fstates{$_};
  1246. X                $s = $n == 1 ? '' : 's';
  1247. X                print ", $n failure$s" if $n;
  1248. X                print ".\n";
  1249. X            }
  1250. X        }
  1251. X    }
  1252. X}
  1253. X
  1254. X# Return a uniform representation of a rule (suitable for usage merging)
  1255. Xsub uniform_rule {
  1256. X    local($rulenum) = @_;
  1257. X    local($text) = $main'Rules[$rulenum - 1];
  1258. X    $text =~ s/^(.*}\s+)//;                    # Get mode and action
  1259. X    local($rule) = $1;
  1260. X    local(@keys) = split(' ', $text);        # H keys for selection / patterns
  1261. X    foreach (@keys) {
  1262. X        $rule .= "\n" . $main'Rule{$_};        # Add selectors and patterns
  1263. X    }
  1264. X    $rule;
  1265. X}
  1266. X
  1267. X# Print a summary of merged rules as found in %Mrule
  1268. Xsub print_rules_summary {
  1269. X    return unless $opt_y;
  1270. X    local(@main'Rules);                # The main rules array
  1271. X    local(%main'Rule);                # The H table for selectors and patterns
  1272. X    local($counter) = 0;            # Counter for H key computation
  1273. X    local($rulenum) = 0;            # Rule number
  1274. X    local(%Rule);                    # The local rule statistics array
  1275. X    local(@components);                # Rule components
  1276. X    local($rule);                    # Constructed rule
  1277. X    foreach (keys %Mrule) {
  1278. X        s/^(\w+)://;                # Get applied state
  1279. X        $state = $1;
  1280. X        @components = split(/\n/);
  1281. X        $rule = shift(@components);
  1282. X        foreach (@components) {
  1283. X            $rule .= " H$counter";
  1284. X            $main'Rule{"H$counter"} = $_;
  1285. X            $counter++;
  1286. X        }
  1287. X        push(@main'Rules, $rule);
  1288. X        $rulenum++;
  1289. X        $Rule{"$rulenum:$state"} += $Mrule{"$state:$_"};
  1290. X    }
  1291. X    &main'dump_rules(*print_header, *rule_stats);
  1292. X}
  1293. X
  1294. X#
  1295. X# Hooks for rule dumping
  1296. X#
  1297. X
  1298. X# Print the rule number and the number of applications
  1299. Xsub print_header {
  1300. X    local($rulenum) = @_;
  1301. X    local($total_matches) = 0;
  1302. X    local(@keys) = grep(/^$rulenum:/, keys %Rule);
  1303. X    local($state);
  1304. X    local($matches);
  1305. X    # Add up the usage of rules, whatever the matching state was
  1306. X    foreach (@keys) {
  1307. X        $matches = $Rule{$_};
  1308. X        $total_matches += $matches;
  1309. X        if ($opt_y && !$in_summary) {
  1310. X            ($state) = /^\d+:(.*)/;
  1311. X            $_ = $state . ":" . &uniform_rule($rulenum);
  1312. X            $Mrule{$_} += $matches;
  1313. X        }
  1314. X    }
  1315. X    return 0 if ($opt_u && $total_matches == 0);
  1316. X    return 0 unless $opt_r;
  1317. X    local($total) = $Top[3];
  1318. X    $total = 1 unless $total;
  1319. X    local($percentage) = sprintf("%.2f", ($total_matches / $total) * 100);
  1320. X    $percentage = '0' if $total_matches == 0;
  1321. X    local($s) = $total_matches == 1 ? '' : 's';
  1322. X    print '-' x 79, "\n";
  1323. X    print "Rule #$rulenum, applied $total_matches time$s ($percentage %).\n";
  1324. X}
  1325. X
  1326. X# Print the rule applications, on a per-state basis
  1327. Xsub rule_stats {
  1328. X    return unless $opt_r;
  1329. X    local($rulenum) = @_;
  1330. X    local($mode) = $main'Rules[$rulenum - 1] =~ /^(.*)\s+{/;
  1331. X    return unless $mode =~ /,/ || $mode eq 'ALL' || $mode =~ /!/;
  1332. X
  1333. X    # If there is only one mode <ALL>, more than one mode, or at least
  1334. X    # a negated mode, then we have a priori more than one possible mode
  1335. X    # that can lead to the execution of the rule. So dump them.
  1336. X
  1337. X    local(@keys) = grep(/^$rulenum:/, keys %Rule);
  1338. X    local(%states);
  1339. X    local($s, $total);
  1340. X    foreach (@keys) {
  1341. X        /^\d+:(.+)/;
  1342. X        $states{$1}++;
  1343. X    }
  1344. X    @keys = keys %states;
  1345. X    return unless $opt_a;
  1346. X    if (@keys == 1) {
  1347. X        print "Applied only in state $keys[0].\n";
  1348. X    } else {
  1349. X        foreach (@keys) {
  1350. X            $total = $states{$_};
  1351. X            $s = $total == 1 ? '' : 's';
  1352. X            print "State $_: $total time$s.\n";
  1353. X        }
  1354. X    }
  1355. X}
  1356. X
  1357. Xpackage main;
  1358. X
  1359. END_OF_FILE
  1360.   if test 23279 -ne `wc -c <'agent/pl/stats.pl'`; then
  1361.     echo shar: \"'agent/pl/stats.pl'\" unpacked with wrong size!
  1362.   fi
  1363.   # end of 'agent/pl/stats.pl'
  1364. fi
  1365. if test ! -d 'agent/test' ; then
  1366.     echo shar: Creating directory \"'agent/test'\"
  1367.     mkdir 'agent/test'
  1368. fi
  1369. if test ! -d 'agent/test/basic' ; then
  1370.     echo shar: Creating directory \"'agent/test/basic'\"
  1371.     mkdir 'agent/test/basic'
  1372. fi
  1373. if test ! -d 'agent/test/cmd' ; then
  1374.     echo shar: Creating directory \"'agent/test/cmd'\"
  1375.     mkdir 'agent/test/cmd'
  1376. fi
  1377. if test ! -d 'agent/test/filter' ; then
  1378.     echo shar: Creating directory \"'agent/test/filter'\"
  1379.     mkdir 'agent/test/filter'
  1380. fi
  1381. if test ! -d 'agent/test/misc' ; then
  1382.     echo shar: Creating directory \"'agent/test/misc'\"
  1383.     mkdir 'agent/test/misc'
  1384. fi
  1385. if test ! -d 'agent/test/option' ; then
  1386.     echo shar: Creating directory \"'agent/test/option'\"
  1387.     mkdir 'agent/test/option'
  1388. fi
  1389. if test ! -d 'agent/test/pl' ; then
  1390.     echo shar: Creating directory \"'agent/test/pl'\"
  1391.     mkdir 'agent/test/pl'
  1392. fi
  1393. if test ! -d 'bin' ; then
  1394.     echo shar: Creating directory \"'bin'\"
  1395.     mkdir 'bin'
  1396. fi
  1397. if test ! -d 'misc' ; then
  1398.     echo shar: Creating directory \"'misc'\"
  1399.     mkdir 'misc'
  1400. fi
  1401. if test ! -d 'misc/shell' ; then
  1402.     echo shar: Creating directory \"'misc/shell'\"
  1403.     mkdir 'misc/shell'
  1404. fi
  1405. if test ! -d 'misc/unkit' ; then
  1406.     echo shar: Creating directory \"'misc/unkit'\"
  1407.     mkdir 'misc/unkit'
  1408. fi
  1409. echo shar: End of archive 1 \(of 26\).
  1410. cp /dev/null ark1isdone
  1411. MISSING=""
  1412. 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
  1413.     if test ! -f ark${I}isdone ; then
  1414.     MISSING="${MISSING} ${I}"
  1415.     fi
  1416. done
  1417. if test "${MISSING}" = "" ; then
  1418.     echo You have unpacked all 26 archives.
  1419.     echo "Now run 'sh PACKNOTES', then read README and type Configure.'"
  1420.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1421. else
  1422.     echo You still must unpack the following archives:
  1423.     echo "        " ${MISSING}
  1424. fi
  1425. exit 0
  1426.  
  1427. exit 0 # Just in case...
  1428.