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