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

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