1<?xml version="1.0"?>
2<!DOCTYPE rfc SYSTEM "rfc2629.dtd">
3
4<?rfc compact="no"?>
5<?rfc toc="yes"?>
6<?rfc private="The README file"?>
7<?rfc header="README"?>
8
9<rfc>
10<front>
11<title>The personal.tcl Mailbot</title>
12
13<author initials="M.T." surname="Rose" fullname="Marshall T. Rose">
14<organization>Dover Beach Consulting, Inc.</organization>
15<address>
16<postal>
17<street>POB 255268</street>
18<city>Sacramento</city> <region>CA</region> <code>95865-5268</code>
19<country>US</country>
20</postal>
21<phone>+1 916 483 8878</phone>
22<facsimile>+1 916 483 8848</facsimile>
23<email>mrose@dbc.mtview.ca.us</email>
24</address>
25</author>
26
27<date month="February" year="2002" />
28
29<abstract><t>The personal.tcl mailbot implements a highly-specialized
30filter for personal messages.
31It MUST not be used by people who receive mailing list traffic in
32their personal mailboxes.</t></abstract>
33</front>
34
35<middle>
36<section title="SYNOPSIS">
37<figure>
38<preamble>Create a <xref target="configFile">configuration file</xref>
39and add this line to your ".forward" file:</preamble>
40<artwork><![CDATA[
41    "| LIB/mbot-1.1/personal.tcl -config FILE -user USER"
42]]></artwork>
43<postamble>where "LIB" is where the Tcl library lives,
44"FILE" is the name of your configuration file,
45and "USER" is your username.</postamble>
46</figure>
47
48<section title="Requirements">
49<t>This package requires:
50<list style="symbols">
51<t><eref target="http://sourceforge.net/projects/tcl/">Tcl version 8.3</eref>
52or later</t>
53
54<t><eref target="http://sourceforge.net/projects/tcllib/">tcl lib</eref></t>
55
56<t><eref target="http://sourceforge.net/projects/tclx/">TclX version 8.0</eref>
57or later</t>
58</list></t>
59</section>
60
61<section title="Copyrights">
62<t>(c) 1999-2002 Marshall T. Rose</t>
63
64<t>Hold harmless the author, and any lawful use is allowed.</t>
65</section>
66</section>
67
68<section title="PHILOSOPHY">
69<t>The mailbot's philosophy is simple:
70<list style="symbols">
71<t>The mailbot receives all of your incoming personal mail.</t>
72
73<t>You ALWAYS copy yourself on every message you send,
74so that the mailbot receives all of your outgoing personal mail.</t>
75
76<t>The mailbot performs six tasks, all optional:
77<list>
78<t>makes audit copies of your incoming and outgoing mail;</t>
79
80<t>performs duplicate supression;</t>
81
82<t>performs originator supression by rejecting messages from people
83who aren't your friends or on a guest list;</t>
84
85<t>performs content supression by rejecting messages that contain
86attachments with extensions on your prohibited list;</t>
87
88<t>sends a textual synopsis to your PDA; and,</t>
89
90<t>sends a copy to your remote mailbox.</t>
91</list></t>
92</list></t>
93
94<t>Do NOT use the personal.tcl mailbot if you receive mailing list
95traffic in your personal mailbox.
96When sending mail to a mailing list,
97either:
98<list style="symbols">
99<t>use a "From" address that the personal.tcl mailbot will process as
100"impersonal" mail,
101(e.g., "hewes+ietf.general@example.com"); or,</t>
102
103<t>set the "Reply-To" for the message to the mailing list.</t>
104</list>
105Consult <xref target="impersonal" /> for information on how
106"impersonal" mail is identified and processed.</t>
107
108<vspace blankLines="10000" />
109
110<section title="Guest Lists">
111<t>Guest lists are an effective mechanism for cutting back on
112excessive mail.
113<list style="symbols">
114<t>when the mailbot receives a message from you,
115it adds any recipients it finds to a permanent-guest list;</t>
116
117<t>when the mailbot receives a message from someone on a guest list,
118it adds any recipients it finds to a temporary-guest list; but,</t>
119
120<t>when the mailbot receives a message from someone not on any guest
121list,
122they get a rejection notice.</t>
123</list>
124Note that in order to promote someone to the permanent-guest list,
125you must send them a message (with a copy to yourself).
126In most cases,
127simply replying to the original message accomplishes this.
128Of course,
129if you don't want to promote someone to the permanent-guest list,
130simply remove that address (or your address) from the list of
131recipients in your reply.</t>
132
133<t>Here are the fine points:
134<list style="symbols">
135<t>rejection notices contain a passphrase that may be used at most
136once to bypass the guest list mechanism
137(notices also contain the original message to minimize type-in
138by the uninvited);</t>
139
140<t>a flip-flop is used to avoid mail loops; and,</t>
141
142<t>messages originated by an administrative address (e.g.,
143"Postmaster") bypass the guest list mechanism
144(unless the message refers to a previously-rejected message,
145in which case it is supressed).</t>
146</list></t>
147
148<t>The rejection notice should be written carefully to minimize an
149extreme negative reaction on the part of the uninvited.
150Of course,
151by allowing a passphrase,
152this provides something of a CQ test for the uninvited --
153if someone can't pass the test...</t>
154</section>
155</section>
156
157<section title="BEHAVIOR">
158<section title="Arguments">
159<t>The mailbot supports the following command line arguments:
160<list style="hanging">
161<t hangText="   -config configFile:">
162specifies the name of the configuration file to use;</t>
163
164<t hangText="   -debug boolean:">
165enables debug output;</t>
166
167<t hangText="   -file messageFile:">
168specifies the name of the file containing the message;</t>
169
170<t hangText="   -originator orginatorAddress:">
171specifies the email-address of the originator of the message; and,</t>
172
173<t hangText="   -user userName:">
174specifies the user-identity of the recipient.</t>
175</list>
176Note that if "-user" is given,
177then the working directory is set to userName's home directory before
178configFile is sourced,
179and the umask is set defensively.</t>
180
181<figure>
182<preamble>The default values are:</preamble>
183<artwork><![CDATA[
184    personal.tcl -config     .personal-config.tcl   \
185                 -debug      0                      \
186                 -file       -                      \
187                 -originator "derived from message"
188]]></artwork>
189<postamble>Given the default values,
190only "-user" need be specified.
191The reason is that if a message is being delivered to multiple local
192recipients,
193and if any of the ".forward" files are identical in content,
194then sendmail may not deliver the message to all of the local
195recipients.</postamble>
196</figure>
197
198<t>A few other (sendmail related) tips:
199<list style="symbols">
200<t>If sendmail is configured with smrsh,
201you'll need to symlink personal.tcl into the
202/usr/libexec/sm.bin/ directory.</t>
203
204<t>Make sure that tclsh8.0 is in the path specified on the third-line
205of personal.tcl.</t>
206
207<t>You should chmod your ".forward" file to 0600.</t>
208</list></t>
209</section>
210
211<vspace blankLines="10000" />
212
213<section anchor="actions" title="Actions">
214<t>The mailbot begins by parsing its arguments,
215sourcing configFile,
216and then examining the incoming message:
217<list style="numbers">
218<t>If <xref target="options.auditInFile">auditInFile</xref> is set,
219a copy of the message is 
220<xref target="procs.saveMessage">saved</xref> there.</t>
221
222<t>If the message contains a previously-encountered "Message-ID",
223processing terminates.</t>
224
225<t>If the message's originator can not be determined,
226a copy of the message is
227<xref target="procs.saveMessage">saved</xref> in the
228<xref target="options.defaultMaildrop">defaultMaildrop</xref> and
229processing terminates.</t>
230
231<t>The originator's email-address is examined:
232<list>
233<t>If the originator appears to be an
234<xref target="procs.adminP">automated administrative process</xref>,
235and if a previously rejected email-address is found in the message,
236processing terminates.</t>
237
238<t>Otherwise,
239if the originator isn't <xref target="procs.ownerP">the user</xref>,
240or <xref target="procs.friendP">a friend</xref>,
241or a permanent-access guest,
242or a temporary-access guest,
243and if <xref target="options.noticeFile">noticeFile</xref> is set,
244then the message is rejected.</t>
245
246<t>Otherwise,
247each recipient email-address in the message's header is added to a guest
248list.
249(If the originator is <xref target="procs.ownerP">the user</xref>,
250the permanent-guest list is used instead of the temporary-guest
251list.)</t>
252</list></t>
253
254<t>If the originator is the <xref target="procs.ownerP">the user</xref>,
255then:
256<list>
257<t>If <xref target="options.auditOutFile">auditOutFile</xref> is set,
258<xref target="procs.saveMessage">saved</xref> there.</t>
259
260<t>Regardless, processing terminates.</t>
261</list></t>
262
263<t>If <xref target="options.pdaMailboxes">pdaMailboxes</xref> is set,
264and if any plaintext is contained in the message,
265then the plaintext is sent to those email-addresses.</t>
266
267<t>If <xref target="options.remoteMailboxes">remoteMailboxes</xref> is set,
268and if the message is successful resent to those email-addresses,
269then processing terminates.</t>
270
271<t>A copy of the message is
272<xref target="procs.saveMessage">saved</xref> in the
273<xref target="options.defaultMaildrop">defaultMaildrop</xref> and
274processing terminates.</t>
275</list></t>
276</section>
277
278<section anchor="configFile" title="The Configuration File">
279<t>There are two kinds of information that may be defined in configFile:
280<xref target="options">configuration options</xref> and
281<xref target="procs">configurable procedures</xref>.</t>
282
283<figure>
284<preamble>Here's a simple example of a configFile for a user named
285"example":</preamble>
286<artwork><![CDATA[
287    set options(dataDirectory)   .personal
288    set options(defaultMaildrop) /var/mail/example
289    set options(logFile)         [file join .personal personal.log]
290    set options(noticeFile)      [file join .personal notice.txt]
291]]></artwork>
292</figure>
293
294<section anchor="options" title="Configuration Options">
295<t>configFile must define 
296<xref target="options.dataDirectory">dataDirectory</xref>
297and
298<xref target="options.defaultMaildrop">defaultMaildrop</xref>.
299All other configuration options are optional.</t>
300
301<section anchor="options.dataDirectory" title="dataDirectory">
302<t>The directory where the mailbot keeps its databases.
303The subdirectories are:
304<list style="hanging">
305<t hangText="   badaddrs:">the directory of rejected email-addresses</t>
306
307<t hangText="   inaddrs:">the directory of originator email-addresses</t>
308
309<t hangText="   msgids:">the directory of Message-IDs</t>
310
311<t hangText="   outaddrs:">the permanent-guest list</t>
312
313<t hangText="   phrases:">the directory of at-most-once passphrases</t>
314
315<t hangText="   tmpaddrs:">the temporary-guest list</t>
316</list>
317If you want to remove someone from a guest list,
318simply go to that directory and delete the corresponding file.</t>
319</section>
320
321<section anchor="options.defaultMaildrop" title="defaultMaildrop">
322<t>The filename where messages are 
323<xref target="procs.saveMessage">saved</xref> for later viewing by
324your user agent.</t>
325</section>
326
327<section anchor="options.auditInFile" title="auditInFile">
328<t>The filename where messages are
329<xref target="procs.saveMessage">saved</xref> for audit purposes.</t>
330</section>
331
332<section anchor="options.auditOutFile" title="auditOutFile">
333<t>The filename where your outgoing messages are
334<xref target="procs.saveMessage">saved</xref> for audit purposes.</t>
335</section>
336
337<section anchor="options.dropNames" title="dropNames">
338<t>A list of filename extensions for attachments that automatically
339cause the message to be rejected.</t>
340</section>
341
342<section anchor="options.friendlyDomains" title="friendlyDomains">
343<t>A list used by <xref target="procs.friendP">friendP</xref> giving
344the domain names where your friends live.</t>
345</section>
346
347<section anchor="options.friendlyfire" title="friendlyfire">
348<t>If present and true,
349then someone sending a message both to you and someone you've
350previously sent mail to,
351is considered a friend.</t>
352</section>
353
354<section anchor="options.logFile" title="logFile">
355<t>The filename where the mailbot
356<xref target="procs.tclLog">logs</xref> its actions.</t>
357</section>
358
359<section anchor="options.myMailbox" title="myMailbox">
360<figure>
361<preamble>Your preferred email-address with commentary text, e.g.,</preamble>
362<artwork><![CDATA[
363    Arlington Hewes <hewes@example.com>
364]]></artwork>
365</figure>
366</section>
367
368<section anchor="options.noticeFile" title="noticeFile">
369<t>The filename containing the textual notice sent when a message is
370rejected.
371Note that all occurrances of "%passPhrase%" within this file are
372replaced with an at-most-once passphrase allowing the originator to
373bypass the mailbot's filtering.
374Similarly,
375any occurrences of "%subject%" are replaced by the "Subject" of the
376incoming message.</t>
377</section>
378
379<section anchor="options.pdaMailboxes" title="pdaMailboxes">
380<t>The email-addresses where a textual synopsis of the incoming message is
381sent.</t> 
382</section>
383
384<section anchor="options.remoteMailboxes" title="remoteMailboxes">
385<t>The email-addresses where a copy of the incoming message is resent.</t> 
386</section>
387</section>
388
389<vspace blankLines="10000" />
390
391<section anchor="procs" title="Configurable Procedures">
392<t>All of these procedures are defined in personal.tcl.
393You may override any of them in configFile.</t>
394
395<section anchor="procs.adminP" title="adminP">
396<figure>
397<artwork><![CDATA[
398    proc adminP {local domain}
399]]></artwork>
400</figure>
401
402<t>Returns "1" if the email-address is an automated administrative
403process.</t>
404</section>
405
406<section anchor="procs.friendP" title="friendP">
407<figure>
408<artwork><![CDATA[
409    proc friendP {local domain}
410]]></artwork>
411</figure>
412
413<t>Returns "1" if the email-address is from a
414<xref target="options.friendlyDomains">friendly domain</xref> or
415sub-domain.</t>
416</section>
417
418<section anchor="procs.ownerP" title="ownerP">
419<figure>
420<artwork><![CDATA[
421    proc ownerP {local domain}
422]]></artwork>
423</figure>
424
425<t>Returns "1" if the email-address refers to the user
426(as determined by looking at
427<xref target="options.myMailbox">myMailbox</xref>,
428<xref target="options.pdaMailboxes">pdaMailboxes</xref>, and
429<xref target="options.remoteMailboxes">remoteMailboxes</xref>.</t>
430</section>
431
432<section anchor="procs.saveMessage" title="saveMessage">
433<figure>
434<artwork><![CDATA[
435    proc saveMessage {inF {outF ""}}
436]]></artwork>
437</figure>
438
439<t>Saves a copy of the message contained in the file inF.
440If the destination file,
441outF,
442isn't specified,
443it defaults to the
444<xref target="options.defaultMaildrop">defaultMaildrop</xref>.</t> 
445</section>
446
447<section anchor="procs.findPhrase" title="findPhrase">
448<figure>
449<artwork><![CDATA[
450    proc findPhrase {subject}
451]]></artwork>
452</figure>
453
454<t>Returns "1" if a previously-allocated passphrase is present in the
455subject.
456If so,
457the passphrase is forgotten.</t>
458</section>
459
460<section anchor="procs.makePhrase" title="makePhrase">
461<figure>
462<artwork><![CDATA[
463    proc makePhrase {}
464]]></artwork>
465</figure>
466
467<t>Returns an at-most-once passphrase for use with a rejection notice.</t>
468</section>
469
470<section anchor="procs.pruneDir" title="pruneDir">
471<figure>
472<artwork><![CDATA[
473    proc pruneDir {dir type}
474]]></artwork>
475</figure>
476
477<t>Removes old entries from one of the mailbot's 
478<xref target="options.dataDirectory">databases</xref>.
479The second parameter is one of "addr", "msgid", or "phrase".</t>
480</section>
481
482<section anchor="procs.tclLog" title="tclLog">
483<figure>
484<artwork><![CDATA[
485    proc tclLog {message}
486]]></artwork>
487</figure>
488
489<t>Writes a message to the <xref target="options.logFile">logFile</xref>.</t>
490</section>
491</section>
492</section>
493
494</section>
495
496</middle>
497
498<back>
499<references />
500
501<section anchor="impersonal" title="Impersonal Mail">
502<t>If <xref target="procs.impersonalMail">impersonalMail</xref>
503returns a non-empty string
504then the message is processed differently than the algorithm given in
505<xref target="actions" />.
506Specifically:
507<list style="numbers">
508<t>If the message contains a previously-encountered "Message-ID",
509processing terminates.</t>
510
511<t>If the message's originator can not be determined,
512processing terminates.</t>
513
514<t>The value returned by
515<xref target="procs.impersonalMail">impersonalMail</xref>
516is the folder's name and is broken into one or more components
517seperated by dots (".").
518If there aren't at least two components,
519or if any of the components are empty
520(e.g., the folder is named "sys..announce"),
521then the message is bounced.</t>
522
523<t>If <xref target="options.mappingFile">mappingFile</xref> exists,
524that file is examined to see if an entry is present for the folder.
525If so,
526the message is processed according to the value present,
527one of:
528<list style="hanging">
529<t hangText='     "ignore":'>the message is silently ignored;</t>
530
531<t hangText='     "bounce":'>the message is noisily bounced; or,</t>
532
533<t hangText="    otherwise:">the message is resent to the address.</t>
534</list>
535Regardless,
536if an entry was present for the folder,
537then processing terminates.</t>
538
539<t>The message is <xref target="procs.saveMessage">saved</xref> 
540in a file whose name is constructed by replacing each dot (".") in the
541folder name with a directory seperator
542(e.g., if the folder is named "sys.announce",
543then the file is called "announce" underneath the directory "sys"
544underneath the directory identified by
545<xref target="options.foldersDirectory">foldersDirectory</xref>.</t>
546
547<t>Finally,
548the file identified by <xref target="options.foldersFile">foldersFile</xref>
549is updated as necessary.</t>
550</list></t>
551
552<vspace blankLines="10000" />
553
554<section anchor="impersonal.options" title="Configuration Options">
555<t>If "impersonal" mail is received,
556then <xref target="options.foldersFile">foldersFile</xref> and
557<xref target="options.foldersDirectory">foldersDirectory</xref> 
558must exist.</t>
559
560<section anchor="options.foldersDirectory" title="foldersDirectory">
561<t>The directory where the mailbot keeps private folders.</t>
562</section>
563
564<section anchor="options.foldersFile" title="foldersFile">
565<t>This file contains one line for each private folder.</t>
566</section>
567
568<section anchor="options.announceMailboxes" title="announceMailboxes">
569<t>The email-addresses where an announcement is sent when a new
570private folder is created.</t>
571</section>
572
573<section anchor="options.mappingFile" title="mappingFile">
574<t>The file consulted by the mailbot to determine how to process
575"impersonal" messages.
576Each line of the file consists of a folder name and value,
577seperated by a colon (":").
578There are three reserved values: "bounce", "ignore", and "store".</t>
579</section>
580</section>
581
582<vspace blankLines="10000" />
583
584<section anchor="impersonal.procs" title="Configurable Procedures">
585<t>All of these procedures are defined in personal.tcl.
586You may override any of them in configFile.</t>
587
588<section anchor="procs.impersonalMail" title="impersonalMail">
589<figure>
590<artwork><![CDATA[
591    proc impersonalMail {}
592]]></artwork>
593</figure>
594
595<t>If the message is deemed "impersonal",
596return the name of a corresponding private folder;
597otherwise,
598return the empty-string.</t>
599
600<t>Many mail systems have a mechanism of passing additional
601information when performing final delivery using a program.
602With modern versions of sendmail,
603for example,
604if mail is sent to a local user named "user+detail",
605then,
606in the absense of an alias for either "user+detail" or "user+*",
607then the message is delivered to "user".
608The trick is to get sendmail to pass the "detail" part to the mailbot.</t>
609
610<figure>
611<preamble>At present,
612sendmail passes this information only if procmail is your local
613mailer.
614Here's how I do it:</preamble>
615<artwork><![CDATA[
616    *** _alias.c    Tue Dec 29 10:42:25 1998
617    --- alias.c     Sat Sep 18 21:51:35 1999
618    ***************
619    *** 813,818 ****
620    --- 813,821 ----
621            define('z', user->q_home, e);
622            define('u', user->q_user, e);
623            define('h', user->q_host, e);
624    + 
625    +       setuserenv("SUFFIX", user->q_host);
626    + 
627            if (ForwardPath == NULL)
628                    ForwardPath = newstr("\201z/.forward");
629]]></artwork>
630<postamble>This makes available an environment variable called
631"SUFFIX" which has the "details" part.
632The drawback in this approach is that this information is lost if the
633message is re-queued for delivery
634(what's really needed is an addition to the .forward syntax to allow
635macros such as $h to be passed).</postamble>
636</figure>
637
638<figure>
639<preamble>The corresponding impersonalMail procedure is defined as:</preamble>
640<artwork><![CDATA[
641    proc impersonalMail {} {
642        global env
643
644        return $env(SUFFIX)
645    }
646]]></artwork>
647</figure>
648</section>
649
650<section anchor="procs.processFolder" title="processFolder">
651<figure>
652<artwork><![CDATA[
653    proc processFolder {folderName mimeT} { return $string }
654]]></artwork>
655</figure>
656
657<t>If an entry for the folder exists in the
658<xref target="options.mappingFile">mappingFile</xref>,
659and if the value for that entry is "process",
660then this procedure is invoked to return a string indicating what
661action to take
662(cf., <xref target="impersonal" />).</t>
663</section>
664</section>
665</section>
666
667<section title="An Example configFile">
668<figure>
669<preamble>Here is the ".forward" file for the user "hewes":</preamble>
670<artwork><![CDATA[
671    "|/usr/pkg/lib/mbot-1.1/personal.tcl 
672         -config .personal/config.tcl -user hewes"
673]]></artwork>
674<postamble>(Of course, it's all on one line.)</postamble>
675</figure>
676
677<figure>
678<preamble>Here is the user's ".personal/config.tcl" file:</preamble>
679<artwork><![CDATA[
680    array set options [list                                          \
681        dataDirectory     .personal                                  \
682        defaultMaildrop   /var/mail/hewes                            \
683        auditInFile       [file join .personal INCOMING]             \
684        auditOutFile      [file join .personal OUTGOING]             \
685        friendlyDomains   [list tcp.int example.com]                 \
686        logFile           [file join .personal personal.log]         \
687        myMailbox         "Arlington Hewes <hewes@example.com>"      \
688        pdaMailboxes      hewes.pager@example.com                    \
689        noticeFile        [file join .personal notice.txt]           \
690        foldersDirectory  [file join .personal folders]              \
691        foldersFile       [file join .personal .mailboxlist]         \
692        announceMailboxes hewes+sys.announce@example.com             \
693        mappingFile       [file join .personal mapping]              \
694        friendlyFire      1                                          \
695        dropNames         [list *.bat *.exe *.src *.pif *.wav *.vbs] \
696    ]
697
698    proc impersonalMail {} {
699        global env
700
701        return $env(SUFFIX)
702    }
703]]></artwork>
704<postamble>Note that because
705<xref target="options.remoteMailboxes">remoteMailboxes</xref> isn't
706defined,
707personal messages are ultimately stored in the user's
708<xref target="options.defaultMaildrop">defaultMaildrop</xref>.</postamble>
709</figure>
710</section>
711
712<section title="Acknowledgements">
713<t>The original version of this mailbot was written by the author in 1994,
714implemented using  the safe-tcl package
715(Borenstein and Rose, circa 1993).</t>
716</section>
717
718</back>
719
720</rfc>
721