home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #27 / NN_1992_27.iso / spool / comp / sources / misc / 4109 < prev    next >
Encoding:
Text File  |  1992-11-20  |  54.5 KB  |  1,648 lines

  1. Newsgroups: comp.sources.misc
  2. Path: sparky!kent
  3. From: ram@eiffel.com (Raphael Manfredi)
  4. Subject:  v33i104:  mailagent - Rule Based Mail Filtering, Part12/17
  5. Message-ID: <1992Nov20.230705.26840@sparky.imd.sterling.com>
  6. Followup-To: comp.sources.d
  7. X-Md4-Signature: e5665db0878c39b02fda39df5ae49249
  8. Sender: kent@sparky.imd.sterling.com (Kent Landfield)
  9. Organization: Sterling Software
  10. References: <csm-v33i093=mailagent.230117@sparky.IMD.Sterling.COM>
  11. Date: Fri, 20 Nov 1992 23:07:05 GMT
  12. Approved: kent@sparky.imd.sterling.com
  13. Lines: 1633
  14.  
  15. Submitted-by: ram@eiffel.com (Raphael Manfredi)
  16. Posting-number: Volume 33, Issue 104
  17. Archive-name: mailagent/part12
  18. Environment: Perl, Sendmail, UNIX
  19.  
  20. #! /bin/sh
  21. # This is a shell archive.  Remove anything before this line, then feed it
  22. # into a shell via "sh file" or similar.  To overwrite existing files,
  23. # type "sh file -c".
  24. # Contents:  agent/files/agenthelp agent/filter/environ.c
  25. #   agent/filter/logfile.c agent/pl/eval.pl agent/pl/header.pl
  26. #   agent/pl/interface.pl agent/pl/parse.pl agent/test/cmd/split.t
  27. #   agent/test/option/c.t
  28. # Wrapped by kent@sparky on Wed Nov 18 22:42:28 1992
  29. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  30. echo If this archive is complete, you will see the following message:
  31. echo '          "shar: End of archive 12 (of 17)."'
  32. if test -f 'agent/files/agenthelp' -a "${1}" != "-c" ; then 
  33.   echo shar: Will not clobber existing file \"'agent/files/agenthelp'\"
  34. else
  35.   echo shar: Extracting \"'agent/files/agenthelp'\" \(6402 characters\)
  36.   sed "s/^X//" >'agent/files/agenthelp' <<'END_OF_FILE'
  37. XThe purpose of the mail agent is to enable me answer some of your
  38. Xrequests, even if it's early in the morning and I've just gone to bed ! :-)
  39. X
  40. XFor instance, you need Larry Wall's patch program or Rich Salz's cshar.
  41. XI have them and I use them in my own kits.  So you may ask me to send them
  42. Xto you.  Of course, you could send me a mail saying "Please, could you
  43. Xsend me the cshar distribution kit ?", but I certainly won't be able to do
  44. Xit at once , either because I am not there when the mail arrives, or
  45. Xbecause someone else asked before you...
  46. X
  47. XWith the mail agent, there are no problems. You simply (!) send me a mail
  48. Xof the following form:
  49. X
  50. X    Subject: Command
  51. X    @SH maildist =DEST= cshar 3.0
  52. X
  53. Xand you will get version 3.0 of cshar.
  54. X
  55. X
  56. XHere are the possible commands:
  57. X
  58. X    - mailhelp PATH
  59. X        # sends some help
  60. X
  61. X    - maillist PATH
  62. X        # sends a list of what is available
  63. X
  64. X    - mailpatch PATH SYSTEM VERSION LIST
  65. X        # sends patches for a system
  66. X
  67. X    - maildist PATH SYSTEM VERSION
  68. X        # sends a whole distribution kit (latest patchlevel)
  69. X
  70. Xwhere PATH is a return path FROM ME TO YOU either in Internet notation
  71. Xor in bang notation from some well-known host.  As far as you are
  72. Xconcerned, it appears to be =DEST=.
  73. X
  74. XPATH may be omitted for mailhelp and maillist, in which case the return
  75. Xaddress found in the mail header will be used.
  76. X
  77. XSYSTEM is the system's name and VERSION is the version number.  For
  78. Xsystems that are not maintained, the version number has no sense and
  79. Xthus may be omitted (for maildist). A '-' stands for the latest version.
  80. X
  81. XThe LIST for mailpatch is the number of one or more patches you need,
  82. Xseparated by spaces, commas, and/or hyphens. For instance:
  83. X
  84. X    2,3 4-7,10-
  85. X
  86. Xasks for patches 2 and 3, then 4 to 7, and from 10 to the end, while
  87. X
  88. X    -5 10 11
  89. X
  90. Xrequests patches up to 5, then 10 and 11.
  91. X
  92. X
  93. XCommands must be preceded by the token "@SH" at the beginning of a line.
  94. XDo not put spaces/tabs in front of "@SH".  In the mail examples I give,
  95. XI do put one, but it is only for clarity purpose.
  96. X
  97. XIn the same way, the line "Subject: Command" must be left-justified.
  98. XNote that the subject of the mail does not need to be "Command", as long
  99. Xas you put the "Subject: Command" line in the body of your message,
  100. Xbefore your commands. You may use either "Command" or "command".
  101. X
  102. XOnce the "Subject: Command" line appears in your mail, either in the
  103. Xheader or in the body, you may put as many commands as necessary.
  104. XFor example:
  105. X
  106. X    Subject: Command
  107. X
  108. X    @SH maillist =DEST=
  109. X    @SH maildist =DEST= cshar 3.0
  110. X
  111. X
  112. XIf you are in doubt of what is the return path, you may put "PATH" or a
  113. Xsingle '-' instead of your address, and the mail agent will replace it
  114. Xwith the return path it finds in the mail header.  In case you do not
  115. Xtrust your mail headers, you may force the return path with the "@PATH"
  116. Xcommand.  The mail agent reads the whole message before actually
  117. Xprocessing it, so the location of this command does not really matters.
  118. XHere is an example:
  119. X
  120. X    Subject: Command
  121. X
  122. X    @SH mailhelp
  123. X    @SH mailpatch - kit 2.0 4,5
  124. X    @PATH =DEST=
  125. X
  126. X
  127. XWhen you ask for files to be sent, the mail agent makes shell archives or
  128. Xkit archives, depending on the amount of bytes that are to be returned.
  129. XIf it exceeds an arbitrary-fixed limit of =MAXSIZE= bytes, files are sent
  130. Xas kit archives.  Otherwise, they will be sent as shell archives provided
  131. Xthat no file is greater than the maximum allowed for a single shell
  132. Xarchive.  This is called the "auto" packing mode.
  133. X
  134. XThe "@PACK" command forces the distribution mode, which is "auto" by
  135. Xdefault. The specified packing mode is used, until another "@PACK"
  136. Xcommand is found. Valid parameters are "auto", "kit" and "shar".
  137. XNote that forcing mode to "shar" may well result in a failure if one
  138. Xof the files to be sent is bigger than the maximum size allowed for a
  139. Xshell-archive (around 50000 bytes). However, the mail agent does its
  140. Xbest: it will split large files and uuencode non-ASCII ones.
  141. X
  142. XWhen you use maildist, please do not request for "shar" mode, as "kit" will
  143. Xbe more efficient and safer. Note that when the packing mode is "auto" and
  144. Xthe mailagent has to kit the files, a minikit is included. Hence you may
  145. Xunkit the distribution even if you do not have kit. But it will always be
  146. Xsimpler with kit anyway.
  147. X
  148. X"Kit" is a binary tar-mailer that you must own in order to unkit
  149. Xthe kit archives which do not include a 'minikit'. If you do not have it,
  150. Xsend me the following mail:
  151. X
  152. X    Subject: Command
  153. X    @SH maildist =DEST= kit -
  154. X
  155. Xand you will get the latest release of "kit".
  156. X
  157. XHere is another example that uses the "@PACK" request (the following
  158. Xpackage names, revision numbers and patchlevels are here for the purpose
  159. Xof demonstration only. Reality may -- and often will -- be completely
  160. Xdifferent):
  161. X
  162. X    Subject: Command
  163. X
  164. X    -- Set the return path, so that we can use '-' without fear.
  165. X    @PATH =DEST=
  166. X    -- Request patches for kit 2.0, sent in "auto" packing mode.
  167. X    -- Note that the '-' actually stands for the return path.
  168. X    -- We could also have said:
  169. X    --     @SH mailpatch =DEST= kit 2.0 3-
  170. X    -- but as long as we have more than one command in the file,
  171. X    -- it would be cumbersome to repeat the address each time.
  172. X    @SH mailpatch - kit 2.0 3-
  173. X    -- Force packing mode to "shar", as we don't want to kit 'kit'.
  174. X    -- We don't know what the latest version is, so we put a '-'.
  175. X    -- Maildist will send the version at its highest patchlevel.
  176. X    @PACK shar
  177. X    @SH maildist - kit -
  178. X    -- Kit is more reliable and will greatly reduce the amount of
  179. X    -- transmitted data (typical gain is 50% for sources).
  180. X    @PACK kit
  181. X    -- We want version 2.0 for dist and nothing else.
  182. X    @SH maildist - dist 2.0
  183. X    -- Request all patches for the latest version of matrix
  184. X    @SH mailpatch - matrix - 1-
  185. X
  186. X
  187. XA nice thing with the mail agent is that you can ask for a receipt, in
  188. Xorder to be sure that I received your mail.  You may do so by placing
  189. Xthe "@RR" command at the beginning of any line in the body of your
  190. Xmessage.  A receipt will then be sent to the return path extracted from
  191. Xthe header.  You may force the receipt to be sent to a given address by
  192. Xgiving it after the @RR token.  Saying "@RR PATH" or "@RR -" is possible
  193. Xbut not very different from a single "@RR" !!
  194. X
  195. XHere are valid requests:
  196. X
  197. X    @RR
  198. X    @RR =DEST=
  199. X    @RR login@cpu.domain.top
  200. X
  201. XNote that no "Subject: Command" line is necessary for that, so you may
  202. Xask for receipts in every mail.
  203. X
  204. X
  205. XIf this help file is not clear enough, or if you have suggestions/questions,
  206. Xfeel free to ask me.
  207. END_OF_FILE
  208.   if test 6402 -ne `wc -c <'agent/files/agenthelp'`; then
  209.     echo shar: \"'agent/files/agenthelp'\" unpacked with wrong size!
  210.   fi
  211.   # end of 'agent/files/agenthelp'
  212. fi
  213. if test -f 'agent/filter/environ.c' -a "${1}" != "-c" ; then 
  214.   echo shar: Will not clobber existing file \"'agent/filter/environ.c'\"
  215. else
  216.   echo shar: Extracting \"'agent/filter/environ.c'\" \(6195 characters\)
  217.   sed "s/^X//" >'agent/filter/environ.c' <<'END_OF_FILE'
  218. X/*
  219. X
  220. X ######  #    #  #    #     #    #####    ####   #    #           ####
  221. X #       ##   #  #    #     #    #    #  #    #  ##   #          #    #
  222. X #####   # #  #  #    #     #    #    #  #    #  # #  #          #
  223. X #       #  # #  #    #     #    #####   #    #  #  # #   ###    #
  224. X #       #   ##   #  #      #    #   #   #    #  #   ##   ###    #    #
  225. X ######  #    #    ##       #    #    #   ####   #    #   ###     ####
  226. X
  227. X    Environment setting.
  228. X*/
  229. X
  230. X/*
  231. X * $Id: environ.c,v 2.9 92/07/14 16:48:04 ram Exp $
  232. X *
  233. X *  Copyright (c) 1992, Raphael Manfredi
  234. X *
  235. X *  You may redistribute only under the terms of the GNU General Public
  236. X *  Licence as specified in the README file that comes with dist.
  237. X *
  238. X * $Log:    environ.c,v $
  239. X * Revision 2.9  92/07/14  16:48:04  ram
  240. X * 3.0 beta baseline.
  241. X * 
  242. X */
  243. X
  244. X#include "config.h"
  245. X#include "portable.h"
  246. X#include "hash.h"
  247. X#include <stdio.h>
  248. X
  249. X#ifdef I_STRING
  250. X#include <string.h>
  251. X#else
  252. X#include <strings.h>
  253. X#endif
  254. X
  255. X#define ENV_VARS    200                /* An average number of environment vars */
  256. X#define MAX_STRING    4096            /* Maximum size for an environment value */
  257. X
  258. X/* The environment is stored as an associative array: the key is the variable's
  259. X * name, and we store the value as the associated value, of course. This is
  260. X * not suitable for direct passing to a child, but it eases the environment
  261. X * modifications.
  262. X */
  263. Xprivate struct htable henv;            /* The associative array for env */
  264. X
  265. Xextern char *malloc();                /* Memory allocation */
  266. Xextern char *strsave();                /* String saving */
  267. X
  268. Xpublic void print_env(fd, envp)
  269. XFILE *fd;
  270. Xchar **envp;
  271. X{
  272. X    /* Print the environment held in 'envp' on file 'fd'. This is mainly
  273. X     * intended for debug purposes.
  274. X     */
  275. X
  276. X    while (*envp)
  277. X        fprintf(fd, "%s\n", *envp++);
  278. X}
  279. X
  280. Xpublic int init_env(envp)
  281. Xchar **envp;
  282. X{
  283. X    /* Initializes the associative array with the current environment. Returns
  284. X     * 0 if ok, -1 if failed due to a lack of memory.
  285. X     */
  286. X
  287. X    char env_line[MAX_STRING + 1];    /* The environment line */
  288. X    char *ptr;                        /* Pointer inside env_line */
  289. X    char *env;                        /* The current environment line */
  290. X
  291. X    if (-1 == ht_create(&henv, ENV_VARS))
  292. X        return -1;                    /* Cannot create H table */
  293. X
  294. X    while (env = *envp++) {
  295. X        strncpy(env_line, env, MAX_STRING);
  296. X        ptr = index(env_line, '=');
  297. X        if (ptr == (char *) 0) {
  298. X            add_log(6, "WARNING bad environment line");
  299. X            continue;
  300. X        }
  301. X        *ptr = '\0';                /* Before '=' lies the key */
  302. X        if ((char *) 0 == ht_put(&henv, env_line, ptr + 1)) {
  303. X            add_log(4, "ERROR cannot record environment any more");
  304. X            return -1;
  305. X        }
  306. X    }
  307. X
  308. X    return 0;    /* Ok */
  309. X}
  310. X
  311. Xpublic int append_env(key, value)
  312. Xchar *key;
  313. Xchar *value;
  314. X{
  315. X    /* Appends 'value' at the end of the environment variable 'key', if it
  316. X     * already exits, otherwise create it with that value.
  317. X     * Returns 0 for success, -1 for failure.
  318. X     */
  319. X    
  320. X    char env_line[MAX_STRING + 1];    /* Then environment line */
  321. X    char *cval;                        /* Current value */
  322. X
  323. X    cval = ht_value(&henv, key);
  324. X    if (cval == (char *) 0) {
  325. X        if ((char *) 0 == ht_put(&henv, key, value)) {
  326. X            add_log(1, "ERROR cannot insert environment variable '%s'", key);
  327. X            return -1;                /* Insertion failed */
  328. X        }
  329. X        return 0;                    /* Insertion ok */
  330. X    }
  331. X
  332. X    strncpy(env_line, cval, MAX_STRING);
  333. X    if (strlen(env_line) + strlen(value) > MAX_STRING) {
  334. X        add_log(1, "ERROR cannot append to environment variable '%s'", key);
  335. X        return -1;
  336. X    }
  337. X    strcat(env_line, value);
  338. X    if ((char *) 0 == ht_force(&henv, key, env_line)) {
  339. X        add_log(1, "ERROR cannot update environment variable '%s'", key);
  340. X        return -1;
  341. X    }
  342. X
  343. X    return 0;    /* Ok */
  344. X}
  345. X
  346. Xpublic int prepend_env(key, value)
  347. Xchar *key;
  348. Xchar *value;
  349. X{
  350. X    /* Prepends 'value' at the head of the environment variable 'key', if it
  351. X     * already exits, otherwise create it with that value.
  352. X     * Returns 0 for success, -1 for failure.
  353. X     */
  354. X    
  355. X    char env_line[MAX_STRING + 1];    /* Then environment line */
  356. X    char *cval;                        /* Current value */
  357. X
  358. X    cval = ht_value(&henv, key);
  359. X    if (cval == (char *) 0) {
  360. X        if ((char *) 0 == ht_put(&henv, key, value)) {
  361. X            add_log(1, "ERROR cannot insert environment variable '%s'", key);
  362. X            return -1;                /* Insertion failed */
  363. X        }
  364. X        return 0;                    /* Insertion ok */
  365. X    }
  366. X
  367. X    strncpy(env_line, value, MAX_STRING);
  368. X    if (strlen(env_line) + strlen(cval) > MAX_STRING) {
  369. X        add_log(1, "ERROR cannot prepend to environment variable '%s'", key);
  370. X        return -1;
  371. X    }
  372. X    strcat(env_line, cval);
  373. X    if ((char *) 0 == ht_force(&henv, key, env_line)) {
  374. X        add_log(1, "ERROR cannot update environment variable '%s'", key);
  375. X        return -1;
  376. X    }
  377. X
  378. X    return 0;    /* Ok */
  379. X}
  380. X
  381. Xpublic int set_env(key, value)
  382. Xchar *key;
  383. Xchar *value;
  384. X{
  385. X    /* Set environment value 'key' and return 0 for success, -1 for failure. */
  386. X
  387. X    char env_line[MAX_STRING + 1];    /* Then environment line */
  388. X    char *cval;                        /* Current value */
  389. X
  390. X    cval = ht_value(&henv, key);
  391. X    if (cval == (char *) 0) {
  392. X        if ((char *) 0 == ht_put(&henv, key, value)) {
  393. X            add_log(1, "ERROR cannot insert environment variable '%s'", key);
  394. X            return -1;                /* Insertion failed */
  395. X        }
  396. X        return 0;                    /* Insertion ok */
  397. X    }
  398. X
  399. X    if ((char *) 0 == ht_force(&henv, key, value)) {
  400. X        add_log(1, "ERROR cannot update environment variable '%s'", key);
  401. X        return -1;
  402. X    }
  403. X
  404. X    return 0;    /* Ok */
  405. X}
  406. X
  407. Xpublic char **make_env()
  408. X{
  409. X    /* Create the environment pointer suitable for the execle() system call.
  410. X     * Return a null pointer if there is not enough memory to create the
  411. X     * environment.
  412. X     */
  413. X
  414. X    char env_line[MAX_STRING + 1];    /* The environment line */
  415. X    char **envp;                    /* The environment pointer returned */
  416. X    char **ptr;                        /* Pointer in the environment */
  417. X    int nb_line;                    /* Number of lines */
  418. X
  419. X    nb_line = ht_count(&henv) + 1;    /* Envp ends with a null pointer */
  420. X    if (nb_line == 0) {
  421. X        add_log(6, "NOTICE environment is empty");
  422. X        return (char **) 0;
  423. X    }
  424. X    envp = (char **) malloc(nb_line * sizeof(char *));
  425. X    if (envp == (char **) 0)
  426. X        fatal("out of memory");
  427. X    
  428. X    if (-1 == ht_start(&henv))
  429. X        fatal("environment H table botched");
  430. X    
  431. X    ptr = envp;
  432. X    for (ptr = envp; --nb_line > 0; (void) ht_next(&henv), ptr++) {
  433. X        sprintf(env_line, "%s=%s", ht_ckey(&henv), ht_cvalue(&henv));
  434. X        *ptr = strsave(env_line);
  435. X        if (*ptr == (char *) 0)
  436. X            fatal("no more memory for environment");
  437. X    }
  438. X
  439. X    *ptr = (char *) 0;                /* Environment is NULL terminated */
  440. X
  441. X    return envp;                    /* Pointer to new environment */
  442. X}
  443. X
  444. END_OF_FILE
  445.   if test 6195 -ne `wc -c <'agent/filter/environ.c'`; then
  446.     echo shar: \"'agent/filter/environ.c'\" unpacked with wrong size!
  447.   fi
  448.   # end of 'agent/filter/environ.c'
  449. fi
  450. if test -f 'agent/filter/logfile.c' -a "${1}" != "-c" ; then 
  451.   echo shar: Will not clobber existing file \"'agent/filter/logfile.c'\"
  452. else
  453.   echo shar: Extracting \"'agent/filter/logfile.c'\" \(5659 characters\)
  454.   sed "s/^X//" >'agent/filter/logfile.c' <<'END_OF_FILE'
  455. X/*
  456. X
  457. X #        ####    ####   ######     #    #       ######           ####
  458. X #       #    #  #    #  #          #    #       #               #    #
  459. X #       #    #  #       #####      #    #       #####           #
  460. X #       #    #  #  ###  #          #    #       #        ###    #
  461. X #       #    #  #    #  #          #    #       #        ###    #    #
  462. X ######   ####    ####   #          #    ######  ######   ###     ####
  463. X
  464. X    Handles logging facilities.
  465. X*/
  466. X
  467. X/*
  468. X * $Id: logfile.c,v 2.9 92/07/14 16:48:22 ram Exp $
  469. X *
  470. X *  Copyright (c) 1992, Raphael Manfredi
  471. X *
  472. X *  You may redistribute only under the terms of the GNU General Public
  473. X *  Licence as specified in the README file that comes with dist.
  474. X *
  475. X * $Log:    logfile.c,v $
  476. X * Revision 2.9  92/07/14  16:48:22  ram
  477. X * 3.0 beta baseline.
  478. X * 
  479. X */
  480. X
  481. X#include "config.h"
  482. X#include "portable.h"
  483. X#include <stdio.h>
  484. X#include <sys/types.h>
  485. X
  486. X#ifdef I_TIME
  487. X# include <time.h>
  488. X#endif
  489. X#ifdef I_SYSTIME
  490. X# include <sys/time.h>
  491. X#endif
  492. X#ifdef I_SYSTIMEKERNEL
  493. X# define KERNEL
  494. X# include <sys/time.h>
  495. X# undef KERNEL
  496. X#endif
  497. X
  498. X#define MAX_STRING    1024            /* Maximum length for logging string */
  499. X
  500. Xprivate FILE *logfile = (FILE *) 0;    /* File pointer used for logging */
  501. Xshared int loglvl = 20;                /* Logging level */
  502. Xprivate char *logname;                /* Name of the logfile in use */
  503. Xprivate void expand();                /* Run the %m %e expansion on the string */
  504. Xprivate int add_error();            /* Prints description of error in errno */
  505. Xprivate int add_errcode();            /* Print the symbolic error name */
  506. X
  507. Xpublic char *progname = "ram";    /* Program name */
  508. Xpublic Pid_t progpid = 0;        /* Program PID */
  509. X
  510. Xextern Time_t time();            /* Time in seconds since the Epoch */
  511. Xextern char *malloc();            /* Memory allocation */
  512. Xextern char *strsave();            /* Save string in memory */
  513. Xextern int errno;                /* System error report variable */
  514. X
  515. X/* VARARGS2 */
  516. Xpublic void add_log(level, format, arg1, arg2, arg3, arg4, arg5)
  517. Xint level;
  518. Xchar *format;
  519. Xint arg1, arg2, arg3, arg4, arg5;
  520. X{
  521. X    /* Add logging informations at specified level. Note that the arguments are
  522. X     * declared as 'int', but it should work fine, even when we give doubles,
  523. X     * because they will be pased "as is" to fprintf. Maybe I should use
  524. X     * vfprintf when it is available--RAM.
  525. X     * The only magic string substitution which occurs is the '%m', which is
  526. X     * replaced by the error message, as given by errno and '%e' which gives
  527. X     * the symbolic name of the error (if available, otherwise the number).
  528. X     * The log file must have been opened with open_log() before add_log calls.
  529. X     */
  530. X
  531. X    struct tm *ct;                /* Current time (pointer to static data) */
  532. X    Time_t clock;                /* Number of seconds since the Epoch */
  533. X    char buffer[MAX_STRING];    /* Buffer which holds the expanded %m string */
  534. X
  535. X    if (loglvl < level)            /* Logging level is not high enough */
  536. X        return;
  537. X
  538. X    if (logfile == (FILE *) 0)    /* Logfile not opened for whatever reason */
  539. X        return;
  540. X
  541. X    clock = time((Time_t *) 0);    /* Number of seconds */
  542. X    ct = localtime(&clock);        /* Get local time from amount of seconds */
  543. X    expand(format, buffer);        /* Expansion of %m and %e into buffer */
  544. X
  545. X    fprintf(logfile, "%d/%.2d/%.2d %.2d:%.2d:%.2d %s[%d]: ",
  546. X        ct->tm_year, ct->tm_mon + 1, ct->tm_mday,
  547. X        ct->tm_hour, ct->tm_min, ct->tm_sec,
  548. X        progname, progpid);
  549. X
  550. X    fprintf(logfile, buffer, arg1, arg2, arg3, arg4, arg5);
  551. X    putc('\n', logfile);
  552. X    fflush(logfile);
  553. X}
  554. X
  555. Xpublic int open_log(name)
  556. Xchar *name;
  557. X{
  558. X    /* Open log file 'name' for logging. If a previous log file was opened,
  559. X     * it is closed before. The routine returns -1 in case of error.
  560. X     */
  561. X    
  562. X    if (logfile != (FILE *) 0)
  563. X        fclose(logfile);
  564. X    
  565. X    logfile = fopen(name, "a");        /* Append to existing file */
  566. X    logname = strsave(name);        /* Save file name */
  567. X
  568. X    if (logfile == (FILE *) 0)
  569. X        return -1;
  570. X    
  571. X    return 0;
  572. X}
  573. X
  574. Xpublic void close_log()
  575. X{
  576. X    /* Close log file */
  577. X
  578. X    if (logfile != (FILE *) 0)
  579. X        fclose(logfile);
  580. X
  581. X    logfile = (FILE *) 0;
  582. X}
  583. X
  584. Xpublic void set_loglvl(level)
  585. Xint level;
  586. X{
  587. X    /* Set logging level to 'level' */
  588. X
  589. X    loglvl = level;
  590. X}
  591. X
  592. Xprivate void expand(from, to)
  593. Xchar *from;
  594. Xchar *to;
  595. X{
  596. X    /* The string held in 'from' is copied into 'to' and every '%m' is expanded
  597. X     * into the error message deduced from the value of errno.
  598. X     */
  599. X
  600. X    int len;                            /* Length of substituted text */
  601. X
  602. X    while (*to++ = *from)
  603. X        if (*from++ == '%')
  604. X            switch (*from) {
  605. X            case 'm':                    /* %m is the English description */
  606. X                len = add_error(to - 1);
  607. X                to += len - 1;
  608. X                from++;
  609. X                break;
  610. X            case 'e':                    /* %e is the symbolic error code */
  611. X                len = add_errcode(to - 1);
  612. X                to += len - 1;
  613. X                from++;
  614. X                break;
  615. X            }
  616. X}
  617. X
  618. Xprivate int add_error(where)
  619. Xchar *where;
  620. X{
  621. X    /* Prints a description of the error code held in 'errno' into 'where' if
  622. X     * it is available, otherwise simply print the error code number.
  623. X     */
  624. X
  625. X#ifdef SYSERRLIST
  626. X    extern int sys_nerr;                    /* Size of sys_errlist[] */
  627. X    extern char *sys_errlist[];                /* Maps error code to string */
  628. X#endif
  629. X
  630. X#ifdef STRERROR
  631. X    sprintf(where, "%s", strerror(errno));
  632. X#else
  633. X#ifdef SYSERRLIST
  634. X    sprintf(where, "%s", strerror(errno));    /* Macro defined by Configure */
  635. X#else
  636. X    sprintf(where, "error #%d", errno);
  637. X#endif
  638. X#endif
  639. X
  640. X    return strlen(where);
  641. X}
  642. X
  643. Xprivate int add_errcode(where)
  644. Xchar *where;
  645. X{
  646. X    /* Prints the symbolic description of the error code heldin in 'errno' into
  647. X     * 'where' if possible. Otherwise, prints the error number.
  648. X     */
  649. X    
  650. X#ifdef SYSERRNOLIST
  651. X    extern int sys_nerrno;                    /* Size of sys_errnolist[] */
  652. X    extern char *sys_errnolist[];            /* Error code to symbolic name */
  653. X#endif
  654. X
  655. X#ifdef SYSERRNOLIST
  656. X    if (errno < 0 || errno >= sys_nerrno)
  657. X        sprintf(where, "UNKNOWN");
  658. X    else
  659. X        sprintf(where, "%s", sys_errnolist[errno]);
  660. X#else
  661. X        sprintf(where, "%d", errno);
  662. X#endif
  663. X
  664. X    return strlen(where);
  665. X}
  666. X
  667. END_OF_FILE
  668.   if test 5659 -ne `wc -c <'agent/filter/logfile.c'`; then
  669.     echo shar: \"'agent/filter/logfile.c'\" unpacked with wrong size!
  670.   fi
  671.   # end of 'agent/filter/logfile.c'
  672. fi
  673. if test -f 'agent/pl/eval.pl' -a "${1}" != "-c" ; then 
  674.   echo shar: Will not clobber existing file \"'agent/pl/eval.pl'\"
  675. else
  676.   echo shar: Extracting \"'agent/pl/eval.pl'\" \(6192 characters\)
  677.   sed "s/^X//" >'agent/pl/eval.pl' <<'END_OF_FILE'
  678. X;# $Id: eval.pl,v 2.9 92/07/14 16:49:53 ram Exp $
  679. X;#
  680. X;#  Copyright (c) 1992, Raphael Manfredi
  681. X;#
  682. X;#  You may redistribute only under the terms of the GNU General Public
  683. X;#  Licence as specified in the README file that comes with dist.
  684. X;#
  685. X;# $Log:    eval.pl,v $
  686. X;# Revision 2.9  92/07/14  16:49:53  ram
  687. X;# 3.0 beta baseline.
  688. X;# 
  689. X;# 
  690. X#
  691. X# The built-in expression interpreter
  692. X#
  693. X
  694. X# Initialize the interpreter
  695. Xsub init_interpreter {
  696. X    do set_priorities();    # Fill in %Priority
  697. X    do set_functions();        # Fill in %Function
  698. X    $macro_T = "the Epoch";    # Default value for %T macro substitution
  699. X}
  700. X
  701. X# Priorities for operators -- magic numbers :-)
  702. X# An operator with higher priority will evaluate before another with a lower
  703. X# one. For instance, given the priorities listed hereinafter, a && b == c
  704. X# would evaluate as a && (b == c).
  705. Xsub set_priorities {
  706. X    $Priority{'&&'} = 4;
  707. X    $Priority{'||'} = 3;
  708. X    $Priority{'>='} = 6;
  709. X    $Priority{'<='} = 6;
  710. X    $Priority{'<'}  = 6;
  711. X    $Priority{'>'}  = 6;
  712. X    $Priority{'=='} = 6;
  713. X    $Priority{'!='} = 6;
  714. X    $Priority{'='}  = 6;
  715. X    $Priority{'/='} = 6;
  716. X}
  717. X
  718. X# Perl functions handling operators
  719. Xsub set_functions {
  720. X    $Function{'&&'} = 'f_and';            # Boolean AND
  721. X    $Function{'||'} = 'f_or';            # Boolean OR
  722. X    $Function{'>='} = 'f_ge';            # Greater or equal
  723. X    $Function{'<='} = 'f_le';            # Lesser or equal
  724. X    $Function{'<'}  = 'f_lt';            # Lesser than
  725. X    $Function{'>'}  = 'f_gt';            # Greader than
  726. X    $Function{'=='} = 'f_eq';            # Equality
  727. X    $Function{'!='} = 'f_ne';            # Difference (not equality)
  728. X    $Function{'='}  = 'f_match';        # Pattern matching
  729. X    $Function{'/='} = 'f_nomatch';        # Pattern matching (no match)
  730. X}
  731. X
  732. X# Print error messages -- asssumes $unit and $. correctly set.
  733. Xsub error {
  734. X    do add_log("ERROR @_") if $loglvl > 1;
  735. X}
  736. X
  737. X# Add a value on the stack, modified by all the monadic operators.
  738. X# We use the locals @val and @mono from eval_expr.
  739. Xsub push_val {
  740. X    local($val) = shift(@_);
  741. X    while ($#mono >= 0) {
  742. X        # Cheat... the only monadic operator is '!'.
  743. X        pop(@mono);
  744. X        $val = !$val;
  745. X    }
  746. X    push(@val, $val);
  747. X}
  748. X
  749. X# Execute a stacked operation, leave result in stack.
  750. X# We use the locals @val and @op from eval_expr.
  751. X# If the value stack holds only one operand, do nothing.
  752. Xsub execute {
  753. X    return unless $#val > 0;
  754. X    local($op) = pop(@op);            # The operator
  755. X    local($val2) = pop(@val);        # Right value in algebraic notation
  756. X    local($val1) = pop(@val);        # Left value in algebraic notation
  757. X    local($func) = $Function{$op};    # Function to be called
  758. X    do macros_subst(*val1);            # Expand macros
  759. X    do macros_subst(*val2);
  760. X    push(@val, eval("do $func($val1, $val2)") ? 1: 0);
  761. X}
  762. X
  763. X# Given an operator, either we add it in the stack @op, because its
  764. X# priority is lower than the one on top of the stack, or we first execute
  765. X# the stacked operations until we reach the end of stack or an operand
  766. X# whose priority is lower than ours.
  767. X# We use the locals @val and @op from eval_expr.
  768. Xsub update_stack {
  769. X    local($op) = shift(@_);        # Operator
  770. X    if (!$Priority{$op}) {
  771. X        do error("illegal operator $op");
  772. X        return;
  773. X    } else {
  774. X        if ($#val < 0) {
  775. X            do error("missing first operand for '$op' (diadic operator)");
  776. X            return;
  777. X        }
  778. X        # Because of a bug in perl 4.0 PL19, I'm using a loop construct
  779. X        # instead of a while() modifier.
  780. X        while (
  781. X            $Priority{$op[$#op]} > $Priority{$op}    # Higher priority op
  782. X            && $#val > 0                            # At least 2 values
  783. X        ) {
  784. X            do execute();    # Execute an higer priority stacked operation
  785. X        }
  786. X        push(@op, $op);        # Everything at higher priority has been executed
  787. X    }
  788. X}
  789. X
  790. X# This is the heart of our little interpreter. Here, we evaluate
  791. X# a logical expression and return its value.
  792. Xsub eval_expr {
  793. X    local(*expr) = shift(@_);    # Expression to parse
  794. X    local(@val) = ();            # Stack of values
  795. X    local(@op) = ();            # Stack of diadic operators
  796. X    local(@mono) =();            # Stack of monadic operators
  797. X    local($tmp);
  798. X    $_ = $expr;
  799. X    while (1) {
  800. X        s/^\s+//;                # Remove spaces between words
  801. X        # A perl statement <<command>>
  802. X        if (s/^<<//) {
  803. X            if (s/^(.*)>>//) {
  804. X                do push_val((system
  805. X                    ('perl','-e', "if ($1) {exit 0;} else {exit 1;}"
  806. X                    ))? 0 : 1);
  807. X            } else {
  808. X                do error("incomplete perl statement");
  809. X            }
  810. X        }
  811. X        # A shell statement <command>
  812. X        elsif (s/^<//) {
  813. X            if (s/^(.*)>//) {
  814. X                do push_val((system
  815. X                    ("if $1 >/dev/null 2>&1; then exit 0; else exit 1; fi"
  816. X                    ))? 0 : 1);
  817. X            } else {
  818. X                do error("incomplete shell statement");
  819. X            }
  820. X        }
  821. X        # The '(' construct
  822. X        elsif (s/^\(//) {
  823. X            do push_val(do eval_expr(*_));
  824. X            # A final '\' indicates an end of line
  825. X            do error("missing final parenthesis") if !s/^\\//;
  826. X        }
  827. X        # Found a ')' or end of line
  828. X        elsif (/^\)/ || /^$/) {
  829. X            s/^\)/\\/;                        # Signals: left parenthesis found
  830. X            $expr = $_;                        # Remove interpreted stuff
  831. X            do execute() while $#val > 0;    # Executed stacked operations
  832. X            while ($#op >= 0) {
  833. X                $_ = pop(@op);
  834. X                do error("missing second operand for '$_' (diadic operator)");
  835. X            }
  836. X            return $val[0];
  837. X        }
  838. X        # Diadic operators
  839. X        elsif (s/^(\|\||&&|>=|<=|>|<|==|!=|=|\/=)//) {
  840. X            do update_stack($1);
  841. X        }
  842. X        # Unary operator '!'
  843. X        elsif (s/^!//) {
  844. X            push(@mono,'!');
  845. X        }
  846. X        # Everything else is a value which stands for itself (atom)
  847. X        elsif (s/^([\w'"%]+)//) {
  848. X            do push_val($1);
  849. X        }
  850. X        # Syntax error
  851. X        else {
  852. X            print "Syntax error: remaining is >>>$_<<<\n";
  853. X            $_ = "";
  854. X        }
  855. X    }
  856. X}
  857. X
  858. X# Call eval_expr and check that everything is ok (e.g. the stack must be empty)
  859. Xsub evaluate {
  860. X    local($val);                    # Value returned
  861. X    local(*expr) = shift(@_);        # Expression to be parsed
  862. X    while ($expr) {
  863. X        $val = do eval_expr(*expr);        # Expression will be modified
  864. X        print "extra closing parenthesis ignored.\n" if $expr =~ s/^\\\)*//;
  865. X        $expr = $val . $expr if $expr ne '';
  866. X    }
  867. X    $val;
  868. X}
  869. X
  870. X#
  871. X# Boolean functions used by the interpreter. They all take two arguments
  872. X# and return 0 if false and 1 if true.
  873. X#
  874. X
  875. Xsub f_and { $_[0] && $_[1]; }        # Boolean AND
  876. Xsub f_or { $_[0] || $_[1]; }        # Boolean OR
  877. Xsub f_ge { $_[0] >= $_[1]; }        # Greater or equal
  878. Xsub f_le { $_[0] <= $_[1]; }        # Lesser or equal
  879. Xsub f_lt { $_[0] < $_[1]; }            # Lesser than
  880. Xsub f_gt { $_[0] > $_[1]; }            # Greater than
  881. Xsub f_eq { "$_[0]" eq "$_[1]"; }    # Equal
  882. Xsub f_ne { "$_[0]" ne "$_[1]"; }    # Not equal
  883. Xsub f_match { $_[0] =~ /$_[1]/; }    # Pattern matches
  884. Xsub f_nomatch { $_[0] !~ /$_[1]/; }    # Pattern does not match
  885. X
  886. END_OF_FILE
  887.   if test 6192 -ne `wc -c <'agent/pl/eval.pl'`; then
  888.     echo shar: \"'agent/pl/eval.pl'\" unpacked with wrong size!
  889.   fi
  890.   # end of 'agent/pl/eval.pl'
  891. fi
  892. if test -f 'agent/pl/header.pl' -a "${1}" != "-c" ; then 
  893.   echo shar: Will not clobber existing file \"'agent/pl/header.pl'\"
  894. else
  895.   echo shar: Extracting \"'agent/pl/header.pl'\" \(6416 characters\)
  896.   sed "s/^X//" >'agent/pl/header.pl' <<'END_OF_FILE'
  897. X;# $Id: header.pl,v 2.9.1.2 92/08/26 13:12:31 ram Exp $
  898. X;#
  899. X;#  Copyright (c) 1992, Raphael Manfredi
  900. X;#
  901. X;#  You may redistribute only under the terms of the GNU General Public
  902. X;#  Licence as specified in the README file that comes with dist.
  903. X;#
  904. X;# $Log:    header.pl,v $
  905. X;# Revision 2.9.1.2  92/08/26  13:12:31  ram
  906. X;# patch8: random clean up
  907. X;# 
  908. X;# Revision 2.9.1.1  92/08/02  16:10:59  ram
  909. X;# patch2: added routines for normalization and formatting
  910. X;# 
  911. X;# Revision 2.9  92/07/14  16:50:06  ram
  912. X;# 3.0 beta baseline.
  913. X;# 
  914. X;# 
  915. Xpackage header;
  916. X
  917. X# This package implements a header checker. To initialize it, call 'reset'.
  918. X# Then, call 'valid' with a header line and the function returns 0 if the
  919. X# line is not part of a header (which means all the lines seen since 'reset'
  920. X# are not part of a mail header). If the line may still be part of a header,
  921. X# returns 1. Finally, -1 is returned at the end of the header.
  922. X
  923. Xsub init {
  924. X    # Main header fields which should be looked at when parsing a mail header
  925. X    %Mailheader = (
  926. X        'From', 1,
  927. X        'To', 1,
  928. X        'Subject', 1,
  929. X        'Date', 1,
  930. X    );
  931. X}
  932. X
  933. X# Reset header checking status
  934. Xsub reset {
  935. X    &init unless $init_done++;        # Initialize private data
  936. X    $last_was_header = 0;            # Previous line was not a header
  937. X    $maybe = 0;                        # Do we have a valid part of header?
  938. X    $line = 0;                        # Count number of lines in header
  939. X}
  940. X
  941. X# Is the current line still part of a valid header ?
  942. Xsub valid {
  943. X    local($_) = @_;
  944. X    return 1 if $last_was_header && /^\s/;    # Continuation line
  945. X    return -1 if /^$/;                        # End of header
  946. X    $last_was_header = /^([\w\-]+):/ ? 1 : 0;
  947. X    # Activate $maybe when essential parts of a valid mail header are found
  948. X    # Any client can check 'maybe' to see if what has been parsed so far would
  949. X    # be a valid RFC-822 header, even though syntactically correct.
  950. X    $maybe |= $Mailheader{$1} if $last_was_header;
  951. X    $last_was_header = /^From\s+\S+/
  952. X        unless $last_was_header || $line;    # First line may be special
  953. X    ++$line;                                # One more line
  954. X    $last_was_header;                        # Are we still inside header?
  955. X}
  956. X
  957. X# Produce a warning header field about a specific item
  958. Xsub warning {
  959. X    local($field, $added) = @_;
  960. X    local($warning);
  961. X    local(@field) = split(' ', $field);
  962. X    $warning = 'X-Filter-Note: ';
  963. X    if ($added && @field == 1) {
  964. X        $warning .= "Previous line added at ";
  965. X    } elsif ($added && @field > 1) {
  966. X        $field = join(', ', @field);
  967. X        $field =~ s/^(.*), (.*)/$1 and $2/;
  968. X        $warning .= "Headers $field added at ";
  969. X    } else {
  970. X        $warning .= "Parsing error in original previous line at ";
  971. X    }
  972. X    $warning .= &main'domain_addr;
  973. X    $warning;
  974. X}
  975. X
  976. X# Make sure header contains vital fields. The header is held in an array, on
  977. X# a line basis with final new-line chopped. The array is modified in place,
  978. X# setting defaults from the %Header array (if defined, which is the case for
  979. X# digests mails) or using local defaults.
  980. Xsub clean {
  981. X    local(*array) = @_;                    # Array holding the header
  982. X    local($added) = '';                    # Added fields
  983. X
  984. X    $added .= &check(*array, 'From', $cf'user, 1);
  985. X    $added .= &check(*array, 'To', $cf'user, 1);
  986. X    $added .= &check(*array, 'Date', &fake_date, 0);
  987. X    $added .= &check(*array, 'Subject', '<none>', 1);
  988. X
  989. X    &push(*array, &warning($added, 1)) if $added ne '';
  990. X}
  991. X
  992. X# Check presence of specific field and use value of %Header as a default if
  993. X# available and if '$use_header' is set, otherwise use the provided value.
  994. X# Return added field or a null string if nothing is done.
  995. Xsub check {
  996. X    local(*array, $field, $default, $use_header) = @_;
  997. X    local($faked);                        # Faked value to be used
  998. X    if ($use_header) {
  999. X        $faked = (defined $'Header{$field}) ? $'Header{$field} : $default;
  1000. X    } else {
  1001. X        $faked = $default;
  1002. X    }
  1003. X
  1004. X    # Try to locate field in header
  1005. X    local($_);
  1006. X    foreach (@array) {
  1007. X        return '' if /^$field:/;
  1008. X    }
  1009. X
  1010. X    &push(*array, "$field: $faked");
  1011. X    $field . ' ';
  1012. X}
  1013. X
  1014. X# Push header line at the end of the array, without assuming any final EOH line
  1015. Xsub push {
  1016. X    local(*array, $line) = @_;
  1017. X    local($last) = pop(@array);
  1018. X    push(@array, $last) if $last ne '';    # There was no EOH
  1019. X    push(@array, $line);                # Insert header line
  1020. X    push(@array, '') if $last eq '';    # Restore EOH
  1021. X}
  1022. X
  1023. X# Compute a valid date field suitable for mail header
  1024. Xsub fake_date {
  1025. X    require 'ctime.pl';
  1026. X    local($date) = &'ctime(time);
  1027. X    # Traditionally, MTAs add a ',' right after week day
  1028. X    $date =~ s/^(\w+)(\s)/$1,$2/;
  1029. X    chop($date);                    # Ctime adds final new-line
  1030. X    $date;
  1031. X}
  1032. X
  1033. X# Normalizes header: every first letter is uppercase, the remaining of the
  1034. X# word being lowercased, as in This-Is-A-Normalized-Header. Note that RFC-822
  1035. X# does not impose such a formatting.
  1036. Xsub normalize {
  1037. X    local($field_name) = @_;            # Header to be normalized
  1038. X    $field_name =~ s/(\w+)/\u\L$1/g;
  1039. X    $field_name;                        # Return header name with proper case
  1040. X}
  1041. X
  1042. X# Format header field to fit into 78 columns, each continuation line being
  1043. X# indented by 8 chars. Returns the new formatted header string.
  1044. Xsub format {
  1045. X    local($field) = @_;            # Field to be formatted
  1046. X    local($tmp);                # Buffer for temporary formatting
  1047. X    local($new) = '';            # Constructed formatted header
  1048. X    local($kept);                # Length of current line
  1049. X    local($len) = 78;            # Amount of characters kept
  1050. X    local($cont) = ' ' x 8;        # Continuation lines starts with 8 spaces
  1051. X    # Format header field, separating lines on ',' or space.
  1052. X    while (length($field) > $len) {
  1053. X        $tmp = substr($field, 0, $len);        # Keep first $len chars
  1054. X        $tmp =~ s/^(.*)([,\s]).*/$1$2/;        # Cut at last space or ,
  1055. X        $kept = length($tmp);                # Amount of chars we kept
  1056. X        $tmp =~ s/\s*$//;                    # Remove trailing spaces
  1057. X        $tmp =~ s/^\s*//;                    # Remove leading spaces
  1058. X        $new .= $cont if $new;                # Continuation starts with 8 spaces
  1059. X        $len = 70;                            # Account continuation for next line
  1060. X        $new .= "$tmp\n";
  1061. X        $field = substr($field, $kept, 9999);
  1062. X    }
  1063. X    $new .= $cont if $new;                    # Add 8 chars if continuation
  1064. X    $new .= $field;                            # Remaining information on one line
  1065. X}
  1066. X
  1067. X# Scan the head of a file and try to determine whether there is a mail
  1068. X# header at the beginning or not. Return true if a header was found.
  1069. Xsub main'header_found {
  1070. X    local($file) = @_;
  1071. X    local($correct) = 1;                # Were all the lines from top correct ?
  1072. X    local($_);
  1073. X    open(FILE, $file) || return 0;        # Don't care to report error
  1074. X    &reset;                                # Initialize header checker
  1075. X    while (<FILE>) {                    # While still in a possible header
  1076. X        last if /^$/;                    # Exit if end of header reached
  1077. X        $correct = &valid($_);            # Check line validity
  1078. X        last unless $correct;            # No, not a valid header
  1079. X    }
  1080. X    close FILE;
  1081. X    $correct;
  1082. X}
  1083. X
  1084. Xpackage main;
  1085. X
  1086. END_OF_FILE
  1087.   if test 6416 -ne `wc -c <'agent/pl/header.pl'`; then
  1088.     echo shar: \"'agent/pl/header.pl'\" unpacked with wrong size!
  1089.   fi
  1090.   # end of 'agent/pl/header.pl'
  1091. fi
  1092. if test -f 'agent/pl/interface.pl' -a "${1}" != "-c" ; then 
  1093.   echo shar: Will not clobber existing file \"'agent/pl/interface.pl'\"
  1094. else
  1095.   echo shar: Extracting \"'agent/pl/interface.pl'\" \(5290 characters\)
  1096.   sed "s/^X//" >'agent/pl/interface.pl' <<'END_OF_FILE'
  1097. X;# $Id: interface.pl,v 2.9.1.3 92/11/10 10:14:02 ram Exp $
  1098. X;#
  1099. X;#  Copyright (c) 1992, Raphael Manfredi
  1100. X;#
  1101. X;#  You may redistribute only under the terms of the GNU General Public
  1102. X;#  Licence as specified in the README file that comes with dist.
  1103. X;#
  1104. X;# $Log:    interface.pl,v $
  1105. X;# Revision 2.9.1.3  92/11/10  10:14:02  ram
  1106. X;# patch12: perl command interface changed to return boolean success
  1107. X;# 
  1108. X;# Revision 2.9.1.2  92/11/01  15:50:39  ram
  1109. X;# patch11: fixed English typo
  1110. X;# 
  1111. X;# Revision 2.9.1.1  92/08/26  13:14:28  ram
  1112. X;# patch8: created
  1113. X;# 
  1114. X;# 
  1115. X;# This is for people who, like me, are perl die-hards :-). It simply provides
  1116. X;# a simple perl interface for hook scripts and PERL commands. Instead of
  1117. X;# writing 'COMMAND with some arguments;' in the filter rule file, you may say
  1118. X;# &command('with some arguments') in the perl script. Big deal! Well, at least
  1119. X;# that brings you some other nice features from perl itself ;-).
  1120. X;#
  1121. X#
  1122. X# Perl interface with the filter actions
  1123. X#
  1124. X
  1125. Xpackage mailhook;
  1126. X
  1127. Xsub abort        { &interface'dispatch; }
  1128. Xsub annotate    { &interface'dispatch; }
  1129. Xsub assign        { &interface'dispatch; }
  1130. Xsub back        { &interface'dispatch; }
  1131. Xsub begin        { &interface'dispatch; }
  1132. Xsub bounce        { &interface'dispatch; }
  1133. Xsub delete        { &interface'dispatch; }
  1134. Xsub feed        { &interface'dispatch; }
  1135. Xsub forward        { &interface'dispatch; }
  1136. Xsub give        { &interface'dispatch; }
  1137. Xsub keep        { &interface'dispatch; }
  1138. Xsub leave        { &interface'dispatch; }
  1139. Xsub message        { &interface'dispatch; }
  1140. Xsub nop            { &interface'dispatch; }
  1141. Xsub notify        { &interface'dispatch; }
  1142. Xsub once        { &interface'dispatch; }
  1143. Xsub pass        { &interface'dispatch; }
  1144. Xsub perl        { &interface'dispatch; }
  1145. Xsub pipe        { &interface'dispatch; }
  1146. Xsub post        { &interface'dispatch; }
  1147. Xsub process        { &interface'dispatch; }
  1148. Xsub purify        { &interface'dispatch; }
  1149. Xsub queue        { &interface'dispatch; }
  1150. Xsub record        { &interface'dispatch; }
  1151. Xsub reject        { &interface'dispatch; }
  1152. Xsub restart        { &interface'dispatch; }
  1153. Xsub resync        { &interface'dispatch; }
  1154. Xsub run            { &interface'dispatch; }
  1155. Xsub save        { &interface'dispatch; }
  1156. Xsub select        { &interface'dispatch; }
  1157. Xsub split        { &interface'dispatch; }
  1158. Xsub store        { &interface'dispatch; }
  1159. Xsub strip        { &interface'dispatch; }
  1160. Xsub subst        { &interface'dispatch; }
  1161. Xsub tr            { &interface'dispatch; }
  1162. Xsub unique        { &interface'dispatch; }
  1163. Xsub vacation    { &interface'dispatch; }
  1164. Xsub write        { &interface'dispatch; }
  1165. X
  1166. X# A perl filtering script should call &exit and not exit directly.
  1167. Xsub exit { 
  1168. X    local($code) = @_;
  1169. X    die "OK\n" unless $code;
  1170. X    die "Exit $code\n";
  1171. X}
  1172. X
  1173. Xpackage interface;
  1174. X
  1175. X# Perload OFF
  1176. X# (Cannot be dynamically loaded as it uses the caller() function)
  1177. X
  1178. X# The dispatch routine is really simple. We compute the name of our caller,
  1179. X# prepend it to the argument and call run_command to actually run the command.
  1180. X# Upon return, if we get anything but a continue status, we simply die with
  1181. X# an 'OK' string, which will be a signal to the routine monitoring the execution
  1182. X# that nothing wrong happened.
  1183. Xsub dispatch {
  1184. X    local($args) = join(' ', @_);            # Arguments for the command
  1185. X    local($name) = (caller(1))[3];            # Function which called us
  1186. X    local($status);                            # Continuation status
  1187. X    $name =~ s/^\w+'//;                        # Strip leading package name
  1188. X    &'add_log("calling '$name $args'") if $'loglvl > 18;
  1189. X    $status = &'run_command("$name $args");    # Case does not matter
  1190. X
  1191. X    # The status propagation is the only thing we have to deal with, as this
  1192. X    # is handled within run_command. All other variables which are meaningful
  1193. X    # for the filter are dynamically bound to function called before in the
  1194. X    # stack, hence they are modified directly from within the perl script.
  1195. X
  1196. X    die "Status $status\n" unless $status == $'FT_CONT;
  1197. X
  1198. X    # Return the status held in $lastcmd, unless the command does not alter
  1199. X    # the status significantly, in which case we return success. Note that
  1200. X    # this is in fact a boolean success status, so 1 means success, whereas
  1201. X    # $lastcmd records a failure status.
  1202. X
  1203. X    $name =~ tr/a-z/A-Z/;                    # Stored upper-cased
  1204. X    $'Nostatus{$name} ? 1 : !$lastcmd;        # Propagate status
  1205. X}
  1206. X
  1207. X# Perload ON
  1208. X
  1209. X$in_perl = 0;                    # Number of nested perl evaluations
  1210. X
  1211. X# Record entry in new perl evaluation
  1212. Xsub new {
  1213. X    ++$in_perl;                    # Add one evalution level
  1214. X}
  1215. X
  1216. X# Reset an empty mailhook package by undefining all its symbols.
  1217. X# (Warning: heavy wizardry used here -- look at perl's manpage for recipe.)
  1218. Xsub reset {
  1219. X    return if --$in_perl > 0;    # Do nothing if pending evals remain
  1220. X    &'add_log("undefining variables from mailhook") if $'loglvl > 11;
  1221. X    local($key, $val);            # Key/value from perl's symbol table
  1222. X    # Loop over perl's symbol table for the mailhook package
  1223. X    while (($key, $val) = each(%_mailhook)) {
  1224. X        local(*entry) = $val;    # Get definitions of current slot
  1225. X        undef $entry unless length($key) == 1 && $key !~ /^\w/;
  1226. X        undef @entry;
  1227. X        undef %entry unless $key =~ /^_/ || $key eq 'header';
  1228. X        undef &entry if &valid($key);
  1229. X        $_mailhook{$key} = *entry;    # Commit our changes
  1230. X    }
  1231. X}
  1232. X
  1233. X# Return true if the function may safely be undefined
  1234. Xsub valid {
  1235. X    local($fun) = @_;            # Function name
  1236. X    return 0 if $fun eq 'exit';    # This function is a convenience
  1237. X    # We cannot undefine a filter function, which are listed (upper-cased) in
  1238. X    # the %main'Filter table.
  1239. X    return 1 unless length($fun) == ($fun =~ tr/a-z/A-Z/);
  1240. X    return 1 unless $'Filter{$fun};
  1241. X    0;
  1242. X}
  1243. X    
  1244. Xpackage main;
  1245. X
  1246. END_OF_FILE
  1247.   if test 5290 -ne `wc -c <'agent/pl/interface.pl'`; then
  1248.     echo shar: \"'agent/pl/interface.pl'\" unpacked with wrong size!
  1249.   fi
  1250.   # end of 'agent/pl/interface.pl'
  1251. fi
  1252. if test -f 'agent/pl/parse.pl' -a "${1}" != "-c" ; then 
  1253.   echo shar: Will not clobber existing file \"'agent/pl/parse.pl'\"
  1254. else
  1255.   echo shar: Extracting \"'agent/pl/parse.pl'\" \(5848 characters\)
  1256.   sed "s/^X//" >'agent/pl/parse.pl' <<'END_OF_FILE'
  1257. X;# $Id: parse.pl,v 2.9.1.1 92/08/26 13:17:47 ram Exp $
  1258. X;#
  1259. X;#  Copyright (c) 1992, Raphael Manfredi
  1260. X;#
  1261. X;#  You may redistribute only under the terms of the GNU General Public
  1262. X;#  Licence as specified in the README file that comes with dist.
  1263. X;#
  1264. X;# $Log:    parse.pl,v $
  1265. X;# Revision 2.9.1.1  92/08/26  13:17:47  ram
  1266. X;# patch8: created by extraction from analyze.pl
  1267. X;# patch8: parsing can now be done on header only
  1268. X;# 
  1269. X;# 
  1270. X#
  1271. X# Parsing mail
  1272. X#
  1273. X
  1274. X# Parse the mail and fill-in the Header associative array. The special entries
  1275. X# All, Body and Head respectively hold the whole message, the body and the
  1276. X# header of the message.
  1277. Xsub parse_mail {
  1278. X    local($file_name) = shift(@_);    # Where mail is stored ("" for stdin)
  1279. X    local($head_only) = shift(@_);    # Optional parameter: parse only header
  1280. X    local($last_header) = "";        # Name of last header (for continuations)
  1281. X    local($first_from) = "";        # The first From line in mails
  1282. X    local($lines) = 0;                # Number of lines in the body
  1283. X    local($length) = 0;                # Length of body, in bytes
  1284. X    local($last_was_nl) = 1;        # True when last line was a '\n' (1 for EOH)
  1285. X    local($fd) = STDIN;                # Where does the mail come from ?
  1286. X    local($value);                    # Value of current field line
  1287. X    local($_);
  1288. X    undef %Header;                    # Reset the all structure holding message
  1289. X
  1290. X    if ($file_name ne '') {            # Mail spooled in a file
  1291. X        unless(open(MAIL, $file_name)) {
  1292. X            &add_log("ERROR cannot open $file_name: $!");
  1293. X            return;
  1294. X        }
  1295. X        $fd = MAIL;
  1296. X    }
  1297. X    $Userpath = "";                    # Reset path from possible previous @PATH 
  1298. X
  1299. X    # Pre-extend 'All', 'Body' and 'Head'
  1300. X    $Header{'All'} = ' ' x 5000;
  1301. X    $Header{'Body'} = ' ' x 4500;
  1302. X    $Header{'Head'} = ' ' x 500;
  1303. X    $Header{'All'} = '';
  1304. X    $Header{'Body'} = '';
  1305. X    $Header{'Head'} = '';
  1306. X
  1307. X    do add_log ("parsing mail") if $loglvl > 18;
  1308. X    while (<$fd>) {
  1309. X        $Header{'All'} .= $_;
  1310. X        if (1../^$/) {                        # EOH is a blank line
  1311. X            next if /^$/;                    # Skip EOH marker
  1312. X            $Header{'Head'} .= $_;            # Record line in header
  1313. X
  1314. X            if (/^\s/) {                    # It is a continuation line
  1315. X                s/^\s+/ /;                    # Swallow multiple spaces
  1316. X                chop;                        # Remove final new-line
  1317. X                $Header{$last_header} .= "\n$_" if $last_header ne '';
  1318. X                do add_log("WARNING bad continuation in header, line $.")
  1319. X                    if $last_header eq '' && $loglvl > 4;
  1320. X            } elsif (/^([\w-]+):\s*(.*)/) {    # We found a new header
  1321. X                # Guarantee only one From: header line. If multiple From: are
  1322. X                # found, keep the last one.
  1323. X                # Multiple headers like 'Received' are separated by a new-
  1324. X                # line character. All headers end on a non new-line.
  1325. X                # Case is normalized before recording, so apparently-to will
  1326. X                # be recorded as Apparently-To but header is not changed.
  1327. X                $value = $2;                # Bug in perl 4.0 PL19
  1328. X                $last_header = &header'normalize($1);    # Normalize case
  1329. X                if ($last_header eq 'From' && defined $Header{$last_header}) {
  1330. X                    $Header{$last_header} = $value;
  1331. X                    do add_log("WARNING duplicate From in header, line $.")
  1332. X                        if $loglvl > 4;
  1333. X                } elsif ($Header{$last_header} ne '') {
  1334. X                    $Header{$last_header} .= "\n$value";
  1335. X                } else {
  1336. X                    $Header{$last_header} .= $value;
  1337. X                }
  1338. X            } elsif (/^From\s+(\S+)/) {        # The very first From line
  1339. X                $first_from = $1;
  1340. X            }
  1341. X
  1342. X        } else {
  1343. X            last if $head_only;        # Stop parsing if only header wanted
  1344. X            $lines++;                                # One more line in body
  1345. X            $length += length($_);                    # Update length of message
  1346. X            s/^From(\s)/>From$1/ if $last_was_nl;    # Escape From keyword
  1347. X            $last_was_nl = /^$/;                    # Keep track of single '\n'
  1348. X            $Header{'Body'} .= $_;
  1349. X            chop;
  1350. X            # Deal with builtin commands
  1351. X            if (s/^@(\w+)\s*//) {                    # A builtin command ?
  1352. X                local($subroutine) = $Builtin{$1};
  1353. X                &$subroutine($_) if $subroutine;
  1354. X            }
  1355. X        }
  1356. X    }
  1357. X    close MAIL if $file_name ne '';
  1358. X    &header_check($first_from, $lines);    # Sanity checks
  1359. X}
  1360. X
  1361. X# Now do some sanity checks:
  1362. X# - if there is no From: header, fill it in with the first From
  1363. X# - if there is no To: but an Apparently-To:, copy it also as a To:
  1364. X#
  1365. X# We guarantee the following header entries:
  1366. X#   From:         the value of the From field
  1367. X#   To:           to whom the mail was sent
  1368. X#   Lines:        number of lines in the message
  1369. X#   Length:       number of bytes in the message
  1370. X#   Reply-To:     the address we may use to reply
  1371. X#   Sender:       the actual sender, even if same as From
  1372. X
  1373. Xsub header_check {
  1374. X    local($first_from, $lines) = @_;    # First From line, number of lines
  1375. X    unless (defined $Header{'From'}) {
  1376. X        &add_log("WARNING no From: field, assuming $first_from") if $loglvl > 4;
  1377. X        $Header{'From'} = $first_from;
  1378. X    }
  1379. X
  1380. X    # There is usually one Apparently-To line per address. Remove all new lines
  1381. X    # in the header line and replace them with ','.
  1382. X    $* = 1;
  1383. X    $Header{'Apparently-To'} =~ s/\n/,/g;    # Remove new-lines
  1384. X    $* = 0;
  1385. X    $Header{'Apparently-To'} =~ s/,$/\n/;    # Restore last new-line
  1386. X
  1387. X    # If no To: field, then maybe there is an Apparently-To: instead. If so,
  1388. X    # make them identical. Otherwise, assume the mail was directed to the user.
  1389. X    if (!$Header{'To'} && $Header{'Apparently-To'}) {
  1390. X        $Header{'To'} = $Header{'Apparently-To'};
  1391. X    }
  1392. X    unless ($Header{'To'}) {
  1393. X        do add_log("WARNING no To: field, assuming $cf'user") if $loglvl > 4;
  1394. X        $Header{'To'} = $cf'user;
  1395. X    }
  1396. X
  1397. X    # Set number of lines in body, unless there is already a Lines:
  1398. X    # header in which case we trust it. Same for Length.
  1399. X    $Header{'Lines'} = $lines unless defined($Header{'Lines'});
  1400. X    $Header{'Length'} = $length unless defined($Header{'Length'});
  1401. X
  1402. X    # If there is no Reply-To: line, then take the return path, if any.
  1403. X    # Otherwise use the address found in From.
  1404. X    if (!$Header{'Reply-To'}) {
  1405. X        local($tmp) = $Header{'Return-Path'};
  1406. X        $tmp =~ /<(.*)>/ && ($tmp = $1);        # Remove the <> in path
  1407. X        $Header{'Reply-To'} = $tmp if $tmp;
  1408. X        $Header{'Reply-To'} = (&parse_address($Header{'From'}))[0] unless $tmp;
  1409. X    }
  1410. X
  1411. X    # Unless there is already a sender line, fake one using From field
  1412. X    if (!$Header{'Sender'}) {
  1413. X        $Header{'Sender'} = $first_from;
  1414. X    }
  1415. X}
  1416. X
  1417. END_OF_FILE
  1418.   if test 5848 -ne `wc -c <'agent/pl/parse.pl'`; then
  1419.     echo shar: \"'agent/pl/parse.pl'\" unpacked with wrong size!
  1420.   fi
  1421.   # end of 'agent/pl/parse.pl'
  1422. fi
  1423. if test -f 'agent/test/cmd/split.t' -a "${1}" != "-c" ; then 
  1424.   echo shar: Will not clobber existing file \"'agent/test/cmd/split.t'\"
  1425. else
  1426.   echo shar: Extracting \"'agent/test/cmd/split.t'\" \(6055 characters\)
  1427.   sed "s/^X//" >'agent/test/cmd/split.t' <<'END_OF_FILE'
  1428. X# The SPLIT command
  1429. Xdo '../pl/cmd.pl';
  1430. X
  1431. X&add_header('X-Tag: digest #2');
  1432. X&make_digest;
  1433. X
  1434. X# First time, normal split: one (empty) header plus 3 digest items.
  1435. X# A single 'SPLIT here' is run
  1436. X&add_header('X-Tag: split #1', 'digest');
  1437. X`cp digest mail`;
  1438. X`$cmd`;
  1439. X$? == 0 || print "1\n";
  1440. X-f "$user" && print "2\n";            # Was not split in-place, but also saved
  1441. X-f 'here' || print "3\n";            # Where digest was split...
  1442. X&get_log(4, 'here');                # Slurp folder in @log
  1443. X&check_log('^X-Tag: digest #1', 5) == 2 || print "6\n";
  1444. X&check_log('^X-Tag: digest #2', 7) == 2 || print "8\n";
  1445. X&check_log('^X-Tag: digest #3', 9) == 2 || print "10\n";
  1446. X&check_log('^X-Tag: split #1', 11) == 2 || print "12\n";
  1447. X&check_log('^X-Filter-Note:', 13) == 2 || print "14\n";
  1448. Xunlink 'here';
  1449. X
  1450. X# Seconde time: a single 'SPLIT -id here' is run
  1451. X&replace_header('X-Tag: split #2', 'digest');
  1452. X`cp digest mail`;
  1453. X`$cmd`;
  1454. X$? == 0 || print "15\n";
  1455. X-f "$user" && print "16\n";            # Was not split in-place, but in folder
  1456. X-f 'here' || print "17\n";            # Where digest was split...
  1457. X&get_log(18, 'here');                # Slurp folder in @log
  1458. X&check_log('^X-Tag: digest #1', 19) == 1 || print "20\n";
  1459. X&check_log('^X-Tag: digest #2', 21) == 1 || print "22\n";
  1460. X&check_log('^X-Tag: digest #3', 23) == 1 || print "24\n";
  1461. X¬_log('^X-Tag: split #2', 25);    # Header was deleted by -d
  1462. X&check_log('^X-Filter-Note:', 26) == 2 || print "27\n";
  1463. X&check_log('^X-Digest-To:', 84) == 3 || print "85\n";
  1464. Xunlink 'here';
  1465. X
  1466. X# Third time: a single 'SPLIT -iew here' is run
  1467. X&replace_header('X-Tag: split #3', 'digest');
  1468. X`cp digest mail`;
  1469. X`$cmd`;
  1470. X$? == 0 || print "28\n";
  1471. X-f "$user" && print "29\n";            # Was not split in-place, but in folder
  1472. X-f 'here' || print "30\n";            # Where digest was split...
  1473. X&get_log(31, 'here');                # Slurp folder in @log
  1474. X&check_log('^X-Tag: digest #1', 32) == 1 || print "33\n";
  1475. X&check_log('^X-Tag: digest #2', 34) == 1 || print "35\n";
  1476. X&check_log('^X-Tag: digest #3', 36) == 1 || print "37\n";
  1477. X¬_log('^X-Tag: split #3', 38);    # Header was deleted by -e
  1478. X&check_log('^X-Filter-Note:', 39) == 3 || print "40\n";    # Trailing garbage...
  1479. X&check_log('anticonstitutionellement', 41) == 1 || print "42\n";
  1480. Xunlink 'here';
  1481. X
  1482. X# Fourth time: a single 'SPLIT -iew' is run. All the digest items will still
  1483. X# be saved in 'here' because they all bear a X-Tag: header. The trailing
  1484. X# garbage will not match anything and will be left in the mailbox.
  1485. X&replace_header('X-Tag: split #4', 'digest');
  1486. X`cp digest mail`;
  1487. X`$cmd`;
  1488. X$? == 0 || print "43\n";
  1489. X-f "$user" || print "44\n";            # That must be the trailing garbage
  1490. X-f 'here' || print "45\n";            # Where digest was split...
  1491. X&get_log(46, 'here');                # Slurp folder in @log
  1492. X&check_log('^X-Tag: digest #1', 47) == 1 || print "48\n";
  1493. X&check_log('^X-Tag: digest #2', 49) == 1 || print "50\n";
  1494. X&check_log('^X-Tag: digest #3', 51) == 1 || print "52\n";
  1495. X¬_log('^X-Tag: split #3', 53);    # Header was deleted by -e
  1496. X&check_log('^X-Filter-Note:', 54) == 2 || print "55\n";    # No trailing garbage...
  1497. X¬_log('anticonstitutionellement', 56);
  1498. X&get_log(57, "$user");
  1499. X&check_log('anticonstitutionellement', 58) == 1 || print "59\n";
  1500. X&check_log('^X-Filter-Note:', 60) == 1 || print "61\n";
  1501. Xunlink 'here', "$user";
  1502. X
  1503. X# Fifth time: a single 'SPLIT -iew here', but this time header is not empty...
  1504. X# Besides, there will be an empty message between encapsulation boundaries
  1505. X# and we want to make sure SPLIT deals correctly with it. Trailing garbage
  1506. X# is removed.
  1507. Xopen(MAIL, ">mail");
  1508. Xclose MAIL;
  1509. X&make_digest('Not empty digest header');
  1510. X`cp digest mail`;
  1511. X&add_header('X-Tag: split #5');
  1512. X`$cmd`;
  1513. X$? == 0 || print "62\n";
  1514. X-f 'here' || print "63\n";            # Where digest was split...
  1515. X&get_log(64, 'here');                # Slurp folder in @log
  1516. X&check_log('^X-Tag: digest #1', 65) == 1 || print "66\n";
  1517. X&check_log('^X-Tag: digest #3', 67) == 1 || print "68\n";
  1518. X¬_log('^X-Tag: digest #2', 69);    # Empty second message
  1519. X¬_log('Mailagent-Test-Suite', 70);    # No trailing garbage
  1520. X&check_log('^X-Filter-Note:', 71) == 2 || print "72\n";
  1521. X&check_log('^From ', 73) == 4 || print "74\n";    # One built up for last item
  1522. X&check_log('^Message-Id:', 75) == 1 || print "76\n";
  1523. X&check_log('^>From', 80) == 2 || print "81\n";
  1524. X&check_log('^From which', 82) == 1 || print "83\n";
  1525. X
  1526. X# Sixth time: mail is not in digest format.
  1527. X`cp ../mail .`;
  1528. X$? == 0 || print "77\n";        # Fool guard for myself
  1529. X&add_header('X-Tag: split #5');
  1530. X`$cmd`;
  1531. X$? == 0 || print "78\n";
  1532. X-f 'here' || print "79\n";        # Where mail was saved (not in digest format)
  1533. X
  1534. Xunlink 'mail', 'here', 'digest';
  1535. X# Last is 85
  1536. Xprint "0\n";
  1537. X
  1538. X# Build digest out of mail
  1539. Xsub make_digest {
  1540. X    local($msg) = @_;        # Optional, first line in header
  1541. X    &get_log(100, 'mail');    # Slurp mail in @log
  1542. X    open(DIGEST, ">digest");
  1543. X    print DIGEST <<EOH;
  1544. XReceived: from eiffel.eiffel.com by lyon.eiffel.com (5.61/1.34)
  1545. X    id AA25370; Fri, 10 Jul 92 23:48:30 -0700
  1546. XReceived: by eiffel.eiffel.com (4.0/SMI-4.0)
  1547. X    id AA27809; Fri, 10 Jul 92 23:45:14 PDT
  1548. XDate: Fri, 10 Jul 92 23:45:14 PDT
  1549. XFrom: root@eiffel.com (Postmaster)
  1550. XMessage-Id: <9207110645.AA27809@eiffel.eiffel.com>
  1551. XTo: postmaster@eiffel.com
  1552. XSubject: Mail Report - 10/07
  1553. X
  1554. X$msg
  1555. X----------------------------------------------
  1556. XFrom ram Sun Jul 12 18:20:27 PDT 1992
  1557. XFrom: ram
  1558. XSubject: Notice
  1559. XX-Tag: digest #1
  1560. X
  1561. XJust to tell you there was no digest header... unless $msg set
  1562. X
  1563. X----
  1564. X
  1565. XEOH
  1566. X    print DIGEST @log;
  1567. X    print DIGEST <<'EOM';
  1568. X----
  1569. XFrom: ram
  1570. XX-Tag: digest #3
  1571. X
  1572. XFrom line should be >escaped.
  1573. XAnother message with a really minimum set of header!!
  1574. XFrom which should NOT be
  1575. X
  1576. XFrom escaped again...
  1577. X----
  1578. X
  1579. XEOM
  1580. X    if ($msg eq '') {
  1581. X        print DIGEST <<'EOM';
  1582. XThis is trailing garbage. I will use the SPLIT command with the '-w'
  1583. Xoption and this will be saved is a separate mail with the subject
  1584. Xtaken from that of the whole digest, with the words (trailing garbage)
  1585. Xappended to it... This token, "anticonstitutionellement " will make
  1586. Xit obvious for grep -- it's the longest word in French, and it means
  1587. Xthe government is not doing its job, roughly speaking :-).
  1588. XEOM
  1589. X    } else {
  1590. X        print DIGEST <<'EOM';
  1591. XEnd of digest Mailagent-Test-Suite
  1592. X**********************************
  1593. XEOM
  1594. X    }
  1595. X    close DIGEST;
  1596. X}
  1597. X
  1598. END_OF_FILE
  1599.   if test 6055 -ne `wc -c <'agent/test/cmd/split.t'`; then
  1600.     echo shar: \"'agent/test/cmd/split.t'\" unpacked with wrong size!
  1601.   fi
  1602.   # end of 'agent/test/cmd/split.t'
  1603. fi
  1604. if test -f 'agent/test/option/c.t' -a "${1}" != "-c" ; then 
  1605.   echo shar: Will not clobber existing file \"'agent/test/option/c.t'\"
  1606. else
  1607.   echo shar: Extracting \"'agent/test/option/c.t'\" \(486 characters\)
  1608.   sed "s/^X//" >'agent/test/option/c.t' <<'END_OF_FILE'
  1609. X# -c : specify alternate configuration file
  1610. Xdo '../pl/init.pl';
  1611. X$output = `cat ../mail | $mailagent -c foo 2>&1`;
  1612. X$? != 0 || print "1\n";        # Cannot open config file
  1613. X$* = 1;
  1614. X$output =~ /^\*\*.*not processed/ || print "2\n";
  1615. Xchdir '../out';
  1616. X$user = $ENV{'USER'};
  1617. Xunlink "$user";
  1618. X`cp .mailagent alternate`;
  1619. X$output = `$mailagent -c alternate /dev/null 2>/dev/null`;
  1620. X$? == 0 || print "3\n";
  1621. X$output eq '' || print "4\n";
  1622. X-s "$user" || print "5\n";
  1623. Xunlink "$user", 'alternate';
  1624. Xprint "0\n";
  1625. END_OF_FILE
  1626.   if test 486 -ne `wc -c <'agent/test/option/c.t'`; then
  1627.     echo shar: \"'agent/test/option/c.t'\" unpacked with wrong size!
  1628.   fi
  1629.   # end of 'agent/test/option/c.t'
  1630. fi
  1631. echo shar: End of archive 12 \(of 17\).
  1632. cp /dev/null ark12isdone
  1633. MISSING=""
  1634. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ; do
  1635.     if test ! -f ark${I}isdone ; then
  1636.     MISSING="${MISSING} ${I}"
  1637.     fi
  1638. done
  1639. if test "${MISSING}" = "" ; then
  1640.     echo You have unpacked all 17 archives.
  1641.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1642. else
  1643.     echo You still must unpack the following archives:
  1644.     echo "        " ${MISSING}
  1645. fi
  1646. exit 0
  1647. exit 0 # Just in case...
  1648.