1 ;;; tinymail.el --- Mail add-ons. Report incoming mail, passwd, BBDB complete.
3 ;; This file is not part of Emacs
7 ;; Copyright (C) 1996-2007 Jari Aalto
10 ;; Maintainer: Jari Aalto
12 ;; To get information on this program run M-x tinymail-version
13 ;; Look at the code with folding.el
17 ;; This program is free software; you can redistribute it and/or modify it
18 ;; under the terms of the GNU General Public License as published by the Free
19 ;; Software Foundation; either version 2 of the License, or (at your option)
22 ;; This program is distributed in the hope that it will be useful, but
23 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with program; see the file COPYING. If not, write to the
29 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 ;; Boston, MA 02110-1301, USA.
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
37 ;; ....................................................... &t-install ...
38 ;; Put this file on your Emacs-Lisp load path, add following into your
39 ;; ~/.emacs startup file:
41 ;; (require 'tinymail-install)
42 ;; (require 'tinymail-install-extras) ;; optional
44 ;; Other setting you may wish to add:
46 ;; ;; Activate nice citation
47 ;; (add-hook 'tinymail-:load-hook 'tinymail-install-citation)
49 ;; ;; If you want nice TAB to indent for your messages,
50 ;; ;; add this. You TAB advances 4 spaces in the body of message.
51 ;; (autoload 'turn-on-tinytab-mode "tinytab" "" t)
52 ;; (add-hook 'tinymail-:mode-hook 'turn-on-tinytab-mode)
54 ;; ;; If you use NIS, use "ypcat passwd"
55 ;; (setq tinymail-:password-cat-cmd "cat /etc/passwd")
57 ;; ;; Protect plain text email addresses in the body
58 ;; (add-hook 'mail-send-hook 'tinymail-buffer-email-address-scramble)
59 ;; (add-hook 'message-send-hook 'tinymail-buffer-email-address-scramble)
61 ;; If you have any questions, use 'submit' function. In case of error
62 ;; or misbehavior, turn on the debug and send the debug result and
63 ;; describe what you did and what went wrong.
65 ;; .. do what you did in mail buffer ..
66 ;; M-x tinymail-debug-toggle Make sure debug is on
67 ;; C-u M-x tinymail-process-1 Run this if you got error signall
68 ;; M-x tinymail-submit-bug-report And compose bug report
70 ;; To read the documentation, run
72 ;; M-x load-library RET tinymail RET
73 ;; M-x tinymail-version [Add C-u, shows version only]
78 ;; ..................................................... &t-commentary ...
82 ;; Overview of features
84 ;; o Generate sendmail PLUS address: login@domain (Mr Foo+info)
85 ;; Works like real sendmail PLUS addressing:
86 ;; login+info@domain (Mr Foo)
87 ;; o Generate anti-ube addresses to prevent UBE/Spam from arriving
89 ;; o Changes Fcc dynamically according to header content.
90 ;; o Very easy TAB completion: two modes, alias and definition string.
91 ;; Also completes password file entries if your .mailrc doesn't
93 ;; o Easy interface for completing any field with TAB. E.g. complete
94 ;; `Followup-To:', `Gcc', `Newsgroups' and any user defined fields,
95 ;; like `Class' or `Priority'.
96 ;; o Fcc/Gcc folder can have compression extension .gz or .Z
97 ;; o When you reply, tour address is removed from CC to prevent
99 ;; o `mail-mode], Gnus `message-mode' and VM compatible.
100 ;; o MIME support: turns on Multi part sending if buffer size is
105 ;; Search BBDB for partial matches when you complete *To* and *Cc*
106 ;; fields in header. E.g. if you remember person's address, "site" or
107 ;; something, hit just TAB and all found BBDB's `net' field completions
110 ;; Notice that you have to _manually_ add full
111 ;; user name, phone number, whatever to the Net Field on order to
112 ;; complete to those items. The default `:' command adds only
116 ;; net: abc@example.com
118 ;; An in order to make that useful for completion purposes, you need to
119 ;; modify the `net' field with `C-o'
122 ;; net: Foo Bar - Head of Skyscraper inc. <abc@example.com>
124 ;; Now you can complete to any word found in the `net' line.
125 ;; If you want case sensitive completions, set this:
127 ;; (setq tinymail-:complete-bbdb-case-fold-search nil)
131 ;; This package installs itself to `mail-setup-hook' and you should
132 ;; know why if you try to get the package running for some other
133 ;; mail agent than Emacs mail, RMAIL, Gnus and VM where this package
136 ;; The `mail-setup-hook' is called *after* the basic headers, like
137 ;; `To' and `Subject', which are already in the buffer. Function
138 ;; `tinymail-mail' needs to read the contents of `To' in order to
139 ;; determine how it starts. It puts 1 or 2 spaces at the beginning of
140 ;; `To' field at the initial start, so that the packages `Cc' control
141 ;; is started correctly. When you use simple mail, `C-x' `m', the auto
142 ;; Cc feature addds 1 space (on) and when you hit reply, the auto Cc
143 ;; feature adds 2 spaces (off).
144 ;; So, if your mail agent doesn't call `mail-setup-hook', find similar
145 ;; hook that runs after the headers are in the buffer and install
146 ;; to that hook `tinymail-mail'.
148 ;; Completion: Guess Completion feature
150 ;; There are two basic completion modes: 'alias and 'string, which is
151 ;; selected via `tinymail-:complete-mode'. They refer to your
152 ;; ~/.mailrc definitions: When you hit the completions key (TAB in
153 ;; headers) the current word is picked at point and searched from
154 ;; either of these two definition lists.
156 ;; alias test "Mister Foo, Skyscraper Doing co. <foo@company.com>"
158 ;; alias mode string match mode
160 ;; The caces 1-4 below present words that you can type into the `To' field
161 ;; before you hit the completion key, TAB.
168 ;; It doesn't matter what you type initially; it can be anything you
169 ;; remember from the person's definition string. TheTAB calls function
170 ;; `tinymail-complete-guess' and any of those lines, 1-4, will be
173 ;; To: Mister Foo, Skyscraper Doing co. <foo@doing.com>
175 ;; If there are more than one match, a completion list is displayed.
177 ;; Completion and BBDB integration
179 ;; The completion is integrated to BBDB, but you have to have
180 ;; BBDB present with appropriate (require 'bbdb). The fields
181 ;; in NET and NAME are searched by default, but you can make the
182 ;; completion feature to try ANY-FIELD if you change the value
183 ;; of `tinymail-:complete-bbdb-fuzzy-method'. See variable documentation
184 ;; for complete description.
186 ;; Accepting the found match from .mailrc
188 ;; TinyMail supports running several completion functions so that
189 ;; the right match is inserted into the buffer. In order to
190 ;; discard the found match from .mailrc file, you can set a trigger
191 ;; to `tinymail-:confirm-mailrc-regexp'. Suppose, you want to confirm
192 ;; completion whenever you are sending mail to your colleagues that
193 ;; work in "disney.com". You'd set:
195 ;; (setq tinymail-:confirm-mailrc-regexp "disney.com")
197 ;; And if the match was picked from mailrc, you have a chance to
198 ;; reject the string and move on with other completion functions.
202 ;; When you hit tab here, a string "info@disneyword.com" was found from
203 ;; the mailrc, but may not be what you want to insert. Because
204 ;; you had set `tinymail-:confirm-mailrc-regexp', you get confirmation:
206 ;; TinyMail: Use? info@disneyword.com
208 ;; Where you can answer "n". The completion is canceled and all
209 ;; other completion function have a chance to find more
210 ;; suitable choice. (See Shared Shared Tab key explanation later)
212 ;; Completion: Password table
214 ;; In addition to .mailrc completion, there is support for completing
215 ;; entries found from *passwd* file. If the guess complete above fails
216 ;; the password file is examined if the mode is turned on. See
219 ;; tinymail-:password-mode
221 ;; Which is set to t by default. When you complete password entries
222 ;; for the first time, building all necessary variables will take some
223 ;; time. After the password file completions have been parsed, the
224 ;; content is written to cache file
226 ;; tinymail-:password-file
228 ;; Next time you need password completions, if this file exists,
229 ;; it will be read instead of heavy /etc/passwd file parsing. If you
230 ;; want to force reading the /etc/passwd again, just delete
231 ;; `tinymail-:password-file' and it will be recreated next time
232 ;; password completion is used.
234 ;; Completion: Custom completion of any header
236 ;; You can complete any field by setting variable
237 ;; `tinymail-:table-header-complete' For example to complete "Class" header,
238 ;; you would set the variable like this. See variable documentation
239 ;; for more information.
243 ;; 'tinymail-:table-header-complete
244 ;; "Class" ;; Add new header for use with TAB
246 ;; '("confidential" ;; completion list
248 ;; "for internal use only"
249 ;; "personal private"
250 ;; "personal another")))
255 ;; You can delete any elements from `Cc' field if you set variable
256 ;; `tinymail-:cc-kill-regexp'. This feature can be used to delete
257 ;; e.g. your email addresses from the list of `Cc' recipients to
258 ;; to avoid getting duplicate copies of the mail when you reply.
260 ;; At any time you can add two spaces in front of `Cc' field to
261 ;; disable this "kill" feature. This is desirable if you WANT to
262 ;; add a CC to your other email addresses. An example:
264 ;; (setq tinymail-:cc-kill-regexp \"me@here.at\")
266 ;; To: some@example.com
267 ;; CC: me@here.at << will be removed
268 ;; CC: me@here.at << NOT removed, because field has two spaces.
270 ;; RMAIL Fcc field tracking
272 ;; By default the `Fcc' is not added in your mail message, thus this
273 ;; package's automatic Fcc tracking isn't activated. Add following entry
274 ;; into your $HOME/.emacs to record your outgoing mail messages
276 ;; (setq mail-archive-file-name "~/.RMAIL.out")
278 ;; When Emacs sees that you have set this, it adds the Fcc field to
279 ;; your mail message. Alternatively you can press keys
281 ;; C-c C-f C-f ;; mail-fcc
283 ;; in *mail* buffer and it asks you to insert the Fcc field. Only
284 ;; now, when the `Fcc' is in the message, the automatic Fcc handling
285 ;; starts snooping around your headers and changing it if it finds a
286 ;; match from variable
288 ;; tinymail-:table-fcc
290 ;; If you want to disable Fcc changing (and edit it by hand),
291 ;; put two spaces at front of the Fcc. like this:
293 ;; FCC: ~/.RMAIL.secondary
296 ;; Fcc and saving outgoing copy in compressed format
298 ;; If you have an count that has quota limits, you want to save space
299 ;; as much as possible. You can save your outgoing mail copy in
300 ;; compressed format if you prepend the filename with ".gz" or
301 ;; ".Z". TinyMail will automatically load jka-compr if it sees any of
302 ;; those extensions. The fcc folder definition looks like this.
304 ;; (defconst tinymail-:table-fcc
306 ;; (list "elisp-archive" " ~/.mail.elisp-post.gz")
307 ;; (list "bug-gnu" " ~/.mail.bug.gz")
308 ;; (list "." " ~/.mail.out.gz"))) ;; general
310 ;; You _have_ to add (require 'jka-compr) is you want to use compresses
313 ;; ;; first one is defined in paths.el
314 ;; (setq rmail-file-name "~/RMAIL.gz")
315 ;; (setq mail-archive-file-name "~/.RMAIL.out.gz")
317 ;; Gnus Gcc archiving
319 ;; `Gcc' feature is similar to Fcc, but the Gcc is special to Gnus.
320 ;; All instruction you read above for Fcc are same for
321 ;; Gcc tracking feature. The table you have to configure is
323 ;; tinymail-:table-gcc
325 ;; Before you start defining Gnus folders, you must create them from
326 ;; Gnus *Group* buffer with command `G' `m'. E.g. you may have
327 ;; created following Gnus folders for newsgroup posting
329 ;; nnfolder+archive:post-pgp
330 ;; nnfolder+archive:post-emacs
331 ;; nnfolder+archive:post-gen
333 ;; The code below sets the `Gcc' folder only once when you start
334 ;; composing message, probably a followup and there is a `Newsgroups'
335 ;; header in the buffer. But if you hit `R' or `r' to reply directly to
336 ;; person (or use message-mode for mailing), there is `To' header in
337 ;; the buffer. Only now this package changes the Gcc field according to `To'
338 ;; field contents. The code below is for Newsgroup posting.
340 ;; (setq gnus-message-archive-group 'my-gnus-archive)
342 ;; (defun my-gnus-archive (group)
343 ;; "Archive outgoing mail to right group: Create the group by G m"
346 ;; (or (stringp group) ;No accidents...
349 ;; ((string-match "pgp\\|anon\\|privacy" group)
350 ;; "nnfolder+archive:post-pgp")
351 ;; ((string-match "emacs\\|gnu" group)
352 ;; "nnfolder+archive:post-emacs")
354 ;; "nnfolder+archive:post-gen"))))
356 ;; Feature: Sending message to mailing list
358 ;; In Gnus you may have defined mailing lists like this
360 ;; list.linux-announce
364 ;; And your personal work and mail groups with
373 ;; Daemon messages to junk.daemon, Spam to junk.spam and so on.
374 ;; Now suppose you are reading group `list.xxx' and you hit `f'
375 ;; to send followup to an article. Your composed message looks like this:
377 ;; To: answer-to-person <foo@bar.com>
378 ;; Cc: <someone@list.com>, <list-foo@bar.com>
380 ;; The Message goes to two people in the list and gets CC'd to
381 ;; list. Not what you want. You want simple:
383 ;; To: <list-foo@bar.com>
385 ;; And this is what this package does for you. All you need to do it to
386 ;; make sure the current group has Group parameter `to-list'.
387 ;; defined. You add one with `G' `p] From *Group* and typing
389 ;; ((to-list . "The List FOO <list-foo@bar.com>"))
391 ;; This feature is controlled by `tinymail-:feature-hook' which contains
392 ;; function `tinymail-mail-send-to-list'. If you remove the function from
393 ;; the hook, this feature is disabled
395 ;; Feature: Reporting incoming mail in local mail spool
397 ;; Function to control mail reporting:
399 ;; turn-on-tinymail-report-mail
400 ;; turn-off-tinymail-report-mail
402 ;; When you load this package the Report Mail feature is activated.
403 ;; If you're running windowed Emacs, the X-drag bar (top of the frame)
404 ;; is used to display the last incoming mail and count of pending
405 ;; unread mail. Here the last message was from Mr. foo and the
406 ;; pending mail count in spool is six.
408 ;; "foo@bar.com 6" ;; See variable `tinymail-:report-format-string'
410 ;; In non-windowed Emacs this same information is displayed in echo
411 ;; are instead. If you would like to have it always displayed in
412 ;; echo area, even in X environment, then set variable
413 ;; `tinymail-:report-window-system' to nil before loading this package.
415 ;; If you would like see more information about the arrived mail, you
416 ;; can adjust `tinymail-:report-spool-buffer-control' e.g. to keep
417 ;; permanent record of incoming mail. Value 'keep says that the report
418 ;; mail buffer is kept when mail is queried, so you can glance it from
419 ;; time to to for full information about arrived messages.
421 ;; If the only feature you want is the mail reporting functionality,
422 ;; you can activate it and disable all other settings with:
424 ;; ;; Don't activate tinymail-mode
425 ;; (setq tinymail-:enter-mail-hook-list nil)
426 ;; (require 'tinymail)
428 ;; Setting up report mail notify program
430 ;; The `tinymail-:report-mail-notify-program' fetches the Berkeley Mailbox
431 ;; formatted information from mailboxes. The default program used
432 ;; is `from(1)', but in case you don't have it, a equivalent command
433 ;; "grep '^From ' $MAIL" is used. See also `frm(1)'
434 ;; and `nfrm(1)' `newmail(1)' and `mailfrom(1)' if you can
435 ;; find those in your system.
437 ;; If you use Gnus and separate spool files, like you would do with
438 ;; Procmail, then you need to gather mail arrival information from all
439 ;; the spool files. Let's suppose you don't want to get notified on
440 ;; mailing list messages, but only messages saved to your private and
443 ;; ~/Mail/spool or `nnmail-procmail-directory'
456 ;; In that case you have to install custom mail notify program. A
457 ;; simple multiple mailbox grep will work here. Note, we also grep
460 ;; (setq tinymail-:report-mail-notify-program
462 ;; "grep '^From ' %s %s %s "
463 ;; (or (getenv "MAIL") (error "No $MAIL defined"))
464 ;; (concat (expand-file-name "~/Mail/spool/") "mail.*")
465 ;; (concat (expand-file-name "~/Mail/spool/") "work.*")))
467 ;; Take a look at variable `tinymail-:report-spool-buffer-control'
468 ;; which has default value 'keep where the
469 ;; `tinymail-:report-mail-notify-program' results are gathered. You
470 ;; may find it useful to keep the `tinymail-:report-spool-buffer'
471 ;; *tinymail-mail-spool* visible in some frame to act like `biff(1)'.
472 ;; From there you can find more detailed information of incoming
473 ;; message queue, than the simple message count in echo-area or x-drag
476 ;; _Note_: XEmacs has package `reportmail.el'. In case that package
477 ;; is loaded, the report mail feature here is not installed.
479 ;; Feature: Saving unused mail buffers on Emacs exit
481 ;; This file installs one function to `kill-emacs-hook' that loops
482 ;; through all mail buffers and appends the buffer content to
484 ;; tinymail-:dead-mail-file
486 ;; If you had some unfinished messages that you didn't yet send, you
487 ;; can restore the copy from this file when you restart emacs again.
488 ;; In Gnus `message-mode', you can use following to trash sent mail:
490 ;; (setq message-kill-buffer-on-exit t)
492 ;; If you don't want to use this feature, add following code to your
495 ;; (add-hook 'tinymail-:load-hook 'my-tinymail-:load-hook)
496 ;; (defun my-tinymail-:load-hook ()
497 ;; (remove-hook 'kill-emacs-hook 'tinymail-save-dead-mail))
499 ;; _Note_: VM and Gnus can keep the sent mail buffer around. This
500 ;; package won't install `tinymail-save-dead-mail-maybe' the dead mail
504 ;; Feature: anti-ube email addresses
508 ;; Changing the email address so that is is not pointing to your
509 ;; natural address is usually referred as "address munging". There are
510 ;; two schools that take firm position to express their views in this
511 ;; matter. Those who say that it is "plain wrong to munge address" and
512 ;; those who say "RFC does not require you to use REAL, returnable,
513 ;; address". It can be argued that the email address is property of
514 ;; an individual who can take measures to protect himself from getting
515 ;; into the email harvester's "2 billion email address on a CD for
518 ;; Here is an opinion whether it is right to munge the
519 ;; address according to RFC by Marty Fouts
520 ;; 1997-11-05 in newsgroup gnus.emacs.gnus:
522 ;; o The real implementation of news software doesn't care if the from
523 ;; field is munged or not
524 ;; o No RFC forces the address of the poster to be a *reachable* addr.
525 ;; It only requires such addresses to be syntactically correct.
526 ;; o RFC 1036 _specifically_ states that it is not an Internet
528 ;; o News is a *public* forum. Mail is a *private*
529 ;; communication medium. Posting in a _public_ forum does not
530 ;; require that you give you access to _private_ address, just as
531 ;; speaking at a public meeting does not require that I give you give
532 ;; unlisted phone number.
534 ;; Why to munge From address
536 ;; o Email address is one's own property. The reasons to munge are
537 ;; one's own. In perfect world you wouldn't need lock to your
538 ;; doors, but you do have them in houses. The world has changed
539 ;; in respect to email too.
540 ;; o Filter solution is no-road. It's an arms race; some UBE always
541 ;; sneaks through and it will never stop the actual UBE.
542 ;; o Not all people have access to filtering tools (some amy
543 ;; require certain Operating System e.g. Unix Procmail).
544 ;; o POP users download their post and each UBE byte costs in transfer
546 ;; o Nothing works as well as *not* giving the real address in the
549 ;; This package can activate the address munging very easily for
550 ;; selected newsgroups and make those email harverters gathering job
551 ;; more difficult. Humans that want to contact the person can still
552 ;; decode the address.
554 ;; To activate address munging for newsgroups matching regexp, set
555 ;; variable `tinymail-:from-anti-ube-regexp'. Your `user-mail-address'
556 ;; is be hashed and different address is generated for each post.
558 ;; me@here.com --> me.ads-hang@here.com, me.hate-ube@here.com ...
560 ;; Feature: Sendmail Plus Addressing (introduction)
562 ;; [excerpted from http://pm-doc.sourceforge.net/ for background]
563 ;; Recall from [rfc1036] that the preferred Usenet email address
564 ;; formats are following
566 ;; From: login@example.com
567 ;; From: login@example.com (First Surname)
568 ;; From: First Surname <login@example.com>
570 ;; A new sendmail supports plus addressing, where the address is
571 ;; treated like <login@example.com> and the extra "plus-info" is
572 ;; available eg to procmail or other LDAs. See Eli'd faq for more
573 ;; information at http://www.faqs.org/faqs/mail/addressing/ A typical
574 ;; sendmail enabled plus address looks like:
576 ;; login+plus-info@domain
578 ;; We can simulate plus addressing with pure RFC compliant address.
579 ;; We exploit RFC comment syntax, where comment is any text inside
580 ;; parentheses. According to Eli's paper, comments should be
581 ;; preserved during transit. They may not appear in the exact place
582 ;; where originally put, but that shouldn't be a problem. So, we
583 ;; send out message with following `From' or `Reply-To' line:
585 ;; first.surname@domain (First Surname+mail.default)
587 ;; Now, when someone replies to you, the MUA usually copies that
588 ;; address as is and you can read in the receiving end the PLUS
589 ;; information and drop the mail to appropriate folder: `mail.default'.
591 ;; [About subscribing to mailing lists with RFC comment-plus addess]
593 ;; It's very unfortunate that when you subscribe to lists, the comment
594 ;; is not preserved when you're added to the list database. Only the
595 ;; address part is preserved. I even put the comment inside angles to
596 ;; fool program to pick up everything between angles.
598 ;; first.surname(+list.linux)@example.com
600 ;; But I had no luck. They have too good RFC parsers, which throw away
601 ;; and clean comments like this. E.g. procmail based mailing lists, the
602 ;; famous `Smartlist', use `formail' to derive the return address and
603 ;; `formail' does not preserve comments. The above gets truncated to
605 ;; first.surname@example.com
607 ;; You can put anything inside RFC comment and do whatever you want
608 ;; with these plus addresses. _NOTE_: there are no guarantees that
609 ;; the RFC comment is preserved every time. Well, the standard RFC822
610 ;; says is must be passed untouched, but I'd say it is 90% of the
611 ;; cases where mail is delivered from one server to another, it is
614 ;; Example: if you discuss in usenet groups, you could use address
616 ;; first.surname@example.com (First Surname+usenet.default)
617 ;; first.surname@example.com (First Surname+usenet.games)
618 ;; first.surname@example.com (First Surname+usenet.emacs)
619 ;; first.surname@example.com (First Surname+usenet.linux)
621 ;; Feature: Sendmail Plus Addressing in this package
623 ;; The idea of setting PLUS information is that you "tag" you messages
624 ;; and when messages are returned to you, you can file the messages to
625 ;; proper folders. Unix users can set up a procmail receipe to trap
626 ;; the plus information. Alternatively Emacs Gnus can be configured
627 ;; to use fancy splitting methods for IMAP, POP and regular
630 ;; The sender field generation is disabled in `message-mode-hook' by
631 ;; function `tinymail-message-disable-sender', so that *From* field
632 ;; gets a trusted status. If you still want to generate the *Sender*
633 ;; field, then add this after package has been loaded.
635 ;; (remove-hook 'tinymail-message-disable-sender 'message-mode-hook)
637 ;; Non-Newsgroup posting
639 ;; Use your custom function to decide what address to use and what
640 ;; plus information to use by setting function to
641 ;; `tinymail-:from-info-function'. Non-Newsroup posting means, that
642 ;; you're not inside a Gnus Newsgroup from where you initiate
643 ;; a "post". A typical invocation to non-Newsroup posting is `C-x' `m'.
647 ;; You might want to set `tinymail-:from-info-function' return
648 ;; different email address for Usenet newsgroup posts. Set up an free
649 ;; email account somewhere and use only that for Usenet discussions.
650 ;; That way you can reserve your normal address to your private email
653 ;; The settings you need to enable the address generation is simple.
654 ;; Table `tinymail-:from-table-prefix' sets the left part of
655 ;; the plus address component and `tinymail-:from-table-postfix' can
656 ;; set the right part after period.
658 ;; tinymail-:from-table-prefix + tinymail-:from-table-postfix
660 ;; This makes the the *+left.right* information which is added after
661 ;; your `user-full-name' part. If `tinymail-:from-table-prefix'
662 ;; returns nothing, the `tinymail-:from-table-postfix' is used as is.
663 ;; Here is example setup. Pay attention to the "work.misc" which is
664 ;; the return value for all addresses matching "my-work-site".
666 ;; (setq tinymail-:from-table-prefix
667 ;; '(("emacs\\|perl" . "mail")
670 ;; (setq tinymail-:from-table-postfix
671 ;; '(("games" . "games")
672 ;; ("emacs\\|[a-z]+\\.el\\>\\|(def\\|(setq" . "emacs")
673 ;; ("perl\\|\\.pl\\>" . "perl")
674 ;; ("my-work-site\\>" . "work.misc"))
678 ;; If you use Gnus news reader, then you get some bonus. For Gnus
679 ;; users the default plus information is generated based on the group
680 ;; you're posting from. In general the plus address generated is
681 ;; directly the group's name. That's quite convenient. To make this
682 ;; this effective for mailing lists too, do this:
684 ;; o Rename all your mailing lists to start with *list.NAME*
685 ;; like list.ding, list.linux, list.procmail, list.dance ...
686 ;; o Edit each mailing lists group parameter with `G' `p'
687 ;; from *Group* buffer and add mailing list destination address:
689 ;; ((to-list . "Mailing List Name <address@example.com>"))
691 ;; Now when the `to-list' property is set, The Gnus group is labeled
692 ;; as "mailing list". If the `to-list' property is not set, the group
693 ;; is not considered as mailing list.
695 ;; Feature: Toggle plugged state
697 ;; With dial up connections, it is customary to swap between on-line
698 ;; and off-line mode. If you use Gnus as your mail reader, TinyMail
699 ;; can show the plugged status in the `mode-line'. If you see "tm!"
700 ;; you're plugged (on-line). The key to change the Gnus plugged status
701 ;; is bound to `C-c' `t' `j' in TinyMail controlled mail buffer.
703 ;; Configuration: Highlighting color settings
705 ;; The default highlighting is only provided to your convenience. If
706 ;; you use `font-lock' the internal highlighting is *automatically*
709 ;; Configuration: Default citation header
711 ;; This feature is mainly designed for Gnus `message-mode'. Use it
714 ;; (setq mail-yank-prefix "| ") ;; less noisy, than "> "
715 ;; (setq mail-user-agent 'message-user-agent)
717 ;; There is function `tinymail-citation-generate' which generates
718 ;; citation that uses international ISO 8601 date format, user name
719 ;; and the Gnus mailing group from where the reply started:
721 ;; * Tue YYYY-MM-DD John Doe <johnd@example.com> mail.emacs
722 ;; | ...said something
724 ;; To activate this citation reference function with your Mail User
725 ;; Agent (Gnus, RMAIL ..), call:
727 ;; (add-hook 'tinymail-:load-hook 'tinymail-install-citation)
729 ;; For supercite, install this function to the handlers and select
733 ;; (push (list tinymail-citation-generate) sc-rewrite-header-list)
734 ;; (setq sc-preferred-header-style 0)
736 ;; Code Note: shared TAB key
738 ;; When TinyMail is active in the mail buffer, it takes ower the tab
739 ;; key. The default function `tinymail-complete-guess-in-headers' is
740 ;; electric, meaning that it behaves like ordinary tab if the point is
741 ;; not in completing headers. E.g. If the point is in `Cc' or in `To',
742 ;; then the completion feature is activated. If you have plans to use
743 ;; the tab key to do some other special things in other headers,
744 ;; you're free to to do so. All you have to do is to add your own
745 ;; custom function into
747 ;; `tinymail-:complete-key-hook'
749 ;; The custom function must return `t' if it did something. See also
750 ;; `tinymail-:table-header-complete' where it is possible to define
751 ;; custom headers and the completions easily.
761 ;;; ......................................................... &require ...
764 (require 'tinylibmail)
766 ;; We must have these minor modes loaded beforehand. That's
767 ;; because we can't override that TAB key unless we "became" minor
768 ;; mode after these packages.
770 (require 'tinytab nil 'noerr)
771 (require 'tinyindent nil 'noerr)
773 (autoload 'bbdb-hashtable "bbdb" "" nil 'macro)
774 (autoload 'bbdb-gethash "bbdb")
775 (autoload 'bbdb-record-net "bbdb")
776 (autoload 'bbdb-record-name "bbdb")
777 (autoload 'bbdb-record-notes "bbdb")
779 (autoload 'mail-position-on-field "sendmail")
780 (autoload 'mml-secure-message-sign-pgpmime "mml")
781 (autoload 'mml-secure-message-encrypt-pgpmime "mml")
785 (ti::package-use-dynamic-compilation)
786 (ti::package-require-mail-abbrevs)
788 ;; forward declarations for byte compiler
789 (defvar message-citation-line-function)
790 (defvar message-reply-headers)
792 (defvar tinytab-:tab-insert-hook)
793 (defvar tinytab-mode)
795 (unless (locate-library "bbdb")
797 ** tinymail.el: No bbdb.el along `load-path'. http://bbdb.sourceforge.net/
798 You can still use the package if you do not byte compile it.
799 Package will adapt to missing BBDB features."))
801 (autoload 'message-tab "message" "" t)
802 (autoload 'message-narrow-to-headers "message")
804 (let ((loc (locate-library "nnheader")))
807 ** tinymail.el: You have too old Gnus, visit http://www.gnus.org/
808 Old Gnus version found at %s" loc)))
810 (autoload 'mail-header-from "nnheader" "" nil 'macro)
811 (autoload 'mail-header-date "nnheader" "" nil 'macro))
815 (ti::package-use-dynamic-compilation))
817 (ti::package-defgroup-tiny TinyMail tinymail-: mail
818 "Some mail additions: dynamic Fcc, Cc
821 o Some handy additions to mail sending interface.
822 o Adds automatically Cc field when you type the To: address.
823 o Changes Fcc dynamically according to header content.
824 o Very easy TAB completion: two modes, alias and definition string.
825 or password file entry completion.
826 o if Fcc folder has .gz or .Z name it automatically triggers
829 ;; Without fully qualified domain name, smtpmail.el
830 ;; can't send messages. Make sure the email is in format user@domain.com
832 (when (or (not (stringp user-mail-address))
833 (not (string-match ".+@.*\\..+"
834 (or user-mail-address
837 (concat "Tinymail: [ERROR] Please set `user-mail-address' "
838 "to \"user@somewhere.net\". Was %s")
839 (prin1-to-string user-mail-address)))
844 ;;; ......................................................... &v-hooks ...
845 ;;; hooks and functions
847 (defcustom tinymail-:load-hook nil
848 "*Hook run when package has been loaded."
852 ;; Add more dynamic change functions to this hook
854 (defcustom tinymail-:process-hook nil
855 "*Hook run when `tinymail-:awake-time' is up. This hook is always run."
859 (defcustom tinymail-:feature-hook '(tinymail-mail-send-to-list)
860 "*Hook run when idle time is up. Optional features to run.
861 Eg If you're using Gnus for mailing lists. Please define `to-list'
862 Group parameter for each group."
866 (defcustom tinymail-:complete-key-hook
867 '(tinymail-complete-everything
868 ;; tinymail-complete-bbdb-fuzzy ;honor BBDB first
870 ;; tinymail-complete-bbdb <NO GOOD> because displays
872 tinymail-complete-simple
873 tinymail-complete-guess-in-headers ; then passwd
875 ;; tinymail-complete-bbdb <NO GOOD> because displays
876 ;; BBDB record. fuzzy is better
878 ;; tinymail-complete-bbdb-fuzzy
880 tinymail-complete-headers-nothing-found
882 ;; tinymail-complete-guest-packages
883 ;; tinymail-complete-abbrevs
884 ;; tinymail-complete-headers-move-to-next-field
886 tinymail-complete-everything)
887 "*Run each function with argument nil until success.
888 This function contains default try funcions
889 to completes email addresses in the Cc and To headers. It is strongly
890 suggested that you don't add new functions to this hook with `add-hook',
891 but that that you set the value manually. The order of the tried functions
894 Default value for this hook is as follows. These are preset at startup
895 by calling function `tinymail-install-hooks' at package load time.
897 '(tinymail-complete-everything
898 tinymail-complete-simple
899 tinymail-complete-headers-nothing-found
900 tinymail-complete-abbrevs
901 ;; put your own completion functions here. Befor call to guest packages
902 tinymail-complete-guest-packages)
904 Function call arguments:
906 info This variable holds the string part at current point
909 Function should return:
911 nil Did nothing; pass control to next function in hook.
912 non-nil Handled the Tab at point"
916 (defcustom tinymail-:complete-body-hook
917 '(tinymail-complete-bbdb-fuzzy-at-point
918 tinymail-complete-guess)
919 "*Run each function with argument nil until completion success.
920 This is similar variable like `tinymail-:complete-key-hook' but run in the
921 message body. E.g. When user want to add a BBDB net entry to the current point."
925 (defcustom tinymail-:send-mail-hook-list
926 '(mail-send-hook ;; VM runs this too
928 mh-before-send-letter-hook)
929 "*List of mail sending hooks."
930 :type '(repeat (symbol :tag "Hook"))
933 (defcustom tinymail-:citation-message-id-function 'tinymail-message-id
934 "Return message-id line that is added above the citation header."
939 ;;{{{ setup: config public
941 ;;; ........................................................ &v-public ...
942 ;;; User configurable
944 (defcustom tinymail-:protect-email-addresses t
945 "*If non-nil, then scrable all words that look like an email address.
946 E.g. this@example.com is made something like this <AT> example.com.
948 Note: If you are sending other content with mail, like patches, make sure
949 you protect those with base64 encoding to prevent changing the content."
950 :type '(repeat (symbol :tag "Keymap variable"))
953 (defcustom tinymail-:table-keymap-list
957 "*List of keymaps where to install default bindings."
958 :type '(repeat (symbol :tag "Keymap variable"))
961 (defcustom tinymail-:enter-mail-hook-list
962 '( ;; gnus-message-setup-hook
963 message-header-setup-hook
968 mh-letter-mode-hook ;; MH
969 vm-mail-mode-hook) ;; VM
970 "*List of hooks where to install `tinymail-mail'."
971 :type '(repeat (symbol :tag "Hook"))
974 (defcustom tinymail-:dead-mail-file
975 (ti::package-config-file-prefix "tinymail-dead-mail")
976 "*Append all mail buffers to this fAile on Emacs exit."
980 (defcustom tinymail-:awake-time
982 10 ;XEmacs needs lower value
984 "*Sleep time of `post-command-hook' before activation."
985 :type '(integer :tag "Movement Cycles")
988 (defcustom tinymail-:confirm-mailrc-regexp "."
989 "*If matches, confirm picked completions from .mailrc file.
990 When the completion is found from the .mailrc it is matched against
991 this regexp. If `tinymail-:confirm-mailrc-regexp' matches, then
992 you're asked if you accept the match. If you discard it the other
993 completion functions get a chance to run."
997 (defcustom tinymail-:cc-kill-regexp (and (stringp user-mail-address)
998 (regexp-quote user-mail-address))
999 "*Kill all CC field elements matching regexp.
1000 The usual value is you possibel Email addresses that you
1001 wish to remove from CC fields to avoid duplicate copies when
1002 you already use Means of Fcc, Gcc etc.
1004 At any time you can add two spaces in front of CC field to
1005 disable this \"kill\" feature. This is desirable if you WANT to
1006 add a CC to your other email addresses. An example:
1008 (setq tinymail-:cc-kill-regexp \"me@here.at\")
1010 To: some@example.com
1011 CC: me@here.at << will be removed
1012 CC: me@here.at << NOT removed, because field has two spaces."
1016 (defcustom tinymail-:password-mode t
1017 "*Should we try to complete passwd entries?.
1018 if normal .mailrc completion fails then non-nil enables completion.
1020 If you're running slow machine and huge amount of users, and
1021 you can't afford to use `tinymail-:password-file' due to disk quota
1022 reasons, set this variable to nil and no passwords entries are
1023 completed. It's faster to defne .mailrc aliases that you need.
1025 This variable can be toggled with \\[tinymail-complete-password-mode]."
1029 (defcustom tinymail-:password-file
1030 (ti::package-config-file-prefix "tinypath-passwd.el")
1031 "*Preparsed password file completions.
1032 If this file does not exist it will be created when passwd
1033 completion is needed. You _can_ keep this file in compressed format by
1034 adding extension .gz to filename.
1036 If this file is nil, then no file is read or written to."
1040 (defcustom tinymail-:password-cat-cmd
1042 ((ti::os-check-hpux-p)
1044 ((ti::os-check-sunos-p)
1046 ((string-match "irix" (emacs-version))
1048 ((ti::os-check-linux-like-p)
1051 nil) ;; No password file here
1054 (substitute-command-keys
1056 "TinyMail: No tinymail-:password-cat-cmd. Please share your know-how"
1057 " with \\[tinymail-submit-bug-report]")))))
1058 "*Shell command that print the contents of standard UNIX passwd file.
1059 If your systems shell command isn't seen here, contact maintainer
1060 immedately and report right shell command, so that it is set automatically
1062 :type '(string :tag "Command")
1065 (defcustom tinymail-:complete-bbdb-fuzzy-method
1066 '( ;; Can't funcall macros, so wrap them inside lambda's
1067 (lambda (record) (bbdb-record-net record))
1068 (lambda (record) (bbdb-record-aka record))
1069 (lambda (record) (bbdb-record-name record))
1070 (lambda (record) (bbdb-record-raw-notes record)))
1071 "*Which fields to check against the completion string.
1072 The value must be list of functions to return a string or list of strings
1073 to match when passed and BBDB RECORD.
1075 The value must be callable by `funcall', e.g. macros are not callable."
1079 (defcustom tinymail-:complete-bbdb-case-fold-search case-fold-search
1080 "*Should completing against BBDB record be case sensitive.")
1082 (defcustom tinymail-:complete-mode 'string
1083 "*Control how completion is done.
1087 Means that we should complete alias names and
1088 that the alias expansion is shown in echo-area.
1092 Means that _string_, which may include any character including white
1093 spaces, is searched from the full expansion list of aliases. This way
1094 you can remember anything from the person itself and it will be
1095 searched. Found expansion is inserted in place of typed
1098 Your ~/.mailrc can have entries like this:
1100 alias mark \"Mark Eggert -- Project engineer <meg@twenix.com>\"
1101 alias mike \"Michael Lowell -- SkyTrax consulting <ml@sky.com>\"
1103 The right hand strings are searched with picked _string_ and
1104 if there is only one match for the string, the expansion (rh element)
1105 is inserted into buffer."
1111 (defconst tinymail-:idle-timer-seconds 1
1112 "*Seconds after Emacs is idle to check the mail contant in buffer.")
1114 ;;; ........................................................ &v-tables ...
1116 (defcustom tinymail-:table-fcc nil
1117 "*Replace Fcc content with FCC-FIELD-STRING when headers match REGEXP.
1118 If there is _two_ spaces in the Fcc field, the Fcc header is not touched.
1119 Format is '((REGEXP FCC-FIELD-STRING) ..)"
1122 (string :tag "To Regexp")
1123 (sexp :tag "Fcc field string")))
1126 (defcustom tinymail-:table-gcc nil
1127 "*Replace Gcc content with GCC-FIELD-STRING when headers match REGEXP.
1128 If there is _two_ spaces in the Gcc field, the Gcc header is not touched.
1129 Format is '((REGEXP GCC-FIELD-STRING) ..)"
1132 (string :tag "To Regexp")
1133 (sexp :tag "Gcc field string")))
1136 ;; (all-completions "nnml" gnus-active-hashtb 'gnus-valid-move-group-p)
1138 (defcustom tinymail-:table-header-complete nil
1139 "*List of field names and their copletion values.
1140 If after HEADER-FIELD the value is not a string, the rest value is evaluated.
1141 HEADER-FIELD must not contain colon.
1144 '((HEADER-FIELD (COMPLETION-STRING COMPLETION-STRING ..)
1145 (HEADER-FIELD EVAL-FORM)
1150 The EVAL-FORM must set `tinymail-:complete-key-return-value' to non-nil
1151 if it does not return a list of completions, but otherwise handles
1152 the completions itself. This stops running other completion functions.
1156 If you want to to complete header `Class' with values Urgent, Note, Memo,
1159 In addition completing the Gnus Gcc and Newsgroup header is easy. Some notes
1160 about the EVAL-FORM used: the form is called in function
1161 `tinymail-complete-simple', so all variables used there are visible in
1162 EVAL-FORM. The `string' is the read word from current point, which you
1163 should use when searching completions.
1165 (setq tinymail-:table-header-complete
1167 (\"Urgent\" \"Note\" \"Memo\" \"FYI\" \"Announce\"))
1170 (when (and (featurep 'gnus) (stringp string))
1173 gnus-active-hashtb 'gnus-valid-move-group-p)))
1176 (when (and (featurep 'gnus) (stringp string))
1180 (gnus-read-active-file-p))))))"
1183 (string :tag "Field")
1184 (repeat (string :tag "value"))))
1188 ;;{{{ setup: Sendmail like PLUS Address configuration
1190 (defcustom tinymail-:from-field-plus-separator "+"
1191 "The string to separate `user-full-name' from plus information.
1192 Note: some MTAs may not accept '+' character. An alternative
1195 login+plus-information@example.com
1197 login@example.com (First Surname+plus-information)"
1201 (defcustom tinymail-:from-field-enable-flag t
1202 "*Non-nil means that From: field generation is allowd.
1203 The function to generate the from field is `tinymail-from-set-field'."
1207 (defcustom tinymail-:from-anti-ube-regexp "games\\|ibm"
1208 "*If Regexp match Newsgroups header, generate anti-ube email From address.
1209 Generated address is based un `user-mail-address' with hashed words in
1210 the address. The generated email is made with `ti::mail-email-make-anti-spam-address'.
1211 Returned value is different each time.
1213 me@here.com --> me.ads-hang@here.com, me.hate-ube@here.com ...
1217 For complete email address control, you want to use
1218 `tinymail-:from-info-function'."
1222 (defcustom tinymail-:from-info-function nil
1223 "*Functon to return the suitable `user-mail-address' for message.
1227 '(email-address [plus-string] [Filername Surname])
1229 If if function wants to change only the email-address for the message,
1230 the return value is in format:
1234 And if the Plus info and Another user-id FirstName and Surname is
1235 wanted, then return value is:
1237 '(\"foo@bar.com\" \"mail.priv\" \"Mr. Foo\")
1239 If the return value is nil, the `user-mail-address' is used.
1243 Value returned from this function overrides
1245 `tinymail-:from-table-prefix'
1246 `tinymail-:from-table-postfix'
1247 `tinymail-:from-anti-ube-regexp'
1249 If you want to protect yourself from UBE (Unsolicited bulk Email), you
1250 can use function `ti::mail-email-make-anti-spam-address' which uses hash table
1251 to construct human, but not easily machine decodable address.
1255 Suppose you have some public domain email address, like hotmail
1256 and you want to use that in your Usenet postings instead of your normal
1257 email address. Here is code to do that:
1259 (setq tinymail-:from-info-function 'my-tinymail-address)
1261 (defun my-tinymail-address ()
1262 (when (mail-fetch-field \"Newsgroups\")
1263 (list \"my-virtual@hotmail.com\")))
1267 Suippose you want to make email harverter's work harder and use non-spam
1268 address in the high traffic Usenet groups. Here the ibm and games groups
1269 get \"protected\" address, which human can decode if they wish to contact
1270 you personally. Other usenet groups use your normal virtual aaddress.
1271 All other mail use your default `user-mail-address'.
1273 (setq tinymail-:from-info-function 'my-tinymail-address)
1275 (defun my-tinymail-address ()
1276 let ((group (or (mail-fetch-field \"Newsgroups\") \"\" ))
1277 (addr \"my-virtual@hotmail.com\"))
1279 ((string-match \"games\\\\|ibm\" group)
1280 (list (ti::mail-email-make-anti-spam-address addr))) ;; grumbled address
1281 ((string= \"\" group)
1282 (list addr)))) ;; use normal virtual address
1284 This differ's from `tinymail-:from-anti-ube-regexp' so that you have full
1285 control what address is used to generate the anti-ube address."
1289 (defcustom tinymail-:from-table-prefix nil
1290 "*If `Newsgroup' header, match regexp, return plus address prefix.
1300 '((\"emacs\\\\|perl\" . \"mail\")
1301 (\".\" \"usenet\"))"
1302 :type '(repeat (cons regexp string))
1305 (defcustom tinymail-:from-table-postfix nil
1306 "*Rules for constructing COMMENT PLUS part of the From address.
1308 Match the Newsgroup header:
1310 If there is `Newsgroup' header, match regexp AND combine
1311 result of `tinymail-:from-table-prefix' with `tinymail-:from-table-postfix'
1316 o Go to the beginning of body, after headers and search body
1317 for regexp and return STRING from `tinymail-:from-table-postfix'.
1318 `tinymail-:from-table-prefix' IS NOT USED.
1319 o If not found, search Gcc Gnus header and use it
1320 o Otherwise use no postfix
1324 The left hand element can also be FUNCTION, which is called. It must
1325 return STRING like in the cdr element.
1334 From: foo@bar.com (Foo Bar+mail.emacs)
1335 To: somebody@else.com
1336 Subject: Re: Emacs keybindings
1337 Gcc: nnml:mail.reply
1338 --text follows this line--
1340 ...See function global-set-key and frieds in your Emacs.
1342 Suppose we have above example mail in the buffer. The From line contains
1343 string +mail.emacs added inside the comment (), because word 'emacs' were
1344 found from the body of text according to the following varible contents:
1346 (setq tinymail-:from-table-postfix
1348 ;; Restrictive regexp first. These are searched from body
1351 (\"[a-z]+\\\\.el\\\\>\\\\|(def\\\\|setq\" . \"mail.emacs\")
1352 (\"\\.pl\\\\>\" . \"mail.perl\")
1354 (\"games\" . \"mail.games\")
1355 (\"emacs\" . \"mail.emacs\")
1356 (\"perl\" . \"mail.perl\")))"
1357 :type '(repeat (cons
1358 (choice regexp function)
1363 ;;{{{ setup: Reportmail
1365 (defcustom tinymail-:report-window-system (ti::compat-window-system)
1366 "*If non-nil; then never try to use X dragbar to announce mail.
1367 Display the mail message in echo area instead."
1371 (defconst tinymail-:report-asychronous-timeout 3
1372 "If non-nil, SECONDS to wait for `tinymail-:report-mail-notify-program' finish.
1373 If you are in a system where mailbox is over NFS and there are lot of
1374 periodic NFS mount or access problems (automount failure, hardware
1375 problem or whatever); then set this variable to number of seconds to timeout
1376 `tinymail-:report-mail-notify-program'.
1378 If you don't set the timeout in NFS problematic environment, then the
1379 call to repor tmail is blocked until answer has been received. This may freeze
1380 your whole Emacs for several minutes.")
1382 (defcustom tinymail-:display-time t
1383 "*If non-nil, display the current time, load, and mail flag."
1387 (defvar tinymail-:report-spool-buffer "*tinymail-mail-spool*"
1388 "*Buffer where to write mail spool information.
1389 If this value is initially set to nil, no mail reporting is done.
1390 See `tinymail-:report-spool-buffer-control'.")
1392 (defcustom tinymail-:report-spool-buffer-control 'keep
1393 "*How to treat the `tinymail-:report-spool-buffer'.
1394 Accepted values are:
1396 'kill Query mail spool and kill the buffer
1397 'keep Query mail spool but do not kill after query
1398 'raise If there is mail and mail count has changed since the last
1399 query; show the buffer in current working frame."
1408 (defun tinymail-default-report-mail-command ()
1409 "Construct default report mail shell call."
1410 (let ((mail (getenv "MAIL"))
1414 (or (and (file-exists-p "~/.procmailrc") ;; [1]
1416 TinyMail: [WARNING] autosetup aborted. $HOME/.procmailrc found. Please set
1417 manually `tinymail-:report-mail-notify-program' to cover incoming mail
1420 (executable-find "from") ;; [2a]
1421 (executable-find "mailfrom") ;; [2b]
1423 (or (file-exists-p mail)
1424 ;; In pristine system, user may not have received
1425 ;; mail yet, but if the leading directory is there,
1426 ;; then it's good enough
1428 ;; /var/spool/mail/LOGIN => /var/spool/mail/
1431 (file-name-directory mail))
1432 (message "TinyMail: [ERROR] Environment variable MAIL is invalid: %s "
1434 (executable-find "grep")
1435 (format "%s \"^From \" %s"
1436 (executable-find "grep")
1438 ;; Okay, we give up. This is the fall-through case
1439 (let ((function (if (ti::win32-p)
1442 (funcall function "\ ;; [4]
1443 TinyMail: [WARNING] Can't guess `tinymail-:report-mail-notify-program'. Set manually.")
1449 (string-match "\\<bin\\>" (or shell-file-name "")))
1450 ;; This system is using Cygwin bash
1451 (ti::file-name-forward-slashes-cygwin cmd))
1453 (ti::emacs-type-unix-like-p)) ;Unix, return as is
1456 (ti::file-name-backward-slashes cmd)))))))
1458 (defcustom tinymail-:report-mail-notify-program
1459 (let ((cmd (tinymail-default-report-mail-command)))
1462 "*A shell call to return entries in the mail spool(s).
1463 Set to nil if tou have lo local mail folders to scan.
1467 If you're mixing Cygwin32 and DOS shell buffers in your Emacs,
1468 you MUST SET THIS variable and not rely on the automatic detection
1469 of Cygwin, which is determined by examining `shell-file-name'.
1471 The call must reflect you `shell-file-name', where paths must be
1472 Unix or Win32 styled accordingly.
1474 Program must return entries in following format, which is the Berkeley mailbox
1475 format or commonly known as Unix MBOX format:
1477 From login@site.xx Mon Feb 26 14:41:50 EET 1996
1479 See if you can use from(1), mailfrom(1) or equivalent: \"man -k from\".
1480 Make sure the binary is on your path, possibly located at /usr/ucb/ or
1481 /usr/bin/. If you use absolute path, this program executes faster.
1483 The `tinymail-:report-mail-notify-program' value can be:
1485 STRING A shell program is called to return the lines
1486 SYMBOL an Emacs Lisp function is called to return the lines. Lisp function
1487 must return list of string or nil. There is default function
1488 `tinymail-report-mail-info-spool' which searches all messages in
1489 `tinymail-:report-mail-spool-dir'"
1491 (string :tag "Shell program")
1492 (function :tag "Lisp function"))
1495 (defcustom tinymail-:report-mail-kill-line-regexp
1500 "Kill lines matching this regexp from report mail buffer.
1501 When `tinymail-:report-mail-notify-program' has finished printing the addresses,
1502 it may print some garbage into the buffer like: 'command finished'
1503 'No mail'. With this regexp you can kill these unwanted lines, otherwise
1504 the line count would have been equal to the pending mail count.
1505 Below the actual count is (1) and the message should display the
1506 last message, not the 'Command finished'.
1508 From login@site2.xx Mon Feb 26 14:41:50 EET 1996
1509 From login@site1.xx Mon Feb 26 14:41:50 EET 1996
1514 (defcustom tinymail-:report-keep-intact-list
1516 "*A list of frame names not to change."
1517 :type '(repeat string)
1520 (defcustom tinymail-:report-no-mail-string
1521 (if tinymail-:report-window-system
1523 ;; This is better for echo area in non-window emacs
1525 "*String to be printed to dragbar when no mail is pending.
1526 If this string is nil, then nothing is displayed in
1527 the echo area if Emacs is running in non-windowed envinronment."
1531 (defcustom tinymail-:report-format-string
1533 tinymail-:report-old-frame-string
1535 ;; Use (display-time) in you ~/.Emacs to define display-time-string
1536 (if (and (boundp 'display-time-string) ;may not exist ?
1537 (stringp display-time-string)) ;XEmacs has vector
1540 tinymail-:report-mail-info-string)
1541 "*Customize your display string layout here."
1545 (defcustom tinymail-:report-mail-info-shorten-regexp nil
1546 "*Regexp to match local site address.
1547 When you're in local host and receive mail internally, you
1548 propably want to display user's account name only instead of full
1549 email name. This is REGEXP that is tried upon arrived email address,
1550 if it matches, the email address is truncated to account name."
1551 :type '(string :tag "Regexp")
1555 ;;{{{ Setup: private
1557 (defvar tinymail-:report-old-frame-string nil
1560 (defvar tinymail-:report-old-mail-info-string nil
1563 (defvar tinymail-:report-timer-object nil
1564 "Private. When package is activated this hold the timer object ativated.")
1566 (defvar tinymail-:report-mail-info-string nil
1567 "Private. Mail message information string.
1568 This variable has one leading and trailingspace around the message.")
1570 (defvar tinymail-:timer-elt nil
1571 "Timer element is stored here.")
1573 (defvar tinymail-:y-or-n-p nil
1574 "Andwered key from `tinymail-y-or-n-p'")
1576 (defvar tinymail-:tm-mode-name ""
1577 "TM MIME message split indicator.")
1579 (defvar tinymail-:message-type nil
1580 "Private flag. The initial message type in mail buffer.
1581 When tinymail is first turned on, it checks if the message
1582 is composed with \\[mail] or if you have replied to someone
1583 else's message with 'r' from some mail mode. This initial
1584 message type determines how \\[tinymail-mail] call behaves in the buffer.")
1586 (put 'tinymail-:message-type 'permanen-local t)
1587 (make-variable-buffer-local 'tinymail-:message-type)
1588 (setq-default tinymail-:message-type nil)
1590 (defvar tinymail-:last-to-field nil
1591 "Private. Last to: field value.")
1592 (make-variable-buffer-local 'tinymail-:last-to-field)
1594 (defvar tinymail-:mail-aliases-alist nil
1595 "Private. Cached aliases.
1596 Run function `tinymail-update-mail-abbrevs' if you change your
1597 ~/.mailrc so that this variable gets updated.
1599 Format: ((\"ALIAS\" . \"EXPANDED\") ..)")
1601 (defvar tinymail-:temp-buffer " *tinymail-tmp*"
1602 "Temporary buffer.")
1604 (defvar tinymail-:password-alist nil
1605 "Private. Password file in assoc form: '((LOGNAME . PASSWD-ENTRY)).")
1607 (defvar tinymail-:password-completion-alist nil
1608 "Private. Completion table of login names.")
1610 (defvar tinymail-:user-mail-address nil
1611 "This is made local to mail buffer.
1612 Only ised if `tinymail-from-anti-ube-maybe' is in effect.")
1615 ;;{{{ setup: private
1617 (defvar tinymail-:complete-key-return-value nil
1618 "Value set to non-nil in `tinymail-:table-header-complete' EVAL-FORM.")
1621 ;;{{{ setup: version
1623 ;;;###autoload (autoload 'tinymail-version "tinymail" "Display commentary." t)
1626 (ti::macrof-version-bug-report
1629 tinymail-:version-id
1630 "$Id: tinymail.el,v 2.88 2007/08/03 20:16:25 jaalto Exp $"
1631 '(tinymail-:version-id
1638 message-header-setup-hook
1643 tinytab-:tab-insert-hook
1644 ;; This list is automatically generated by tinylisp-mode "$ v"
1646 tinymail-:process-hook
1647 tinymail-:feature-hook
1648 tinymail-:complete-key-hook
1649 tinymail-:complete-body-hook
1650 tinymail-:send-mail-hook-list
1651 tinymail-:citation-message-id-function
1653 tinymail-:tm-mode-name
1654 tinymail-:message-type
1655 tinymail-:last-to-field
1656 tinymail-:mail-aliases-alist
1657 tinymail-:temp-buffer
1658 tinymail-:password-alist
1659 tinymail-:password-completion-alist
1660 tinymail-:table-keymap-list
1661 tinymail-:enter-mail-hook-list
1662 tinymail-:dead-mail-file
1663 tinymail-:confirm-mailrc-regexp
1664 tinymail-:cc-kill-regexp
1665 tinymail-:password-mode
1666 tinymail-:password-file
1667 tinymail-:password-cat-cmd
1668 tinymail-:complete-bbdb-fuzzy-method
1669 tinymail-:complete-bbdb-case-fold-search
1670 tinymail-:complete-mode
1673 tinymail-:table-header-complete
1674 tinymail-:from-field-plus-separator
1675 tinymail-:from-field-enable-flag
1676 tinymail-:from-anti-ube-regexp
1677 tinymail-:from-info-function
1678 tinymail-:from-table-prefix
1679 tinymail-:from-table-postfix
1680 tinymail-:report-window-system
1681 tinymail-:display-time
1682 tinymail-:report-spool-buffer
1683 tinymail-:report-spool-buffer-control
1684 tinymail-:report-mail-notify-program
1685 tinymail-:report-mail-kill-line-regexp
1686 tinymail-:report-keep-intact-list
1687 tinymail-:report-no-mail-string
1688 tinymail-:report-format-string
1689 tinymail-:report-mail-info-shorten-regexp
1690 tinymail-:report-old-frame-string
1691 tinymail-:report-old-mail-info-string
1692 tinymail-:report-timer-object
1693 tinymail-:report-mail-info-string
1694 tinymail-:complete-key-return-value)
1695 '(tinymail-:debug-buffer)))
1697 ;;;### (autoload 'tinymail-debug-toggle "tinymail" "" t)
1698 ;;;### (autoload 'tinymail-debug-show "tinymail" "" t)
1700 (eval-and-compile (ti::macrof-debug-standard "tinymail" "-:"))
1705 ;;;###autoload (autoload 'tinymail-mode "tinymail" "" t)
1706 ;;;###autoload (autoload 'turn-on-tinymail-mode "tinymail" "" t)
1707 ;;;###autoload (autoload 'turn-off-tinymail-mode "tinymail" "" t)
1708 ;;;###autoload (autoload 'tinymail-commentary "tinymail" "" t)
1711 (ti::macrof-minor-mode-wizard
1712 "tinymail-" " tm" "\C-ct" "tm" 'TinyMail "tinymail-:" ;1-6
1714 "Mail enchancements.
1715 For Documentation, run \\[tinymail-version]
1719 Prefix key to access the minor mode is defined in `tinymail-:mode-prefix-key'
1721 \\{tinymail-:mode-prefix-map}"
1727 (if buffer-read-only
1728 (error "TinyMail: Buffer is read-only, cannot turn on mode")
1731 (tinymail-mail 'disable))))
1732 "Mail enchancement mode"
1734 tinymail-:mode-easymenu-name
1735 ["TO field tracking on/off" tinymail-on-off-toggle t]
1736 ["Complete by guessing" tinymail-complete-guess t]
1737 ["Complete in body" tinymail-complete-guess-in-body t]
1738 ["Complete passwords mode" tinymail-complete-password-mode t]
1739 ["Abbrev expand at point" expand-abbrev t]
1740 ["Abbrev rebuild (.mailrc)" tinymail-update-mail-abbrevs t]
1741 ["Deactivate and set address"
1742 tinymail-deactivate-and-send-to-you t]
1743 ["Toggle Gnus plugged state"
1744 tinymail-gnus-agent-toggle-plugged t]
1746 ["Debug toggle" tinymail-debug-toggle t]
1747 ["Debug show" tinymail-debug-show t]
1749 ;; ["Keyboard menu" tinymail-menu-main t]
1750 ["Package version" tinymail-version t]
1751 ["Package commentary" tinymail-commentary t]
1752 ["Mode help" tinymail-mode-help t]
1753 ["Mode off (exit)" turn-off-tinymail-mode t])
1755 (define-key root-map "\t" 'tinymail-complete-key-interactive)
1756 ;;; (define-key root-map " " 'tinymail-expand-abbrev)
1757 (define-key map "dd" 'tinymail-debug-toggle)
1758 (define-key map "ds" 'tinymail-debug-show)
1759 (define-key map "j" 'tinymail-gnus-agent-toggle-plugged)
1760 (define-key map "p" 'tinymail-complete-password-mode)
1761 (define-key map "u" 'tinymail-update-mail-abbrevs)
1762 (define-key map "t" 'tinymail-on-off-toggle)
1763 (define-key map "\t" 'tinymail-complete-guess-in-body)
1764 (define-key map "x" 'turn-off-tinymail-mode)
1765 (define-key map "?" 'tinymail-mode-help)
1766 (define-key map "Hm" 'tinymail-mode-help)
1767 (define-key map "Hc" 'tinymail-commentary)
1768 (define-key map "Hv" 'tinymail-version))))
1770 ;;; ----------------------------------------------------------------------
1772 (defun tinymail-modeline-update (&rest plugged-status)
1773 "Udate `tinymail-:mode-name' to show ! in plugged state."
1774 (let* ((status (if (not (zerop (length plugged-status)))
1775 (car plugged-status)
1776 (ti::mail-plugged-p))))
1778 (unless (string-match "!" tinymail-:mode-name)
1779 (setq tinymail-:mode-name (concat tinymail-:mode-name "!")))
1780 (when (string-match "^\\([^!]+\\)!" tinymail-:mode-name )
1781 (setq tinymail-:mode-name (match-string 1 tinymail-:mode-name ))))))
1783 ;;; ----------------------------------------------------------------------
1785 (defun tinymail-gnus-agent-toggle-plugged (&optional mode)
1786 "Toggle Gnus plugged state if Gnus has been loaded."
1788 (if (not (fboundp 'gnus-agent-toggle-plugged))
1789 (message "Can't change plugged staus. Gnus Agent is not loaded.")
1790 (let ((status (ti::mail-plugged-p)))
1791 (ti::bool-toggle status mode)
1793 (ti::funcall 'gnus-agent-toggle-plugged t)
1794 (ti::funcall 'gnus-agent-toggle-plugged nil))
1795 (tinymail-modeline-update (ti::mail-plugged-p)))))
1797 ;;; ----------------------------------------------------------------------
1798 ;;; #todo: is this really needed
1800 (defun tinymail-expand-abbrev (&optional arg)
1801 "Call `abbrev-expand' if cursor is inside header or `self-insert-command'.
1802 If Prefix argument is given, call `self-insert-command' with ARG.
1803 This function should be bound to SPACE key."
1805 (if (or (not (null arg))
1806 (not (and (< (point) (ti::mail-hmax))
1807 (fboundp 'expand-abbrev)
1809 (self-insert-command (prefix-numeric-value arg))))
1811 ;;; ----------------------------------------------------------------------
1813 (defun tinymail-install-hooks (&optional remove verb)
1814 "Install needed hooks, optionally REMOVE. VERB."
1816 ;; tinymail-complete-everything
1817 ;; tinymail-complete-bbdb-fuzzy ;honor BBDB first
1818 ;; tinymail-complete-bbdb <NO GOOD> because displays
1820 tinymail-complete-simple
1821 tinymail-complete-guess-in-headers ; then passwd
1822 ;; tinymail-complete-bbdb <NO GOOD> because displays
1823 ;; BBDB record. fuzzy is better
1824 ;; tinymail-complete-bbdb-fuzzy
1825 tinymail-complete-headers-nothing-found)))
1826 ;; tinymail-complete-guest-packages
1827 ;; tinymail-complete-abbrevs
1828 ;; tinymail-complete-headers-move-to-next-field
1829 (ti::add-hooks 'tinymail-:process-hook
1830 '(tinymail-modeline-update)
1832 (ti::add-hooks 'kill-emacs-hook 'tinymail-save-dead-mail-maybe remove)
1833 ;; Install the default functions only if this hook is initially nil
1836 (ti::add-hooks 'tinymail-:complete-key-hook list 'remove))
1837 ((null tinymail-:complete-key-hook)
1838 ;; The TAB key handler. First remove the hooks, and then add, so that
1839 ;; they will be in this order. The order is _very_ important.
1840 (ti::add-hooks 'tinymail-:complete-key-hook list 'remove)
1841 ;; The first function that runs must be at the end of list
1842 (ti::add-hooks 'tinymail-:complete-key-hook (reverse list))))
1843 ;; List of hooks where to install us
1844 (ti::add-hooks tinymail-:enter-mail-hook-list
1845 'turn-on-tinymail-mode remove)
1846 (ti::add-hooks 'write-file-hooks
1847 'tinymail-write-file-hook remove)
1848 (ti::add-hooks 'tinymail-:mode-define-keys-hook
1849 'tinymail-mode-define-keys remove)
1850 ;; If user has allowed message-mode to run tinymail, then also install
1851 ;; this function, which prevents Sender field genearation. (We generate
1853 (when (memq 'message-header-setup-hook tinymail-:enter-mail-hook-list)
1854 (ti::add-hooks 'message-mode-hook
1855 'tinymail-message-disable-sender remove))
1858 (message "TinyMail: hooks removed.")
1859 (message "TinyMail: hooks installed")))))
1861 ;;; ----------------------------------------------------------------------
1863 (defun tinymail-install-table-header-complete-gnus ()
1864 "Add Gnus Followup-To, Gcc, Newsgroups to `tinymail-:table-header-complete'."
1865 ;; Debian bug report header
1866 ;; http://www.debian.org/Bugs/Reporting
1867 ;; See http://www.debian.org/Bugs/Developer#severities
1868 (aput 'tinymail-:table-header-complete
1871 '("critical" ;; Whole system break, serious data loss
1872 "grave" ;; unuseable, data loss, security hole
1873 "serious" ;; violation of Debian policy, unsuitable for release.
1874 "important" ;; major effect withour completely unusable.
1875 "normal" ;; the default value, applicable to most bugs.
1876 "minor" ;; doesn't affect the package's usefulness
1877 "wishlist" ;; feature request
1878 "fixed"))) ;; fixed but should not yet be closed.
1879 ;; Debian bug report header
1880 (aput 'tinymail-:table-header-complete
1884 "wontfix" ;; change will cause other, worse, problems
1885 "moreinfo" ;; more information must be provided by the submitter
1886 "unreproducible" ;; can't be reproduced on the maintainer's system
1887 "fixed" ;; bug is fixed or worked around, needs to be resolved
1888 "security" ;: security problem in a package
1889 "potato" ;; potato release
1890 "woody" ;; woody distribution
1891 "s1id"))) ;; architecture that is currently unreleased
1892 (aput 'tinymail-:table-header-complete
1895 '(when (eq major-mode 'message-mode)
1896 (call-interactively 'message-tab)
1897 ;; We must stop the other completion function from running
1898 (setq tinymail-:complete-key-return-value t)
1900 (aput 'tinymail-:table-header-complete
1903 '(if (not (featurep 'gnus))
1904 (prog1 nil (message "TinyMail: Gcc completion needs Gnus..."))
1905 (when (stringp string))
1908 gnus-active-hashtb 'gnus-valid-move-group-p))))
1909 (aput 'tinymail-:table-header-complete
1912 '(if (not (featurep 'gnus))
1914 (message "TinyMail: Newsgroups completion needs Gnus..."))
1915 (when (stringp string))
1919 (gnus-read-active-file-p))))))
1921 ;;; ----------------------------------------------------------------------
1923 (defun tinymail-read-version (sym)
1924 "Read version number from variable SYM if variable exists.
1925 Otherwise return ''."
1926 (or (ti::string-match "[0-9][0-9.]+" 0
1927 (if (boundp sym) (symbol-value sym) ""))
1930 ;;; ----------------------------------------------------------------------
1931 ;;; - This is the main controller "install" that calls all other
1934 (defun tinymail-install-to-buffers (&optional remove verb)
1935 "Activate or REMOVE tinyamil from mail buffers."
1936 (dolist (buffer (buffer-list)) ;; Activate in current Emacs
1937 (with-current-buffer buffer
1938 (when (and (memq major-mode '(message-mode mail-mode))
1939 ;; #todo: vm send mode?
1940 (null buffer-read-only))
1941 (message "TinyMail: mode %s in buffer %s"
1942 (if remove "deactivated" "activated")
1944 (tinymail-mode (if remove -1 1) )))))
1946 ;;; ----------------------------------------------------------------------
1947 ;;; - This is the main controller "install" that calls all other
1950 (defun tinymail-install (&optional remove)
1951 "Install or REMOVE package."
1953 (let ((idle-p (ti::idle-timer-supported-p)))
1956 TinyMail: This Emacs does not support idle timers. Using regular timers."))
1957 (ti::compat-timer-cancel-function 'tinymail-process)
1958 (tinymail-report-mail-install-maybe remove)
1959 (tinymail-install-hooks remove)
1960 ;; If the idle timer is available, use it. Otherwise we would have
1961 ;; no other option but occupy post command hook
1963 ;;; 2007-05-18 disabled. FIXME: needed? Too much CPU?
1965 ;;; (setq tinymail-:timer-elt
1967 ;;; 'run-with-idle-timer
1968 ;;; tinymail-:idle-timer-seconds
1970 ;;; 'tinymail-process))
1971 ;;; ;; Can't respect tinymail-:idle-timer-seconds,
1972 ;;; ;; so use 20 seconds repeat time.
1973 ;;; (setq tinymail-:timer-elt
1974 ;;; (run-at-time "20 sec" 20 'tinymail-process)))
1975 (when (ti::nil-p (user-full-name))
1978 "TinyMail: [ERROR] please set variable `user-full-name'."
1980 (prin1-to-string (user-full-name))))
1981 (tinymail-install-table-header-complete-gnus)
1982 (tinymail-install-to-buffers)
1984 "TinyMail: Installed. Read documentation with M-x tinymail-version"))))
1986 ;;; ----------------------------------------------------------------------
1988 (defun tinymail-install-citation ()
1989 "Install First line citation function for Mail user agents."
1990 (setq message-citation-line-function
1991 'tinymail-message-citation-line-function))
1996 ;;; ----------------------------------------------------------------------
1998 (defsubst tinymail-y-or-n-p-abort-p ()
1999 "Check if `tinymail-y-or-n-p' was abort."
2000 (ti::char-in-list-case tinymail-:y-or-n-p '(?q ?Q)))
2002 ;;; ------------------------------------------------------------ &misc ---
2004 (defsubst tinymail-field-off-p (header-name &optional header-value)
2005 "Check status of HEADER-NAME field which has optional HEADER-VALUE.
2006 If there is 2 or more leading spaces, then the field is considered 'off'."
2007 (when (and (stringp header-name)
2008 (stringp header-value))
2009 (not (memq (ti::mail-field-space-count header-name header-value)
2012 ;;; ----------------------------------------------------------------------
2014 (defmacro tinymail-mail-aliases ()
2015 "Return `tinymail-:mail-aliases-alist' or build it if it is empty."
2016 (` (or tinymail-:mail-aliases-alist
2017 (tinymail-update-mail-abbrevs))))
2019 ;;; ----------------------------------------------------------------------
2021 (defun tinymail-make-local-hook-available-p ()
2022 "Check if `make-local-hook' is really available."
2023 (or (ti::xemacs-p) ;Always in XEmacs
2024 (and (ti::emacs-p) ;19.30 and up
2025 (not (string-match "19.2[0-9]" (emacs-version))))))
2027 ;;; ----------------------------------------------------------------------
2029 (defun tinymail-y-or-n-p (message)
2030 "Ask confirmation for MESSAGE. Accept TAB as yes.
2031 Setq global variable `tinymail-y-or-n-p' to the result."
2032 (setq tinymail-:y-or-n-p
2033 (ti::read-char-safe-until
2034 (concat message " (tab/n or q)")
2038 ?\b ?\177 ?\C-h ?\127
2041 ;; These keys are usually above the TAB key, so you can answer
2042 ;; NO with your left hand.
2047 (ti::char-in-list-case tinymail-:y-or-n-p '(?y ?Y ?\ ?\t ?\n ?\r)))
2049 ;;; ----------------------------------------------------------------------
2051 (defun tinymail-write-file-hook ()
2052 "Rebuild aliases everytime .mailrc is saved."
2053 (tinymail-debug "tinymail-write-file-hook" "MODE" major-mode (buffer-name))
2054 (when (string-match "\\.mailrc" (or (buffer-file-name) ""))
2055 (message "TinyMail: Updating mail aliases and abbrevs...")
2056 (build-mail-aliases)
2057 (when (fboundp 'build-mail-abbrevs) ;update abbrevs too
2058 (ti::funcall 'build-mail-abbrevs))
2059 (tinymail-update-mail-abbrevs 'force)
2060 (message "TinyMail: Updating mail aliases and abbrevs...done")
2061 ;; Hook return value
2064 ;;; ----------------------------------------------------------------------
2066 (defun tinymail-update-mail-abbrevs (&optional force)
2067 "Build up all aliases to `tinymail-:mail-aliases-alist' cache and return it.
2068 Optional FORCE builds the list in any case.
2069 You need to run this function if you change your ~/.mailrc."
2071 (tinymail-debug "tinymail-update-mail-abbrevs")
2072 (when (and (fboundp 'build-mail-abbrevs) ;update abbrevs too
2073 (or force (interactive-p)))
2074 (ti::funcall 'build-mail-abbrevs))
2075 (setq tinymail-:mail-aliases-alist (ti::mail-abbrev-get-alist)))
2077 ;;; ----------------------------------------------------------------------
2080 (defun tinymail-deactivate-and-send-to-you ()
2081 "Deactivate TinyMail and change To field to point to your address.
2082 This function is normally used when you use mailing lists. See
2083 documentation in the tinymail.el or call \\[tinymail-version]."
2085 (tinymail-debug "tinymail-deactivate-and-send-to-you")
2086 (if (ti::nil-p user-mail-address)
2087 (error "TinyMail: Please set variable `user-mail-address'")
2088 (ti::mail-kill-field "^to:" user-mail-address)
2089 (tinymail-field-to-off)
2091 (message "Address changed to point to you. TinyMail signs off."))))
2093 ;;; ----------------------------------------------------------------------
2095 (defun tinymail-buffer-email-address-scramble-area ()
2096 "Return are of eamil that can be scrambled.
2097 Exclude patches and attachments."
2100 "^RCS[ \t]+file:.*,v\\|^diff[ \t]+-[^- \t\r\n]"
2101 "[<]#part" ;; Gnus attachment
2103 (point-list (list (point-max)))
2108 (when (search-forward (or mail-header-separator
2109 "---NOTHING__TO_FIND")
2111 (setq beg (1+ (line-end-position)))
2113 (when (re-search-forward re nil t)
2114 (push (line-beginning-position) point-list)))
2115 (setq end (apply 'min point-list))))
2119 ;;; ----------------------------------------------------------------------
2121 (defun tinymail-buffer-email-address-scramble-1 ()
2122 "Spam protect email address words.
2123 Scramble Email addresses do that spammers cannot use them.
2124 The end position is before text that looks like a patch or `point-max'"
2125 (multiple-value-bind (beg end)
2126 (tinymail-buffer-email-address-scramble-area)
2130 (let ( ;; If there is patch in this buffer, limit changes before it.
2133 ;; Must be separated by space or "<".
2134 ;; this email@example.com or <email@example.com>
2135 ;; But not http://user@site.com/
2137 "\\([^ /\t\r\r]+\\)@\\([^ /\t\r\r]+\\.[^ /\t\r\r]+\\)"
2138 "\\(^\\|[ \t]\\)")))
2139 (while (re-search-forward regexp end t)
2140 (replace-match "\\1\\2 AT \\3\\4")))))))
2142 ;;; ----------------------------------------------------------------------
2144 (defun tinymail-buffer-email-address-scramble ()
2145 "If `tinymail-:protect-email-addresses' is non-nil, scrable addresses."
2146 (if tinymail-:protect-email-addresses
2147 (tinymail-buffer-email-address-scramble-1))
2148 ;; Hook function. Return nil
2151 ;;; ----------------------------------------------------------------------
2153 (defun tinymail-active-p ()
2154 "Check if TinyMail is active in current buffer."
2157 ;;; ----------------------------------------------------------------------
2160 (defun tinymail-mail (&optional disable verb)
2162 Add or changes Cc, FF, X-Sender-Info fields on the fly while you're
2163 composing the message.
2167 DISABLE Disables package.
2168 VERB print verbose message.
2172 `tinymail-:feature-hook'."
2173 (let* ((fid "tinymail-mail")
2175 (unless fid ;; No-op. XEmacs byte compiler silencer
2181 (when (featurep 'tinytab)
2182 ;; - make TinyTab.el work with TinyMail so that they share
2184 ;; - Remove and add make sure the function is at the beginning and
2186 (remove-hook 'tinytab-:tab-insert-hook
2187 'tinymail-complete-key-interactive)
2188 (add-hook 'tinytab-:tab-insert-hook
2189 'tinymail-complete-key-interactive))
2191 ;; If you're replying to someone else's message, the TO field
2192 ;; must have two spaces to turn off TinyMail so that the remaining
2193 ;; Cc fields are not modified.
2195 ;; R and r keys don't add Cc field, so we put there only one space.
2197 ;; For simple C-x m TO field will be initially empty.
2200 "MAIN STATUS (BEFORE)"
2202 "msg type" tinymail-:message-type
2203 "to" (ti::mail-get-field-1 "to")
2204 "cc" (ti::mail-get-field-1 "cc")
2205 "Subject" (ti::mail-get-field-1 "subject")
2208 (buffer-substring (point-min) (point-max))
2210 (run-hooks 'tinymail-:feature-hook)
2212 (tinymail-:message-type
2213 ;; User calls us again
2214 (if (ti::nil-p (ti::mail-get-field-1 "subject"))
2215 (tinymail-field-to-off)))
2217 ;; We're called from some mail setup hook. See what is the initial
2218 ;; state of the buffer...
2220 ((setq to-list (ti::mail-to-list-p))
2221 (setq tinymail-:message-type 'to-list)
2222 (tinymail-field-to-on))
2223 ((ti::nil-p (ti::mail-get-field-1 "subject"))
2224 ;; simple mail: there is no subject field filled
2225 (setq tinymail-:message-type 'simple))
2226 (t ;R or r; No cc field
2227 (setq tinymail-:message-type 'reply)
2228 (tinymail-field-to-on))))))
2229 (tinymail-field-to-move-maybe)
2230 (unless to-list ;; No-op. XEmacs byte compiler silencer
2237 "msg type" tinymail-:message-type
2240 "to" (ti::mail-get-field-1 "to")
2241 "Subject" (ti::mail-get-field-1 "subject")
2243 (buffer-substring (point-min) (point-max))
2246 ;;; ----------------------------------------------------------------------
2248 (defun tinymail-mail-send-to-list ()
2249 "Check if message is being sent to mailing list and Fix CC/To.
2250 This function makes the To to point to mailing list and delete
2251 any CC. Set Gnus group parameter to take use of this feature: (G p
2255 (to-list . \"discussion-list@list.com\")
2257 (when (eq major-mode 'message-mode)
2258 (let* ((fid "tinymail-mail-send-to-list:")
2259 (tofield (mail-fetch-field "To"))
2260 (ccfield (mail-fetch-field "Cc"))
2261 (to (ti::mail-to-list-p))
2263 (car-safe (ti::mail-email-from-string to)))))
2264 (unless fid ;; No-op. XEmacs byte compiler silencer
2266 (tinymail-debug fid "to" to "email" email)
2267 ;; If TO is not in the headers; then this is private reply with
2268 ;; "r". A followup will include TO in To or Ccc field.
2269 (when (and (string-match "^[ \t]*$" (or tofield ""))
2270 (string-match "^[ \t]*$" (or ccfield ""))
2273 ;; (save-restriction
2274 ;; (message-narrow-to-headers)
2275 ;; (not (ti::re-search-check email)))
2276 (not (tinymail-field-off-p "To" to)))
2277 (tinymail-debug fid "TO-LIST SET, killed To/Cc")
2278 (ti::mail-kill-field "^To" to)
2279 (ti::mail-kill-field "^CC")
2282 ;;; ----------------------------------------------------------------------
2284 (defun tinymail-resolve-abbrevs (list)
2285 "Resolves LIST of mail abbrevs in format '(\"abbrav\" \"abbrev\" ..)
2289 ((\"alias\" . \"expansion\") (A . E) .. )
2290 alias = the alias definition
2291 expansion = expanded alias"
2292 (let* ((abbrevs (tinymail-mail-aliases))
2293 pre-abbrev-expand-hook ;; prevent recursion
2297 (tinymail-debug "tinymail-resolve-abbrevs" elt)
2298 ;; Returns (ABBR . ABBR-EXPANDED)
2299 (if (not (setq hit (assoc elt abbrevs)))
2300 (message "TinyMail: Can't find abbrev '%s', is it in ~/.mailrc ?" elt)
2301 (if (not (member hit exp-list))
2302 (push hit exp-list))))
2305 ;;; ----------------------------------------------------------------------
2307 (defun tinymail-password-save (&optional load verb)
2308 "Save passwd completions to file `tinymail-:password-file'. Optionally LOAD.
2309 If that variable is nil, then do nothing. VERB."
2310 (let* ((file tinymail-:password-file)
2311 ;;; (list tinymail-:password-completion-alist)
2312 (list2 tinymail-:password-alist))
2314 (tinymail-debug "tinymail-password-save" file load verb)
2315 (when (stringp file)
2316 (if (string-match "\\.gz$\\|\\.Z$" file)
2317 (ti::use-file-compression))
2320 (if (not (file-exists-p file)) ;Create file then
2321 (tinymail-password-define-variables 'force))
2322 (ti::load-file-with-wrapper file)
2323 (if verb (message "TinyMail: passwd completions loaded.")))
2327 TinyMail: `tinymail-:password-alist' is empty, nothing to save.
2328 Call `tinymail-password-define-variables' with argument FORCE.")
2329 (ti::write-file-variable-state
2330 file "TinyMail.el password completions"
2331 '(tinymail-:password-completion-alist tinymail-:password-alist))
2333 (message "TinyMail: passwd completions saved."))))))))
2335 ;;; ----------------------------------------------------------------------
2337 (defun tinymail-password-define-variables (&optional force no-save)
2338 "Defines passwd variables.
2339 Read definitions from `tinymail-:password-file' if FORCE is nil.
2343 FORCE flag, if non-nil, read passwd table and reset all variables.
2344 NO-SAVE flag, if non-nil, do not save passwd completions to file.
2348 non-nil if password completion can be used.
2352 `tinymail-:password-mode'"
2354 (tinymail-debug "tinymail-password-define-variables"
2357 'passwd-file tinymail-:password-file)
2358 (when (and tinymail-:password-mode
2359 tinymail-:password-cat-cmd)
2361 ;; .................................................... cond-save ...
2363 (and tinymail-:password-file
2364 (not (file-exists-p tinymail-:password-file)))
2365 (and (null tinymail-:password-file)
2366 (null tinymail-:password-alist)))
2367 (message "TinyMail: Buildig password completions...")
2368 (setq tinymail-:password-alist
2369 (ti::file-passwd-build-alist tinymail-:password-cat-cmd))
2370 (message "TinyMail: Buildig password completions...done"))
2371 ;; .................................................... cond-load ...
2372 ((and (file-exists-p tinymail-:password-file)
2373 (null tinymail-:password-alist))
2374 (tinymail-password-save 'load)))
2375 ;; ......................................................... build ...
2377 (null tinymail-:password-completion-alist))
2378 (setq tinymail-:password-completion-alist
2380 (lambda (x) (cons (car x) 1)))
2381 tinymail-:password-alist))
2383 (tinymail-password-save)))
2384 tinymail-:password-completion-alist))
2389 ;;; ----------------------------------------------------------------------
2391 (defun tinymail-complete-password-mode (&optional mode)
2392 "Toggle `tinymail-:password-mode' on or off."
2394 (ti::bool-toggle tinymail-:password-mode mode)
2395 (when (interactive-p)
2396 (message "TinyMail: Password complete mode is now %s"
2397 (if tinymail-:password-mode "on" "off"))))
2399 ;;; ----------------------------------------------------------------------
2401 (defun tinymail-complete-string-read ()
2402 "Return completion string from current point or nil.
2403 The String must be delimited by comma as in mail header are.
2406 (beg-marker end-marker string)"
2407 (let* ((fid "tinymail-complete-string-read")
2409 (heder-p (ti::mail-point-at-header-p))
2413 (unless fid ;; No-op. XEmacs byte compiler silencer
2415 (when (and (not (bolp)) ;Nothing to read
2416 (not (char-equal (char-syntax (preceding-char)) ?\ )))
2417 (ti::narrow-safe (line-beginning-position) (line-end-position)
2419 ;; First, go away from whitespace so that match-end gets
2420 ;; length in next case statement
2421 (skip-chars-forward " \t")
2423 ((or (if (not heder-p)
2424 (skip-chars-backward "^ \t")
2425 (or (re-search-backward ",[ \t]*" nil t)
2426 (and (re-search-backward "^[^:]+:" nil t)
2427 (goto-char (match-end 0)))
2436 (re-search-backward "[:, ][ \t]*" nil t))))
2437 (skip-chars-forward " ,\t") ;Goto word
2438 ;;; (ti::d! 1 (buffer-substring (point) (line-end-position)))
2440 ((re-search-backward "^[ \t]*" nil t)
2441 ;;; (ti::d! 2 (buffer-substring (point) (line-end-position)))
2442 (goto-char (1+ (point)))))
2443 (setq beg-marker (point-marker))
2444 ;;; (ti::d! beg (looking-at "[^\n\t ,:]+") (buffer-substring beg (line-end-position)))
2445 ;; There must be somthing, not just empty lines
2446 (when (looking-at "[^\n\t ,:]+")
2448 ((re-search-forward " *," nil t)
2449 (setq end-marker (make-marker))
2450 (move-marker end-marker (match-beginning 0)))
2451 ((re-search-forward "[ \t]*,\\|[ \t]*$" nil t)
2452 (setq end-marker (make-marker))
2453 (move-marker end-marker (match-beginning 0)))))
2455 (if (and beg-marker end-marker)
2456 (setq string (buffer-substring-no-properties
2457 (marker-position beg-marker)
2458 (marker-position end-marker)))))
2459 (tinymail-debug fid "RET" string beg-marker end-marker)
2461 (setq beg-marker nil ;; Kill possible markers
2468 ;;; ----------------------------------------------------------------------
2470 (defun tinymail-complete-list-mail-aliases (&optional mode data)
2471 "Return '(match match ...) from mail aliases.
2475 The default match is made against all the alias expansion ('string mode).
2476 With 'alias, only the alias names are matched."
2477 (let* ((fid "tinymail-complete-guess-2-choices: ")
2478 (list (tinymail-mail-aliases))
2479 (mail (ti::mail-mail-p))
2484 (unless fid ;; No-op. XEmacs byte compiler silencer
2486 (tinymail-debug fid "in" mode 'mail-p mail)
2488 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... .. get word ...
2490 ;; ... ... ... ... ... ... ... ... ... ... ... ... second mode ..
2493 (when (< (skip-chars-backward "^ \t\n") 0)
2495 (when (> (skip-chars-forward "^ \n\t") 0)
2496 (setq end (point)))))
2498 (setq str (buffer-substring beg end))
2502 (setq data (tinymail-complete-string-read)))
2504 (setq beg (nth 0 data)
2506 str (regexp-quote (nth 2 data))))))
2507 ;; ... ... ... ... ... ... ... ... ... ... ... ... .. find matches ...
2508 ;;#todo: this code must be rewritten, ti::list-find and `function'
2509 ;;is flow combination.
2511 (when (and (not (ti::nil-p str))
2517 (if (eq mode 'string)
2518 (string-match arg (cdr elt))
2519 (string-match (concat "^" arg)
2522 (tinymail-debug fid "after type" beg end str)
2523 (mapcar 'cdr elt)))))
2525 ;;; ----------------------------------------------------------------------
2527 (defun tinymail-complete-guess-1 (&optional mode verb)
2528 "Try to expand using underlying characters.
2529 Look completion from `mail-aliases'. If there is more than 1 match,
2530 ask which one to use.
2532 If MODE is 'string, then text read from buffer must be separated by
2534 LEFT-COLON: txt [COMMA,WHITESPACE]
2535 LEFT-ALL-WHITESPACE txt [COMMA,WHITESPACE]
2536 COMMA txt [COMMA,WHITESPACE]
2538 If MODE is 'alias then text is read direcly under point separated
2539 by spaces. This function does nothing if the first line doesn't contain
2543 Indicating a mail like mode. VERB prints verbose messages.
2549 (let* ((fid "tinymail-complete-guess-2: ")
2550 (list (tinymail-mail-aliases))
2551 (mail (ti::mail-mail-p))
2552 (check-regexp tinymail-:confirm-mailrc-regexp)
2559 (unless fid ;; No-op. XEmacs byte compiler silencer
2561 (tinymail-debug fid "in" mode verb mail)
2564 ;; ... ... ... ... ... ... ... ... ... ... ... ... ... .. get word ...
2567 (setq data (tinymail-complete-string-read))
2569 (setq beg (nth 0 data)
2571 str (regexp-quote (nth 2 data)))))
2572 ;; ... ... ... ... ... ... ... ... ... ... ... ... second mode ..
2575 (when (< (skip-chars-backward "^ \t\n") 0)
2576 (setq beg (make-marker))
2577 (when (> (skip-chars-forward "^ \n\t") 0)
2578 (setq end (make-marker)))))
2580 (setq str (buffer-substring
2581 (marker-position beg)
2582 (marker-position end)))
2584 (tinymail-debug fid "after type" beg end str)
2585 ;; ... ... ... ... ... ... ... ... ... ... ... ... .. find matches ...
2586 (when (and (not (ti::nil-p str))
2592 (if (eq mode 'string)
2593 (string-match arg (cdr elt))
2594 (string-match (concat "^" arg)
2598 (tinymail-debug fid "ELT matches" (length elt) (cdr (car elt)) elt)
2599 ;; ............................................... any matches ...
2600 ;; How many matches?
2602 ((eq 1 (length elt))
2603 (setq elt (car elt))) ; '( (alias . string) ) --> (a . s)
2605 (let (completion-ignore-case)
2608 (format "%d Choose: " (length elt))
2609 (ti::list-to-assoc-menu (mapcar 'cdr elt))
2614 (setq elt nil) ;User didn't select anything
2615 (setq user-selected-p t
2616 elt (rassoc str elt)
2618 (tinymail-debug fid "ELT" elt)
2619 ;; .............................................. select match ...
2620 ;; Now we have a MATCH unless user cancelled the choices
2622 (if (eq mode 'string)
2623 (setq str (cdr elt))
2624 (setq str (car elt))))
2625 (tinymail-debug fid "SELECTION" mode str elt)
2626 (unless (ti::nil-p str)
2627 ;; For some strings, ask confirmation.
2628 ;; Ie. Give a chance to discard this completions and move on...
2631 "CHECK" check-regexp
2632 (string-match check-regexp str))
2633 ;; *) If user already did selected this match from several
2634 ;; choices, then go ahead
2635 ;; *) If we found only one match, then confirm that match
2637 (when (or user-selected-p
2638 (not (stringp check-regexp))
2639 (or (null (string-match check-regexp str))
2640 (and (string-match check-regexp str)
2641 (tinymail-y-or-n-p (concat "TinyMail: " str)))))
2642 (goto-char (marker-position beg))
2643 (delete-region (marker-position beg) (marker-position end))
2644 (setq beg nil end nil) ;; Kill markers
2646 (setq done t ret t)))) ;; when-nil-var
2648 ((and verb (null done) str)
2649 (message (format "TinyMail: no completion match on '%s'" str)))
2650 ((and verb str done (eq mode 'alias))
2651 (message (cdr (car elt)))))
2652 (tinymail-debug fid "RET" ret)
2655 ;;; ----------------------------------------------------------------------
2657 (defun tinymail-complete-passwd (&optional force verb)
2658 "Complete names in passwd in header area, otw do nothing.
2662 FORCE Complete anyway
2663 VERB enable verbose messages.
2670 (let* ((fid "tinymail-complete-passwd")
2671 (header-p (< (point) (ti::mail-hmax) ))
2677 (unless fid ;; No-op. XEmacs byte compiler silencer
2681 (forward-char -1) ;move over some char
2682 (setq word (ti::buffer-read-word "[-_+a-zA-Z0-9]" 'strict)))
2683 (when (and (or force header-p)
2684 (not (ti::nil-p word))
2685 (tinymail-password-define-variables))
2686 (setq table tinymail-:password-completion-alist)
2687 (setq completions (all-completions word table))
2688 (tinymail-debug fid "COMPLETIONS" word completions)
2690 ((eq 1 (length completions))
2691 (setq str (car completions))
2692 (if (null (tinymail-y-or-n-p (format "Accept Passwd match: %s " str)))
2693 (tinymail-y-or-n-p-abort-p)
2694 ;; We only insert the missing part to the buffer.
2696 ;; * if tab was pressed after abc
2697 (insert (substring (car completions) (length word)))
2699 ((setq completions (tinymail-password-grep word 'verb))
2700 (tinymail-display-list completions)
2701 ;; Show the matched entries from passwd table, sometimes
2702 ;; User doens't want to use them but continue calling other
2703 ;; functions. Ask what's up.
2707 "TinyMail: Continue calling more completion functions?"))))))
2708 (tinymail-debug fid "RET" ret word)
2711 ;;; ----------------------------------------------------------------------
2713 (defun tinymail-complete-guess (&optional verb)
2714 "Complete using .mailrc and passwd.
2715 Optional VERB allows displaying messages.
2719 The completion type is determined by variable `tinymail-:complete-mode',
2720 which can be 'alias or 'string
2722 This function is part of the other completion possibilities run by
2723 `tinymail-complete-key' and installed in `tinymail-:complete-key-hook'.
2727 non-nil Completion handled
2730 (let ((mode tinymail-:complete-mode)
2731 (pmode tinymail-:password-mode)
2736 (and (null (setq ret (tinymail-complete-guess-1 'alias verb)))
2738 (setq ret (tinymail-complete-passwd nil verb))))
2740 (and (null (setq ret (tinymail-complete-guess-1 'string verb)))
2742 (tinymail-field-in-to-cc-p)
2743 (setq ret (tinymail-complete-passwd nil verb))))
2745 (error "TinyMail: Unknown mode %s" mode)))
2746 (tinymail-debug "tinymail-complete-guess" mode "PASS-MODE" pmode "RET" ret)
2749 ;;; ----------------------------------------------------------------------
2751 (defun tinymail-complete-everything (&optional verb)
2752 "Gather list of possible completions and let user choose."
2754 (let ((data (tinymail-complete-string-read)))
2756 ;; It doesn't make sense to search items that already
2757 ;; look like email, this@here.com
2758 (not (string-match "@" (nth 2 data))))
2759 (let* ((fid "tinymail-complete-everything:")
2760 (check-regexp tinymail-:confirm-mailrc-regexp)
2761 (mode tinymail-:complete-mode)
2769 (unless fid ;; No-op. XEmacs byte compiler silencer
2771 ;; .......................................... clean duplicates ...
2772 (dolist (results (list
2773 (tinymail-complete-list-mail-aliases mode data)
2774 (tinymail-complete-list-bbdb mode data)
2775 (tinymail-complete-list-passwd mode data)))
2776 (dolist (address results)
2777 (pushnew address matches :test 'string=)))
2778 ;; ............................................... any matches ...
2779 ;; How many matches?
2781 ((eq 1 (length matches))
2782 (setq choice (car matches)))
2786 (format "%d Choose: " (length matches))
2787 (ti::list-to-assoc-menu matches)
2790 (if (ti::nil-p choice)
2792 (setq user-selected-p t
2794 (tinymail-debug fid "CHOICE" choice)
2795 ;; .............................................. select match ...
2796 ;; Now we have a MATCH unless user cancelled the choices
2798 (tinymail-debug fid 'mode mode "CHOICE" choice)
2799 ;; For some strings, ask confirmation.
2800 ;; Ie. Give a chance to discard this completions and move on...
2802 "CHECK" check-regexp
2803 (string-match check-regexp choice))
2804 ;; *) If user already did selected this match from several
2805 ;; choices, then go ahead
2806 ;; *) If we found only one match, then confirm that match
2807 (when (or user-selected-p
2808 (not (stringp check-regexp))
2809 (or (null (string-match check-regexp choice))
2810 (and (string-match check-regexp choice)
2811 (tinymail-y-or-n-p (concat "TinyMail: " choice)))))
2812 (goto-char (marker-position beg))
2813 (delete-region (marker-position beg) (marker-position end))
2814 (setq beg nil end nil) ;; Kill markers.
2816 (setq done t ret t)))
2817 (if (and verb (null done) choice)
2818 (message (format "TinyMail: no completion match on '%s'" choice)))
2819 (tinymail-debug fid "RET" ret)
2822 ;;; ----------------------------------------------------------------------
2824 (defun tinymail-complete-guess-in-headers (&optional arg)
2825 "Like `tinymail-complete-guess', but complete only in headers. Ignore ARG."
2827 (ti::mail-point-in-header-macro
2828 (when (ti::mail-field-email-address-p)
2829 (tinymail-debug 'tinymail-complete-guess-in-headers
2830 'ARG arg 'MODE major-mode 'POINT (point))
2831 (tinymail-complete-everything))))
2833 ;;; ----------------------------------------------------------------------
2835 (defun tinymail-complete-guess-in-body (&optional arg)
2836 "Like `tinymail-complete-guess', but complete only in body. Ignore ARG."
2838 (when (>(point) (ti::mail-hmax))
2839 (let* ((fid "tinymail-complete-guess-in-body")
2840 (hook tinymail-:complete-body-hook)
2841 (data (tinymail-complete-string-read))
2843 (unless fid ;; No-op. XEmacs byte compiler silencer
2845 (tinymail-debug 'tinymail-complete-guess-in-body
2852 (tinymail-debug fid 'FUNC func)
2854 ((not (fboundp func))
2855 (tinymail-debug fid 'FUNC func "not exist")
2858 (funcall func data)))
2863 ;;; ----------------------------------------------------------------------
2865 (defun tinymail-complete-bbdb (&rest args)
2866 "Call bbdb-complete-name' if is bbdb loaded and ignore ARGS."
2867 (when (and (fboundp 'bbdb-complete-name)
2868 (tinymail-field-in-to-cc-p))
2869 (let* ((point (point)))
2870 (call-interactively 'bbdb-complete-name)
2871 (if (eq (point) point)
2872 nil ;Point not moved, not completed
2873 ;; point moved, completed
2876 ;;; ----------------------------------------------------------------------
2878 (defun tinymail-bbdb-parse-to-string ()
2879 "Parse BBDB to a fast search format."
2885 (lambda (sym &optional symbol val name notes)
2886 (setq symbol (symbol-name sym))
2887 (setq record (bbdb-gethash symbol))
2888 (if (and (listp record)
2889 (vectorp (setq tmp (car-safe record))))
2892 (setq name (bbdb-record-name record))
2894 (setq str (concat str (format "\C-m%s\C-j%s"
2896 (prin1-to-string record)))))))
2900 ;;; ----------------------------------------------------------------------
2902 (defsubst tinymail-bbdb-record-fix (record)
2903 "Fix BBDB RECORD to pure vector.
2904 Upgrading from v3 to v5 BBDB database, the
2905 entries are returned as ([ ... ]) by
2906 bbdb-gethash, but this format is not suitable for
2907 calling (bbdb-record-net record)
2909 The code below removes the extra () and only
2910 leaves RECORD [ .. ]."
2912 (if (and (listp record)
2913 (vectorp (setq tmp (car-safe record))))
2917 ;;; ----------------------------------------------------------------------
2919 (defun tinymail-bbdb-data-read ()
2920 "Read user information based on current line in `bbdb-file'."
2921 (let* ((fid "tinymail-bbdb-data-read:")
2924 tinymail-:complete-bbdb-case-fold-search)
2929 (unless fid ;; No-op. XEmacs byte compiler silencer
2932 ;; ["Jack E." "Den" nil nil nil
2935 (when (looking-at "^.\"\\([^\n\r\"]+\\)[ \t\"]+\\([^ \t\"]+\\)")
2936 (setq one (match-string 1)
2937 two (match-string 2))
2938 (if (string= one "nil")
2940 (if (string= two "nil")
2944 (setq key (format "%s %s" one two)))
2949 (setq record (bbdb-gethash (downcase key))))
2950 (goto-char point) ;; faster than save-excursion
2952 (tinymail-debug fid one two 'key key '=> record))
2955 ;;; ----------------------------------------------------------------------
2957 (defun tinymail-bbdb-record-net-completions (record)
2958 "Construct email completions for RECORD."
2959 (let* ((fid "tinymail-bbdb-record-net-completions:")
2964 (unless fid ;; No-op. XEmacs byte compiler silencer
2966 (dolist (net (bbdb-record-net record))
2967 (when (and (stringp net)
2968 (string-match "@" net))
2970 ;; If user has given a custom name to a NET,
2971 ;; Like: Customer Support -- Phone Number <@>
2972 ;; Then use that. Otherwise
2973 ;; combine name and plain address
2974 (if (string-match "[<>]" net)
2976 (setq name (bbdb-record-name record))
2977 ;; DO NOT ADD name "John doe" if address already
2978 ;; has those in john.doe@some.com because it looks
2979 ;; funny to read "john doe john.doeEMAIL" multiple times
2983 (split-string name) net))
2984 (if (and (setq tmp (split-string name))
2986 (string-match (regexp-quote (nth 0 tmp)) net)
2987 (string-match (regexp-quote (nth 1 tmp)) net))
2989 (format "%s <%s>" name net))))
2990 (push completion list)))
2992 (tinymail-debug fid "\n\t" 'RET list))
2995 ;;; ----------------------------------------------------------------------
2996 ;;; Switched to another implementation (2). Read the matches
2997 ;;; directly from the BBDB data buffer, because it is faster than reading
2998 ;;; with `mapatoms' => obarray.
3000 (defun tinymail-complete-list-bbdb-2 (regexp &optional check)
3001 "Return list of strings that match REGEXP in BBDB hash table.
3005 REGEXP Regexp to match for mail fields
3006 CHECK See `tinymail-:complete-bbdb-fuzzy-method'."
3007 (let ((fid "tinymail-complete-list-bbdb-2: ")
3012 (unless fid ;; No-op. XEmacs byte compiler silencer
3014 ;; The BBDB intrface code is filled with condition statements:
3016 ;; (if tinymail-:debug
3019 ;; This prevents function call to happen, so that
3020 ;; the BBDB interface is as fast as possible.
3021 (when (and (featurep 'bbdb)
3022 (setq buffer (find-buffer-visiting bbdb-file)))
3023 (with-current-buffer buffer
3024 ;; Don't want to see text properties in this buffer.
3025 (if (and (boundp 'font-lock-mode)
3026 (symbol-value 'font-lock-mode))
3027 (font-lock-mode -1))
3029 (while (re-search-forward regexp nil t)
3032 'found (ti::buffer-read-space-word)
3033 (ti::read-current-line)
3035 (when (setq record (tinymail-bbdb-data-read))
3036 (setq record (tinymail-bbdb-record-fix record))
3037 ;; ......................................... field match ...
3040 (dolist (func check)
3041 (when (and (functionp func)
3042 (setq str (funcall func record))
3045 (string-match regexp str))
3047 ;; '((field . "str") ..)
3048 (ti::consp (car-safe str)))
3050 (setq elt (cdr elt))
3051 (when (and (stringp elt)
3052 (string-match regexp elt))
3055 (stringp (car-safe str)))
3057 (when (string-match regexp s)
3060 (tinymail-debug fid 'MATCH regexp func str))
3062 ;; .................................... make completions ...
3063 (dolist (elt (inline
3064 (tinymail-bbdb-record-net-completions
3066 ;; Previously used `pushnew' to to remove duplicates.
3067 ;; push is faster. See `tinymail-complete-everything'
3069 ;; (pushnew elt list :test 'string=)
3073 (tinymail-debug fid 'RET list))
3076 ;;; ----------------------------------------------------------------------
3078 (defun tinymail-complete-list-bbdb-1 (regexp &optional fields)
3079 "Return list of strings that match REGEXP and @ in BBDB hash table.
3083 REGEXP Regexp to match for mail fields
3084 FIELDS See `tinymail-:complete-bbdb-fuzzy-method'."
3085 (let ((fid "tinymail-complete-list-bbdb-1: ")
3090 (unless fid ;; No-op. XEmacs byte compiler silencer
3092 (when (featurep 'bbdb)
3095 (lambda (sym &optional symbol val name notes)
3096 (setq symbol (symbol-name sym))
3097 ;; Look at all atoms in BBDB and try to find email addresses
3098 ;; that have string that would match.
3099 (setq record (bbdb-gethash symbol))
3100 ;; NOTE: upgrading from v3 to v5 BBDB database, the
3101 ;; entries are returned as ([ ... ]) by
3102 ;; bbdb-gethash, but this format is not suitable for
3103 ;; calling (bbdb-record-net record)
3105 ;; The code below removes the extra () and only
3106 ;; leaves RECORD [ .. ]
3107 (if (and (listp record)
3108 (vectorp (setq tmp (car-safe record))))
3111 (setq name (bbdb-record-name record)
3112 notes (bbdb-record-notes record)))
3113 ;; .......................................... select record ...
3115 ;; If ANYTHING has been set:
3116 ;; -- Compare element in BBDB if it is string
3117 ;; -- Require at least 3 characters to compare
3118 ;; (it makes no sense to complete one character "a")
3119 ;; -- Match the element
3120 ;; ["John" "Doe" nil nil nil nil ("jdoe@example.com")
3121 ;; ((creation-date . "2000-09-09") (timestamp . "2000-09-09")
3122 ;; (notes . "that.el"))
3123 ;; ["John Doe" nil #<marker at 114932 in bbdb-data.el> nil]])
3124 (when (and fields record)
3125 ;; #todo: Is there function to `dolist' over vector list?
3127 (len (if (integerp fields)
3130 (max (1- (length record)))
3132 (when (>= (length regexp) len)
3134 (setq elt (aref record i))
3136 (if (not (listp elt))
3137 (setq elt (list elt)))
3139 ;; Try CDR: (notes . "value")
3140 ;; or CAR: ("string")
3142 (setq item (or (cdr-safe item)
3144 (when (and (stringp item)
3145 (string-match regexp item))
3146 (tinymail-debug fid 'ANYTHING regexp elt)
3149 ;; ..................................... make completions ...
3152 (prog1 t (tinymail-debug fid 'BBDB-SCAN record))
3154 ;; If there is case sensitive search in effect, check that,
3155 ;; before adding to completion list
3157 (let ((case-fold-search
3158 tinymail-:complete-bbdb-case-fold-search))
3159 (or (string-match regexp symbol)
3160 (string-match regexp name)
3161 (string-match regexp (or notes ""))))
3162 (setq val (bbdb-record-net record)))
3172 (when (and (stringp net)
3173 (string-match "@" net))
3175 ;; If user has given a custom name to a NET,
3176 ;; Like: Customer Support -- Phone Number <@>
3177 ;; Then use that. Otherwise
3178 ;; combine name and plain address
3179 (if (string-match "[<>]" net)
3181 ;; DO NOT ADD name "John doe" if address already
3182 ;; has those in john.doe@some.com because it looks
3183 ;; funny to read NAME EMAIL multiple times
3186 (split-string name) net)
3187 (if (and (setq tmp (split-string name))
3189 (string-match (regexp-quote (nth 0 tmp)) net)
3190 (string-match (regexp-quote (nth 1 tmp)) net))
3192 (format "%s <%s>" name net))))
3193 (pushnew completion list :test 'string=)))) ;; When-end
3197 (tinymail-debug fid 'RETURN-COMPLETIONS list)
3200 ;;; ----------------------------------------------------------------------
3202 (defun tinymail-complete-list-bbdb (mode data)
3203 "Return list of matches from BBDB.
3207 MODE is the value of `tinymail-:complete-mode'.
3208 DATA can contain values returned from `tinymail-complete-string-read'."
3210 (setq data (tinymail-complete-string-read)))
3211 (setq data (regexp-quote (nth 2 data)))
3212 (tinymail-complete-list-bbdb-2
3214 tinymail-:complete-bbdb-fuzzy-method)))
3216 ;;; ----------------------------------------------------------------------
3218 (defun tinymail-complete-bbdb-fuzzy (&optional info &optional force)
3219 "Scan through BBDB 'net for partial matches and offer completion list.
3223 INFO '(beg end STRING) of the completion word
3224 FORCE Normally this function completes only in Header To/Cc fields< but if
3225 this is non-nil, complete at point."
3226 (when (and (featurep 'bbdb)
3227 (or force (tinymail-field-in-to-cc-p))
3228 (eq tinymail-:complete-mode 'string))
3229 (tinymail-debug 'tinymail-complete-bbdb-fuzzy info 'FORCE force)
3230 (let* ((fid "tinymail-complete-bbdb-fuzzy:")
3231 (string (nth 2 info))
3233 (tinymail-complete-list-bbdb-2
3234 (regexp-quote string)))))
3235 (unless fid ;; No-op. XEmacs byte compiler silencer
3237 (tinymail-debug fid 'LIST list 'STRING string)
3241 ((eq 1 (length list))
3242 (if (tinymail-y-or-n-p (concat "TinyMail bbdb accept: " (car list)))
3243 (tinymail-complete-insert-completion (car list) info)
3244 (tinymail-y-or-n-p-abort-p)))
3246 (setq string (completing-read
3247 (format "TinyMail bbdb fuzzy %d (empty to cancel): "
3249 (ti::list-to-assoc-menu list)))
3250 (unless (ti::nil-p string)
3251 (tinymail-complete-insert-completion string info)
3254 ;;; ----------------------------------------------------------------------
3256 (defun tinymail-complete-bbdb-fuzzy-at-point (info)
3257 "Call tinymail-complete-bbdb-fuzzy with INFO and `FORCE' argument."
3258 (tinymail-complete-bbdb-fuzzy info 'force))
3260 ;;; ----------------------------------------------------------------------
3262 (defun tinymail-complete-insert-completion (string info)
3263 "Replace the content with STRING by using the INFO.
3264 INFO contains list '(begin-point end-point text-between-points)."
3266 (delete-region (nth 0 info) (nth 1 info))
3268 (skip-chars-backward " ")
3271 ;;; ----------------------------------------------------------------------
3273 (defun tinymail-header-complete-choices (field)
3274 "Return completion choices for HEADER-FIELD."
3275 (let* ((fid "tinymail-header-complete-choices:")
3276 (ret (nth 1 (assoc field tinymail-:table-header-complete))))
3277 (unless fid ;; No-op. XEmacs byte compiler silencer
3279 (tinymail-debug fid 'CHOICES-RAW ret)
3280 ;; If the first element is string, then suppose list of strings
3281 ;; If not, evaluate `choices' to get list of strings.
3283 (if (not (stringp (car ret)))
3284 (setq ret (eval ret))))
3285 (tinymail-debug fid 'CHOICES-FINAL ret)
3288 ;;; ----------------------------------------------------------------------
3290 (defun tinymail-complete-simple (&optional info)
3291 "Complete according to `tinymail-:table-header-complete'.
3292 INFO is '(string beg end) of the completion word"
3294 (ti::mail-point-in-header-macro
3295 (let* ((fid "tinymail-complete-simple: ")
3296 (field-1 (ti::remove-properties (ti::mail-current-field-name)))
3298 (capitalize field-1))) ;; gcc -> Gcc
3299 (field-info (or info
3300 (tinymail-complete-string-read)))
3307 (unless fid ;; No-op. XEmacs byte compiler silencer
3309 ;; The EVAL-FORM may set this if it does not return `choices'
3310 (setq tinymail-:complete-key-return-value nil)
3311 ;; The STRING is dynamically bound and visible for EVAL CHOICES
3312 (when (stringp (setq string (nth 2 field-info)))
3313 (setq choices (tinymail-header-complete-choices field)))
3319 ;; ............................................... check choices ...
3322 ((null string) ;; Empty field, user expects all completions
3323 (setq string (completing-read
3325 (ti::list-to-assoc-menu choices)))
3326 (unless (ti::nil-p string)
3330 (setq choices (ti::list-to-assoc-menu choices))
3331 ;; Forget choices that are multiwords "val val"
3332 (unless (string-match " " string)
3333 (setq complete-list (all-completions string choices))
3334 ;; This is the common string at the beginning
3335 (setq tmp (try-completion string choices))
3337 'COMPLETE-LIST complete-list
3340 (dolist (completion complete-list)
3341 (when (string-match " " completion)
3344 ;; ....................................... completion-list ...
3346 ((null complete-list)
3347 (message "TinyMail: no simple completions matching `%s'" string))
3348 ((or (and (eq 1 (length complete-list)) ;; ONE found
3349 (setq string (car complete-list))) ;; that's it
3351 ;; Don't accept partial match from "Multi Word"
3352 ;; completion strings.
3358 (ti::list-to-assoc-menu complete-list)
3363 (tinymail-complete-insert-completion string info)
3366 ((and tmp (not (string= tmp string)))
3367 ;; there was common denominator, complete further
3368 (tinymail-complete-insert-completion tmp info)
3369 (message "Tinymail complete: %s"
3370 (ti::list-to-string complete-list ", " ))
3374 (setq ret (completing-read
3376 (ti::list-to-assoc-menu complete-list)
3380 (unless (ti::nil-p ret)
3381 (tinymail-complete-insert-completion ret info)))
3382 ;; More than 1, stop and return t
3386 "GLOBAL COMPLETE VALUE"
3387 tinymail-:complete-key-return-value)
3388 ;; Return status if we did something in this function
3390 tinymail-:complete-key-return-value))))
3392 ;;; ----------------------------------------------------------------------
3394 (defun tinymail-complete-guest-packages (&optional arg)
3395 "Support minor modes like tinytab and tinyindent which also use TAB key.
3398 (let* ((fid "tinymail-complete-guest-packages:")
3399 (ch last-command-char))
3400 (unless fid ;; No-op. XEmacs byte compiler silencer
3402 (tinymail-debug fid 'ARG arg 'MODE major-mode 'POINT (point))
3403 ;; The TinyTab minor mode overrides tab, return nil
3404 ;; so that it can proceed
3406 ((and (featurep 'tinytab)
3407 (symbol-value 'tinytab-mode)
3408 (fboundp 'tinytab-tab-key))
3409 (tinymail-debug fid 'tinytab-tab-key tinytab-:tab-insert-hook)
3410 (ti::funcall 'tinytab-tab-key))
3411 ((and (featurep 'tinyindent)
3412 (symbol-value 'tinyindent-mode)
3413 (fboundp 'tinyindent-tab-key))
3414 (tinymail-debug fid 'tinyindent-tab-key)
3415 (ti::funcall 'tinyindent-tab-key))
3418 (self-insert-command 1)
3421 ;;; ----------------------------------------------------------------------
3423 (defun tinymail-complete-list-passwd (&optional mode data force)
3424 "Return list of matches from password file.
3428 MODE is the value of `tinymail-:complete-mode'.
3429 DATA can contain values returned from `tinymail-complete-string-read'."
3430 (let* ((fid "tinymail-complete-list-passwd")
3434 (unless fid ;; No-op. XEmacs byte compiler silencer
3437 (setq data (tinymail-complete-string-read)))
3438 (setq str (regexp-quote (nth 2 data))))
3440 (null tinymail-:password-completion-alist))
3441 (tinymail-password-define-variables))
3442 (setq table tinymail-:password-completion-alist)
3443 (setq completions (all-completions str table))
3444 (tinymail-debug fid str "COMPLETIONS" completions)
3447 ;;; ----------------------------------------------------------------------
3449 (defun tinymail-display-list (list &optional flash)
3450 "Display LIST or alist in `tinymail-:temp-buffer' or FLASH in echo area."
3453 (message (ti::list-to-string (mapcar 'car list)))
3454 (let* ((buffer (ti::temp-buffer tinymail-:temp-buffer 'clear)))
3455 (with-current-buffer buffer
3457 (insert (format "%-10s %s\n" (car elt) (or (cdr elt) "")))))
3458 (display-buffer buffer)
3459 (ti::save-excursion-macro ;; Go and make displayed buffer small
3460 (select-window (get-buffer-window buffer))
3461 (shrink-window-if-larger-than-buffer))))))
3463 ;;; ----------------------------------------------------------------------
3465 (defun tinymail-password-grep (match &optional verb)
3466 "Grep USER from passwd.
3470 MATCH String, to grep
3471 DISPLAY flag, display results in separate buffer.
3472 VERB flag, Verbose messages"
3473 (interactive "sUser regexp: ")
3474 (let ((fid "tinymail-password-grep")
3476 (unless fid ;; No-op. XEmacs byte compiler silencer
3479 (tinymail-debug fid match verb)
3480 (if (null tinymail-:password-alist)
3481 (tinymail-password-define-variables))
3482 ;; Force loading it if not exist
3483 (if verb (message "Grepping passwd contents..."))
3484 (setq alist (ti::file-passwd-grep-user-alist
3485 match nil tinymail-:password-alist))
3486 (if verb (message "Grepping...done"))
3489 ;;; ----------------------------------------------------------------------
3491 (defun tinymail-complete-headers-move-to-next-field (&rest ignore)
3492 "Move to next field if cursor is at the end of field in header."
3494 (ti::mail-point-in-header-macro
3495 (let* ((str (buffer-substring (line-beginning-position) (point)))
3496 (max (ti::mail-text-start)))
3497 (when (and (not (ti::nil-p str))
3499 (when (re-search-forward ":." max t)
3502 ;;; ----------------------------------------------------------------------
3504 (defun tinymail-complete-headers-nothing-found (&rest ignore)
3505 "Display 'No completions found' in header and return t. IGNORE arguments.
3506 Advance by 4 spaces if there is only spaces to the left."
3508 (ti::mail-point-in-header-macro
3509 ;; User started a continuing line. Point is at mark (!)
3511 ;; CC: some@example.com
3516 ((or (ti::nil-p (buffer-substring (line-beginning-position) (point)))
3517 (char-equal (char-syntax (preceding-char)) ?\ ))
3519 ((ti::mail-point-at-header-p)
3520 ;; this message is displayed only when cursor is next to character
3522 (message "TinyMail: No completions found.")
3526 ;;; ----------------------------------------------------------------------
3528 (defun tinymail-complete-abbrevs (&optional info)
3529 "Complete using abbrevs. INFO."
3530 ;; Actually we don't need this because SPACE already expands abbrevs
3531 ;; if abbrev mode is on.
3535 ;;; ----------------------------------------------------------------------
3537 (defun tinymail-complete-key-remove-itself ()
3538 "Remove calls from `tinytab-:tab-insert-hook'. See
3539 Source code of `tinymail-complete-key' why. "
3540 ;; In calling function this variable is `let' bound, so the
3541 ;; change is temporary.
3543 (when (boundp 'tinytab-:tab-insert-hook)
3544 (dolist (function tinytab-:tab-insert-hook)
3545 (if (not (string-match "tinymail"
3546 (or (symbol-name function) "")))
3547 (push function clean-hook)))
3548 (setq tinytab-:tab-insert-hook (nreverse clean-hook)))))
3550 ;;; ----------------------------------------------------------------------
3552 (defun tinymail-tab-to-tab-stop (&rest args)
3553 "Ignore ARGS and call `tab-to-tab-stop'."
3556 ;;; ----------------------------------------------------------------------
3558 (defun tinymail-complete-key (&optional header-check)
3559 "Run functions in `tinymail-:complete-key-hook'.
3560 Te first function that return non-nil terminates calling the rest of the
3561 functions. Each function is passed the word info at point: '(BEG END STRING)."
3563 (tinymail-debug 'tinymail-complete-key
3565 tinymail-:complete-key-hook)
3566 ;; It makes no use to call this function anywhere elase than in Mail
3567 ;; buffer. (this prevent's double call from tinytab.el too)
3568 (when (ti::mail-mail-p)
3569 (tinymail-debug 'tinymail-complete-key
3570 'HEADER-CHECK header-check
3572 (concat "\n************ START **********\n"
3574 (progn (forward-line -2) (point))
3575 (progn (forward-line 2) (point)))
3576 "\n************ END **********\n")))
3577 (let* ((fid "tinymail-complete-key:")
3578 ;; Make copies of these
3579 (tinytab-:tab-insert-hook
3580 (if (boundp 'tinytab-:tab-insert-hook)
3581 (symbol-value 'tinytab-:tab-insert-hook)))
3582 (tinymail-:complete-key-hook tinymail-:complete-key-hook)
3585 (unless fid ;; No-op. XEmacs byte compiler silencer
3587 ;; Avoid resursive calls by removing all tinymail entries
3588 (tinymail-complete-key-remove-itself)
3589 ;; It doesn't make sense to run mail completions inside BODY,
3590 ;; remove unnecessary hooks
3592 ((ti::mail-point-at-body-p)
3593 (tinymail-debug 'tinymail-complete-key 'POINT-INSIDE-BODY)
3596 ;; tinymail-complete-abbrevs
3597 ;; tinymail-complete-guest-packages
3598 (ti::add-hooks 'tinymail-:complete-key-hook
3599 '(tinymail-complete-everything
3600 tinymail-complete-simple
3601 tinymail-complete-guess-in-headers
3602 tinymail-complete-headers-nothing-found
3603 tinymail-complete-headers-move-to-next-field)
3606 ((and (boundp 'tinytab-mode)
3608 (fboundp 'tinytab-indent-by-tab-width))
3609 (add-hook 'tinymail-:complete-key-hook
3610 'tinytab-indent-by-tab-width))
3612 (add-hook 'tinymail-:complete-key-hook
3613 'tinymail-tab-to-tab-stop)))
3614 (tinymail-debug fid 'BODY-AREA tinymail-:complete-key-hook))
3616 (tinymail-debug 'tinymail-complete-key 'POINT-INSIDE-HEADER)
3617 (setq string (tinymail-complete-string-read))
3618 ;; Complete only in CC, Bcc, To .. fields. If not there,
3620 (when (and header-check
3621 (not (ti::mail-field-email-address-p)))
3622 (tinymail-debug 'tinymail-complete-key 'NOT-IN-TO-CC-BCC)
3623 (tinymail-debug fid 'header-check 'REMOVED
3624 'tinymail-complete-everything)
3625 (remove-hook 'tinymail-:complete-key-hook
3626 'tinymail-complete-everything))))
3627 ;; .................................................... cond-end ...
3628 (tinymail-debug fid 'LOOPING-LIST tinymail-:complete-key-hook)
3629 (dolist (func tinymail-:complete-key-hook)
3630 (tinymail-debug fid 'FUNC func string)
3632 ((not (fboundp func))
3633 (tinymail-debug fid 'FUNC func "not exist")
3636 (funcall func string)))
3639 (tinymail-debug fid fid 'RET ret)
3642 ;;; ----------------------------------------------------------------------
3644 (defun tinymail-complete-key-interactive ()
3645 "See `tinymail-complete-key'. Comlete only in header."
3647 (tinymail-complete-key 'only-complete-in-headers))
3652 ;;; .......................................................... &advice ...
3654 ;; Old message has autosave name "*message*", but that does not work in
3655 ;; Win32 platform (C-x m M-x message-mode and C-x s and Emacs
3663 (symbol-function 'message-set-auto-save-file-name)))
3664 (defadvice message-set-auto-save-file-name (around tinymail act)
3666 Replace function. Change the autosave name from *message* to #message# due to Win32"
3667 (when message-auto-save-directory
3669 (setq message-draft-article
3670 (nndraft-request-associate-buffer "drafts"))
3671 (setq buffer-file-name
3672 (expand-file-name "#message#"
3673 message-auto-save-directory))
3674 (setq buffer-auto-save-file-name (make-auto-save-file-name)))
3675 (clear-visited-file-modtime)
3676 (setq buffer-file-coding-system message-draft-coding-system)))))
3681 ;;; ----------------------------------------------------------------------
3683 (defun tinymail-save-dead-mail-maybe ()
3684 "Call `tinymail-save-dead-mail' only if RMAIL is used as MUA.
3685 All other Agents have some sort of 'todo' message save feature."
3686 ;; VM after sending, keeps the corresponding mail
3687 ;; buffer which implies that the dead letter facility
3689 ;; Gnus has also Gcc feature; but we can't know if User uses it for mail?
3690 ;; User may only read News.
3691 (when (featurep 'rmail)
3692 (tinymail-save-dead-mail)))
3694 ;;; ----------------------------------------------------------------------
3696 (defun tinymail-save-dead-mail ()
3697 "Save mail buffers to `tinymail-:dead-mail-file' on Emacs exit."
3698 (ti::dolist-buffer-list
3699 (memq major-mode '(mail-mode
3704 ;; In message.el is possible to "save a draft" in normal manner:
3705 ;; C-x C-s. If the mail buffer has already been saved, we ignore
3707 (when (buffer-modified-p)
3708 (set-buffer-modified-p nil) ;"no changes in this buffer"
3712 tinymail-:dead-mail-file)))))
3716 ;;{{{ Email notification (old Dragbar Time package)
3718 ;;; ...................................................... &reportmail ...
3720 ;;; ----------------------------------------------------------------------
3722 (defun tinymail-report-get-email-word (str)
3723 "Return first word, separated by space from STR."
3724 (let* ((word str)) ;set default
3725 (when (string-match "From \\([^ ]+\\) " str)
3726 (setq word (substring str (match-beginning 1) (match-end 1))))
3729 ;;; ----------------------------------------------------------------------
3731 (defun tinymail-report-break-email (str)
3732 "Break email STR into two words.
3734 (ACCOUNT SITE) or nil"
3738 (when (string-match "[@!]" str)
3739 (setq w1 (substring str 0 (match-beginning 0))
3740 w2 (substring str (1+ (match-beginning 0))))
3741 ;; Some sites has "from" command that sends the info in format:
3742 ;; "From site.com!login Mon Feb 26 15:50:18 1996"
3744 ;; And not in traditional format
3745 ;; "From login@site.com Mon Feb 26 15:50:18 1996"
3747 ;; We have to swap the order
3749 ((string-match "!" str)
3750 ;; then swap order, since word1 = site, w2 = account
3751 (setq ret (list w2 w1)))
3753 (setq ret (list w1 w2)))))
3756 ;;; ----------------------------------------------------------------------
3758 (defun tinymail-report-mail-info-1 (shell-call)
3759 "Run SHELL-CALL to get information about arrived mail.
3763 SHELL-CALL If string, run `shell-command'.
3765 If function, call function with no arguments.
3769 The SHELL-CALL must return Mailbox From information
3770 to current empty buffer. Oldest entries first, newest last.
3774 list (line line ..) Berkeley MBOX 'From ' lines. Oldest first.
3776 (let* ((default-directory default-directory)
3777 (buffer (get-buffer-create tinymail-:report-spool-buffer))
3778 (kill-p (eq tinymail-:report-spool-buffer-control 'kill))
3779 ;;; #todo: not yet used
3780 ;;; (timeout tinymail-:report-asychronous-timeout)
3782 (kill-re tinymail-:report-mail-kill-line-regexp)
3785 (with-current-buffer buffer
3787 ;; - launch up the process and restore the directory setting
3788 ;; - The output is like:
3790 ;; From aa@zig.com Thu May 11 19:05:36 EET 1995
3791 ;; From bb@zag.com.edu Thu May 11 18:55:59 EET 1995
3792 (setq default-directory tmp-dir)
3794 ((stringp shell-call)
3795 (shell-command shell-call buffer))
3796 ((fboundp shell-call)
3797 (funcall shell-call))
3800 (when (stringp kill-re)
3802 (flush-lines kill-re))
3803 (unless (eq (point-min) (point-max)) ;No output ?
3804 ;; - Now read persons email delimited by spaces
3805 ;; - Read the last line to get newest mail arrival
3808 (push (ti::read-current-line) ret)
3811 (when (and kill-p (buffer-live-p (setq buffer (get-buffer buffer))))
3812 (with-current-buffer buffer
3813 (set-buffer-modified-p nil)) ;No confirmations
3814 (kill-buffer buffer)))
3817 ;;; ----------------------------------------------------------------------
3819 (defun tinymail-report-mail-info ()
3820 "Run `tinymail-:report-mail-notify-program'."
3821 (and tinymail-:report-mail-notify-program
3822 (tinymail-report-mail-info-1 tinymail-:report-mail-notify-program)))
3824 ;;; ----------------------------------------------------------------------
3826 (defun tinymail-report-get-mail-info-string ()
3827 "Return mail string: last sender and mail count."
3828 (let* ((list (tinymail-report-mail-info))
3829 (re tinymail-:report-mail-info-shorten-regexp)
3830 (ret tinymail-:report-no-mail-string)
3834 (tinymail-debug 'tinymail-report-get-mail-info-string list)
3837 (setq count (length list)
3838 last-line (car (nreverse list))
3839 email (tinymail-report-get-email-word last-line))
3840 ;; does user want shortened version ?
3841 (when (and (stringp re) ;no regexp
3842 (string-match re email)
3843 (setq list (tinymail-report-break-email email)))
3844 (setq email (nth 0 list)))
3845 (setq ret (concat " " email " " (number-to-string count))))
3847 (message "TinyMail: *** tinymail-report-mail-info didn't return list")))
3850 ;;; ----------------------------------------------------------------------
3852 (defun turn-on-tinymail-report-mail (&optional verb)
3853 "Call `tinymail-report-mail-install-maybe'."
3855 (tinymail-report-mail-install-maybe verb))
3857 ;;; ----------------------------------------------------------------------
3859 (defun turn-off-tinymail-report-mail (&optional verb)
3860 "Call `tinymail-report-mail-install' with prefix argument."
3863 (tinymail-report-mail-install 'uninstall verb))
3865 ;;; ----------------------------------------------------------------------
3867 (defun tinymail-report-update (&rest args)
3868 "Update mail status information.
3869 Update the frame's status line, or in non-X show the message in echo area.
3871 (let* ((buffer (and (stringp tinymail-:report-spool-buffer)
3872 (get-buffer-create tinymail-:report-spool-buffer)))
3874 (eq tinymail-:report-spool-buffer-control 'raise)))
3879 ;; Save the contents of frame name, e.g. host name only once
3880 (if (and tinymail-:report-window-system
3881 (null tinymail-:report-old-frame-string))
3882 (setq tinymail-:report-old-frame-string
3883 (ti::compat-set-frame-name nil nil 'get)))
3884 (when (stringp (setq mail-info (tinymail-report-get-mail-info-string)))
3885 (setq tinymail-:report-mail-info-string (format " %s " mail-info))
3886 (setq display-string (eval tinymail-:report-format-string)))
3888 ;; ..................................................... windowed ...
3889 (tinymail-:report-window-system
3890 (dolist (elt (frame-list)) ;Update frames that are not exluded
3892 (ti::compat-set-frame-name nil nil 'get)
3893 tinymail-:report-keep-intact-list))
3894 (ti::compat-set-frame-name display-string elt))))
3895 ;; ...................................................... non-win ...
3896 ;; Do nothing if this is nil, user doesn't want to see evel "No mail"
3899 ;; ................................................. non-windowed ...
3901 (if (and (stringp tinymail-:report-no-mail-string)
3902 (not (string= mail-info tinymail-:report-no-mail-string)))
3903 (setq str "Mail: "))
3905 ((and (not (ti::compat-executing-macro))
3906 ;; printing message while user is in minibuffer
3907 ;; makes it impossible to see what he's doing.
3908 (not (eq (selected-window) (minibuffer-window)))
3910 (message "%s%s" (or str "") display-string)
3911 ;; make sure user sees it
3913 ;; .......................................................... beep ...
3914 ;; Notify about new mail ?
3915 (unless (stringp tinymail-:report-old-mail-info-string)
3916 (setq tinymail-:report-old-mail-info-string
3917 tinymail-:report-no-mail-string))
3918 ;;; (ti::d!! mail-info tinymail-:report-no-mail-string tinymail-:report-old-mail-info-string "\n")
3919 (when (and (stringp mail-info)
3920 (stringp tinymail-:report-no-mail-string)
3921 (not (string= mail-info
3922 tinymail-:report-no-mail-string))
3923 (not (string= mail-info
3924 tinymail-:report-old-mail-info-string)))
3925 (setq tinymail-:report-old-mail-info-string mail-info)
3930 (display-buffer buffer)))
3931 (setq tinymail-:report-old-mail-info-string mail-info))))
3933 ;;; ----------------------------------------------------------------------
3935 (defun tinymail-report-mail-install (&optional uninstall verb)
3936 "Install or UNINSTALL mail watchdog (report mail).
3938 `tinymail-:report-window-system'"
3941 (if (featurep 'reportmail)
3943 TinyMail: tinymail-report-mail-install: 'reportmail feature found, install ignored.")
3944 (let* (process-connection-type) ;Nicer process communication
3945 (if tinymail-:display-time
3946 (display-time))) ;; time.el
3947 ;; In XEmacs the frame must be configured by hand
3948 (when (and tinymail-:report-window-system (ti::xemacs-p))
3949 ;; make sure it's list
3950 (setq frame-title-format (ti::list-make frame-title-format))
3952 (delete 'tinymail-:report-mail-info-string frame-title-format)
3953 (pushnew 'tinymail-:report-mail-info-string
3957 (ti::compat-timer-cancel-function 'tinymail-report-update)
3958 (setq tinymail-:report-timer-object nil)
3960 (setq tinymail-:report-timer-object
3961 (run-at-time "1 min" (* 60 10) 'tinymail-report-update)))
3963 (message "TinyMail: Report mail feature is %s"
3968 ;;; ----------------------------------------------------------------------
3970 (defun tinymail-report-mail-install-maybe (&optional uninstall verb)
3971 "Don't call `tinymail-report-mail-install' if there already exists reporter.
3972 E.g. in XEmacs you can use package reportmail.el."
3976 ;; #todo: Any other report features we should check?
3977 ((featurep 'reportmail)
3978 (message "TinyMail: reportmail.el present, not installing."))
3980 (tinymail-report-mail-install uninstall verb))))
3983 ;;{{{ From-address generator (sendmail PLUS emulation)
3985 ;;; ----------------------------------------------------------------------
3987 (defun tinymail-from-anti-ube-maybe ()
3988 "Return anti-ube address if `newsgroups' match `tinymail-:from-anti-ube-regexp'"
3989 (when (and (stringp tinymail-:from-anti-ube-regexp)
3990 (stringp user-mail-address)
3992 (let* ((group (mail-fetch-field "Newsgroups")))
3993 (when (string-match tinymail-:from-anti-ube-regexp
3995 ;; - Because the anti-ube returns different email every
3996 ;; time it is called, cache the first value.
3997 ;; - The changing value would otherwise cause indication
3998 ;; "Headers have changed".
3999 (make-local-variable 'tinymail-:user-mail-address)
4000 (let ((addr (or tinymail-:user-mail-address
4001 (ti::mail-email-make-anti-spam-address
4002 user-mail-address))))
4003 (setq tinymail-:user-mail-address addr)
4006 ;;; ----------------------------------------------------------------------
4008 (defun tinymail-from-field-value-plus ()
4009 "Return special plus address emulation (RFC Comment)."
4010 (let* ((fid "tinymail-from-field-value-plus:")
4011 (news (mail-fetch-field "newsgroups"))
4012 (prefixes tinymail-:from-table-prefix)
4013 (postfixes tinymail-:from-table-postfix)
4014 ;; Posting from Gnus, so get the Group name
4016 ;; backend:mail.xxx --> mail.xxx
4017 (grp (ti::mail-news-group))
4018 (group (and (stringp grp)
4019 (eq major-mode 'message-mode)
4020 (or (ti::string-match ":\\(.*\\)" 1 grp)
4027 (unless fid ;; No-op. XEmacs byte compiler silencer
4030 ;; ............................................... news followup ...
4032 ;; Direct some mesages to my "mail" group, others to
4033 ;; general usenet group.
4034 (setq prefix (cdr-safe (ti::list-find prefixes news )))
4035 (setq postfix "")) ;; (cdr-safe (ti::list-find prefixes news )))
4037 ;; If posting from inside Group, add Group based PLUS address
4039 ;; list.xxx --> '("list" "xxx")
4040 ;; For Imap folders; INBOX.list.foo => list.foo
4041 (when (string-match "INBOX\\.\\(.+\\)" group)
4042 (setq group (match-string 1 group)))
4043 (setq prefix (ti::string-match "^\\([^.]+\\)" 1 group)
4044 postfix (ti::string-match "\\.\\(.+\\)" 1 group))))
4045 ;; ................................ according to message content ...
4048 (dolist (elt postfixes)
4049 (ti::mail-text-start 'move)
4050 (setq condition (car elt))
4051 (when (if (stringp condition)
4052 (re-search-forward condition nil t)
4053 (setq ret (funcall condition)))
4055 (tinymail-debug fid 'point (point) 'LOOP-SELECT elt )
4056 (setq ret (cdr elt)))
4059 ;; ............................................... guess mail type ...
4060 ;; If not yet set, look at message and decide right postfix
4063 (msg-postfix ;Always obey this
4065 ((and (ti::nil-p prefix) postfix)
4067 ((and (ti::nil-p postfix) prefix)
4069 ((and prefix postfix)
4070 (concat prefix "." postfix))))
4071 (tinymail-debug fid 'ret ret
4074 'msg-postfix msg-postfix
4075 'prefix prefix 'postfix postfix )
4078 ;;; ----------------------------------------------------------------------
4080 (defun tinymail-from-field-value ()
4087 `tinymail-:from-info-function'
4088 `tinymail-:from-field-plus-separator'"
4090 (let* ((fid "tinymail-from-field-value:")
4091 (separator tinymail-:from-field-plus-separator)
4092 (info (and (fboundp tinymail-:from-info-function)
4093 (funcall tinymail-:from-info-function)))
4094 (address (or (and (listp info)
4096 (tinymail-from-anti-ube-maybe)
4097 (or (stringp user-mail-address)
4099 "TinyMail: Please set `user-mail-address'."))))
4100 (name (or (user-full-name)
4101 (error "TinyMail: Please set `user-full-name'")))
4102 (plus (or (and (listp info)
4104 (tinymail-from-field-value-plus)))
4108 (unless fid ;; No-op. XEmacs byte compiler silencer
4110 ;; With procmail you can have plus addresses:
4112 ;; login+additional-info@site.com
4114 ;; But you can accomplish the same with RFC comment syntax
4116 ;; login@site.com (Foo Bar+additional-info)
4118 ;; The extra "+" is just added there to mark that this is
4121 ((and (not (ti::nil-p plus))
4122 (not (ti::nil-p name)))
4123 (setq plus (format " (%s%s%s)" name separator plus )))
4124 ((not (ti::nil-p name)) ;If no plus info, use normal address
4125 (setq plus (format " (%s)" name))))
4126 (when (stringp address)
4127 (setq localpart (or (ti::string-match "^[^@]+" 0 address) "")
4128 domain (or (ti::string-match "@.*" 0 address) ""))
4129 ;; RFC 1036/2.1.1 Says that following address formats are preferred in
4132 ;; From: mark@cbosgd.ATT.COM
4133 ;; From: mark@cbosgd.ATT.COM (Mark Horton)
4134 ;; From: Mark Horton <mark@cbosgd.ATT.COM>
4135 (setq ret (format "%s%s%s" localpart domain (or plus "") )))
4137 'info-function tinymail-:from-info-function
4139 'localpart localpart
4145 ;;; ----------------------------------------------------------------------
4147 (defun tinymail-message-disable-sender ()
4148 "Disable Sender field generation permanently."
4149 ;; Gnus message-mode
4150 ;; Don't generate Sender address, but trust From address
4152 (when (boundp 'message-syntax-checks)
4153 (let* ((syntaxes (and (boundp 'message-syntax-checks)
4154 (symbol-value 'message-syntax-checks)))
4155 ;; Gnus Group / Agent J S comamdn set this to
4156 ;; value 'dont-check-for-anything-just-trust-me
4157 ;; => skip any checks
4158 (list-p (ti::listp syntaxes))
4159 (pointer (and list-p
4160 (assq 'sender syntaxes))))
4163 (setcdr pointer 'disabled)
4164 (add-to-list 'message-syntax-checks '(sender . disabled)))))))
4168 ;;{{{ code: Cc, X-Sender-Info
4170 ;;; ............................................................. &fld ...
4172 (defun tinymail-field-cc-kill-by-regexp ()
4173 "Kill entry from CC field that match `my-:email-regexp'"
4174 ;; don't touch CC field if user has put two spaces in front.
4175 (when (and (mail-fetch-field "CC")
4176 (not (tinymail-field-off-p "CC"))
4177 (stringp tinymail-:cc-kill-regexp))
4178 (let* ((cc (mail-fetch-field "cc"))
4179 (cc-list (and cc (split-string cc ",[ \t\n]*")))
4183 (dolist (elt cc-list)
4184 (unless (string-match tinymail-:cc-kill-regexp elt)
4186 (setq ccl (format "%s\n %s," (or ccl "") elt))))
4188 (when (and cc-list (not (stringp ccl)))
4189 (ti::mail-kill-field "^CC")) ;All CC memebers killed. Wipe field
4191 (when (and (stringp ccl)
4192 (not (eq count (length cc-list)))) ;items removed
4193 ;; delete leading \n and trailing comma
4194 (setq ccl (substring ccl 1 (1- (length ccl))))
4195 (ti::mail-kill-field "^CC" ccl)
4199 ;;; ----------------------------------------------------------------------
4201 (defun tinymail-field-in-to-cc-p ()
4202 "Check if point is at field To, Bcc, Cc."
4203 (and (< (point) (ti::mail-hmax))
4205 (and (ti::mail-next-field-start 'move 'back)
4206 (looking-at "CC\\|BCC\\|To")))))
4208 ;;; ----------------------------------------------------------------------
4210 (defun tinymail-field-to-move-maybe ()
4211 "Move cursor to the end of TO field if it is empty."
4212 (when (save-excursion (beginning-of-line) (looking-at "To: *$"))
4214 (tinymail-debug "TO-move:" (ti::read-current-line) (point))))
4217 ;;{{{ code: Fcc handling
4219 ;;; ......................................................... &fld-fcc ...
4221 ;;; ----------------------------------------------------------------------
4223 (defun tinymail-field-fcc-determine (&optional type hsize)
4224 "Look if default folder must be changed.
4225 Tries to find RE given in `tinymail-:table-fcc' by looking at header area.
4229 TYPE nil: Find Fcc folder. 'gcc: Find Gcc folder.
4230 HSIZE The header size precalculated.
4234 string suggested folder
4236 (let* ((fid "tinymail-field-fcc-determine: ")
4239 tinymail-:table-fcc))
4240 (sym (if type 'gcc 'fcc))
4241 (get-hsize (get 'tinymail-:table-fcc 'hsize))
4242 (get-sym (get 'tinymail-:table-fcc sym))
4247 (unless fid ;; No-op. XEmacs byte compiler silencer
4250 (setq hsize (ti::mail-header-area-size)))
4251 (tinymail-debug fid "IN"
4254 "GET HSIZE" get-hsize
4256 (when (not (and (eq hsize get-hsize)
4257 ;; Previous folder value
4258 (setq ret get-sym)))
4259 ;; Header are has changed; calculate new field and update values
4260 (put 'tinymail-:table-fcc 'hsize hsize)
4261 (put 'tinymail-:table-fcc sym nil)
4262 (when (setq hmax (ti::mail-hmax)) ;header end must be found
4266 (setq re (nth 0 elt)
4268 (when (re-search-forward re hmax t)
4271 (if (and (stringp ret)
4272 (string-match "gz$\\|Z$" ret))
4273 (ti::use-file-compression))
4274 (put 'tinymail-:table-fcc sym ret)
4275 (tinymail-debug fid "SET hmax" hmax "ret" ret "re" re))
4278 ;;; ----------------------------------------------------------------------
4280 (defun tinymail-field-fcc (&optional type hsize)
4281 "Set right [GF]cc folder if there is match in `tinymail-:table-[gf]cc'.
4285 TYPE nil: Find Fcc folder. 'gcc: Find Gcc folder.
4286 HSIZE The header size precalculated."
4287 (let* ((fid "tinymail-field-fcc: ")
4293 (unless fid ;; No-op. XEmacs byte compiler silencer
4295 (tinymail-debug fid "in TYPE" type "HSIZE" hsize)
4297 (setq hsize (ti::mail-header-area-size)))
4299 (type (setq fld "GCC" sym 'gcc-old))
4300 (t (setq fld "FCC" sym 'fcc-old)))
4301 (setq prev (get 'tinymail-:table-fcc sym))
4302 (setq folder (tinymail-field-fcc-determine type hsize))
4303 (tinymail-debug fid fld type hsize
4307 (ti::re-search-check (regexp-quote folder)))
4310 (when (and (stringp folder)
4311 (or (not (stringp prev))
4312 (not (ti::re-search-check (regexp-quote folder)))
4313 (not (string-match (regexp-quote prev) folder)))
4314 (setq str (mail-fetch-field fld))
4315 (not (tinymail-field-off-p nil str)))
4316 (put 'tinymail-:table-fcc sym folder)
4317 (ti::save-line-column-macro nil nil
4318 (tinymail-debug fid "SET" folder (current-buffer))
4319 (ti::mail-kill-field (concat "^" fld) folder)))))
4321 ;;; ----------------------------------------------------------------------
4323 (defun tinymail-field-to-off (&optional count field)
4324 "Disable TinyMail by space COUNT for FIELD.
4325 2 spaces disables Cc tracking
4326 3 spaces disables both Cc and other tracking."
4328 (setq str (make-string (or count 2) ?\ ))
4329 (setq field (or field "to"))
4330 (tinymail-debug "tinymail-field-to-off, count, field" count field)
4333 (when (re-search-forward
4334 (concat "^" field ":\\([ \t]*\\)")
4337 (if (match-beginning 1)
4338 (ti::replace-match 1 str) ;There is spaces
4339 (insert str)) ;There is no spaces
4341 (tinymail-field-to-move-maybe)))
4343 ;;; ----------------------------------------------------------------------
4345 (defun tinymail-field-to-on ()
4346 "Keep activated by making sure the To: field has only one space."
4347 (tinymail-debug "tinymail-field-to-on")
4348 (tinymail-field-to-off 1))
4350 ;;; ----------------------------------------------------------------------
4353 (defun tinymail-on-off-toggle (&optional arg)
4354 "Toggle TinyMail mode on and off by Changing spacing of To field.
4355 This affects automatic Cc and X-Sender-Info tracking.
4356 ARG behaves like mode argument.
4358 Without arg, this toggless Cc tracking, with prefix argument,
4359 it toggless both Cc and X-Sender-Info tracking."
4362 (setq arg 3)) ;3 spaces turn off completely.
4367 (re-search-forward "^to: " nil t))
4368 (tinymail-field-to-on)
4369 (message "TinyMail: mail field tracking mode on."))
4371 (tinymail-field-to-off arg)
4372 (message "TinyMail: mail field tracking mode off.")))
4373 (tinymail-field-to-off arg)
4374 (message "TinyMail: mail field tracking mode off.")))
4377 ;;{{{ code: citation
4379 ;;; ----------------------------------------------------------------------
4381 (defun tinymail-iso8601-date-value ()
4382 "Read Date field and return ISO 8601 date: WEEKDAY YYYY-MM-DD."
4388 (when (setq date (or (mail-fetch-field "date")
4389 (and (featurep 'message)
4390 message-reply-headers
4391 (mail-header-date message-reply-headers))))
4392 (setq date (ti::date-parse-date date))
4394 (setq yyyy (nth 0 date)
4397 week-day (nth 4 date))
4402 (if yyyy (concat (if week-day " " "") yyyy))
4403 (if (and yyyy mm) (concat "-" mm) "")
4404 (if (and yyyy mm dd) (concat "-" dd) ""))))
4407 ;;; ----------------------------------------------------------------------
4409 (defun tinymail-citation-who-said (str)
4410 "Formats sender line reference. Input is From/To field.
4414 str formatted line, without 'From:'
4415 nil if cannot format"
4416 (let ((limit (- 75 13)) ; line-lenght - date ==> limit
4417 ;; Get the group name only when posting from GNUS
4418 ;; gnus-group-real-name
4419 (grp (replace-regexp-in-string
4420 ".*:" "" (or (ti::mail-news-group) "")))
4428 ;; Remove quotes: "Mr. this" <email@example.com>
4429 (setq ret (replace-regexp-in-string "['\"]+" "" ret))
4430 ;; Remove Middle names: Foo X. Bar
4431 (setq ret (replace-regexp-in-string " [A-Z]\\." "" ret)))
4432 ;; If the line is exessive long, say;
4433 ;; "Mr. Foo the most spectacular..." <foo@camel.com>
4434 ;; Then we make it smaller.
4435 (when (> (length ret) limit)
4436 ;; Get only the email, and drop all others
4437 (setq list (ti::mail-parse-name str))
4438 (setq email (or (car-safe (ti::mail-email-from-string str)) ""))
4440 (setq fn (nth 0 list) sn (nth 1 list)) ;; first/surname
4441 ;; this should suffice
4442 (setq ret (concat fn " " sn " <" email ">"))))
4443 ;; Does the group name fit in too ?
4445 (< (+ (length ret) (length grp) 1) limit))
4446 (setq ret (concat ret " " grp)))
4449 ;;; ----------------------------------------------------------------------
4451 (defun tinymail-message-id-value ()
4452 "Return Google group url."
4453 (let ((id (mail-fetch-field "References")))
4455 ;; There are several Message-Id's in a thread. Pick latest.
4456 (setq id (car (nreverse (split-string id))))
4457 (ti::string-match "<\\([^ \t\n>]+\\)>" 1 id))))
4459 ;;; ----------------------------------------------------------------------
4461 (defun tinymail-url-reference-google-group ()
4462 "Return Google group url."
4463 (let* ((id (tinymail-message-id-value))
4465 (mail-fetch-field "Newsgroups"))))
4466 (when (and (stringp group)
4467 ;; See http://groups.google.com/
4471 "^\\(alt\\|biz\\|comp\\|humanities"
4472 "\\|misc\\|news\\|rec\\|sci\\|soc\\|talk")))
4475 ;; http://search.dejanews.com/msgid.xp
4476 ;; ?MID=%3C3cgd8m0w.fsf@blue.sea.net%3E&format=threaded
4478 "<http://groups.google.com/groups?oi=djq"
4483 ;;; ----------------------------------------------------------------------
4485 (defun tinymail-url-reference-mailing-list ()
4486 "Return maling list URL refence."
4487 (when (or (ti::mail-to-list-p)
4490 (or (mail-fetch-field "Newsgroups") "")))
4491 (let* ((id (tinymail-message-id-value)))
4492 (concat "Message-Id: " id))))
4494 ;;; ----------------------------------------------------------------------
4496 (defun tinymail-message-id ()
4497 "Return message id or empty.
4498 This function works best with Gnus:
4500 - Mailing lists replies contain Message-Id reference.
4501 The mailing list status is indicated by Gnus group property `to-list'.
4502 - Newsgroup replies contain URL reference.
4503 - Private mail does _not_ include any extra references."
4504 (let* ((url (or (tinymail-url-reference-google-group)
4505 (tinymail-url-reference-mailing-list))))
4507 (concat "* " url "\n"))))
4509 ;;; ----------------------------------------------------------------------
4511 (defun tinymail-message-citation-line-function ()
4512 "Generate citation line.
4514 * Tue YYYY-MM-DD John Doe <johnd@example.com> mail.emacs
4515 * Message-Id: <......>
4516 | Thankyou for helping me...
4519 It is important to include the Message-Id reference because then it is
4520 possible to retrieve whole News thread e.g. from GOOGLE group. Message-Id is
4521 a handly way to refer to past articles."
4522 (let* ((hdrs (if (boundp 'message-reply-headers)
4523 (symbol-value 'message-reply-headers)))
4524 (from (and hdrs (tinymail-citation-who-said
4525 (mail-header-from hdrs))))
4526 (date (tinymail-iso8601-date-value)))
4527 (delete-horizontal-space)
4528 (insert "* " date " " from "\n")
4529 (let ((id (funcall tinymail-:citation-message-id-function)))
4533 ;;; ----------------------------------------------------------------------
4535 (defun tinymail-citation-generate ()
4536 "Write reference line."
4537 (if (eq major-mode 'message-mode)
4538 (tinymail-message-citation-line-function)
4539 (tinymail-citation-who-said (mail-fetch-field "From"))))
4544 (defun tinymail-gpg-recipient ()
4545 "Check BBDB field gnus-pgp for 'sign' and 'encrypt'."
4546 (when (and (eq (major-mode 'message-mode)
4548 (when (and (not message-has-gpg)
4550 (let* ((to-field (mail-fetch-field "to"))
4551 (components (mail-extract-address-components to_field t))
4553 (when (= (length components) 1)
4554 ;; Only a single recipient
4555 (setq recipient (nth 1 (car components)))
4556 (let* ((record (bbdb-search-simple nil recipient))
4559 (setq gpg (bbdb-get-field record 'gnus-gpg)))
4560 (when (> (length gpg) 0)
4562 ((string= gpg "sign")
4563 (mml-secure-message-sign-pgpmime))
4564 ((string= gpg "encrypt")
4565 (mml-secure-message-encrypt-pgpmime))))))))))
4570 ;;; ----------------------------------------------------------------------
4572 (defun tinymail-from-set-field (&optional from-field)
4573 "Check FROM-FIELD and set From: unless it has two spaces in front."
4575 (let ((from (or from-field
4576 (ti::mail-get-field-1 "From")))
4578 (tinymail-debug fid 'initial-from from-field)
4580 ;; The field was there, if there in NO two spaces, replace
4581 ;; the content with new dynamic value
4583 ;; If used puts two spaces at from, he want to modify
4584 ;; the field himself
4585 ((and (stringp from)
4586 (string-match "^ " from-field))
4587 (tinymail-debug fid 'from-disabled-space))
4588 ((stringp (setq str (tinymail-from-field-value)))
4589 ;; Will create if not exists.
4590 (mail-position-on-field "From")
4591 (ti::mail-kill-field "^From" str))))))
4593 ;;; ----------------------------------------------------------------------
4595 (defun tinymail-process-1 (&optional force)
4596 "See `tinymail-process'. If FORCE is non-nil, run immediately.
4597 This function should be called interactive only when debugging errors:
4598 C-u M-x tinymail-process-1."
4600 (let* ((fid "tinymail-process: ")
4601 (last-to tinymail-:last-to-field)
4602 (alias-alist (tinymail-mail-aliases))
4607 (unless fid ;; No-op. XEmacs byte compiler silencer
4609 ;; - If "To:" field content has two spaces at front, this is signal
4612 ;; - If To address has remained the same, we do nothing.
4613 ;; If we would aways go into expanding and killing the Cc,Fcc
4614 ;; fields blindly, user would notice that while he was
4615 ;; writing his message. Avoid that as much as posisble
4617 (setq to (ti::remove-properties
4618 (or (ti::mail-get-field-1 "To")
4619 (ti::mail-get-field-1 "newsgroups")))
4620 from (ti::remove-properties (ti::mail-get-field-1 "From"))
4621 hsize (ti::remove-properties (ti::mail-header-area-size))
4622 ohsize (get 'tinymail-:process-hook 'old-hsize))
4623 ;; Record the values now so that they aren't calculated any more
4624 (put 'tinymail-:process-hook 'from from)
4625 (put 'tinymail-:process-hook 'to to)
4626 (put 'tinymail-:process-hook 'new-hsize hsize)
4627 (tinymail-debug fid "TO" to
4629 "POST-hook" tinymail-:process-hook
4630 "hsize" hsize ohsize
4633 ((or (not (stringp to))
4634 (tinymail-field-off-p nil to))
4636 ((and (not (string= to last-to)) ;not same as previously ?
4637 (not (ti::nil-p to)))
4639 (ti::mail-abbrev-expand-mail-aliases
4640 (point-min) (ti::mail-hmax) alias-alist))
4641 ;; what was expanded
4642 (setq to (ti::mail-get-field "To"))))
4643 (if tinymail-:from-field-enable-flag
4644 (tinymail-from-set-field from))
4645 ;; ............................................ header changed ...
4646 (tinymail-field-cc-kill-by-regexp)
4647 (when (not (eq hsize ohsize)) ;; Handle dynamic save to folders
4648 (tinymail-field-fcc nil hsize)
4649 (tinymail-field-fcc 'gcc hsize)
4652 "Running post hook" tinymail-:process-hook
4654 (setq tinymail-:last-to-field to) ;update
4655 (tinymail-debug fid "END" "MODE" major-mode))
4656 ;; User's things now
4657 (run-hooks 'tinymail-:process-hook)))
4659 ;;; ----------------------------------------------------------------------
4661 (defun tinymail-process-run-p ()
4662 "Return t if `tinymail-process' is allowed to run."
4663 (and (get-buffer-window (current-buffer))
4664 ;; If buffer is not displayed, do nothing.
4665 (not buffer-read-only)
4669 ;;; ----------------------------------------------------------------------
4671 (defun tinymail-process (&optional force)
4672 "Expand mail aliases and inserts additional info.
4674 optional FORCE argument causes running post hook now.
4676 If you take advantage of the `tinymail-:process-hook', please remember
4679 - Your hook must run as fast as possible so that it won't disturb
4681 - You can peek contents of the precaculated values instead of reading
4682 then again in the buffer
4684 (get 'tinymail-:process-hook 'to to) ;; To field content
4685 (get 'tinymail-:process-hook 'new-hsize) ;; Header size now
4686 (get 'tinymail-:process-hook 'old-hsize) ;; old header size"
4688 (and (tinymail-process-run-p)
4689 ;; If this doesn't look like mail, don't bother
4691 (condition-case error
4692 (tinymail-process-1 force)
4694 (message "TinyMail: post-command error: %s
4695 Spot the error by turning on Emacs debug and calling
4696 (tinymail-process-1 'force) or C-u M-x tinymail-process-1"
4697 (prin1-to-string error))
4699 (sit-for 5) ;Make sure user notices this ;
4700 (message "TinyMail: (error watch) Please Check *Messages* buffer.")))))
4705 (run-hooks 'tinymail-:load-hook)
4707 ;;; tinymail.el ends here