Revision: miles@gnu.org--gnu-2005/emacs--unicode--0--patch-86
[bpt/emacs.git] / lisp / mail / rmail.el
1 ;;; rmail.el --- main code of "RMAIL" mail reader for Emacs
2
3 ;; Copyright (C) 1985, 1986, 1987, 1988, 1993, 1994, 1995, 1996, 1997, 1998,
4 ;; 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
5
6 ;; Maintainer: FSF
7 ;; Keywords: mail
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
25
26 ;;; Commentary:
27
28 ;;; Code:
29
30 ;; Souped up by shane@mit-ajax based on ideas of rlk@athena.mit.edu
31 ;; New features include attribute and keyword support, message
32 ;; selection by dispatch table, summary by attributes and keywords,
33 ;; expunging by dispatch table, sticky options for file commands.
34
35 ;; Extended by Bob Weiner of Motorola
36 ;; New features include: rmail and rmail-summary buffers remain
37 ;; synchronized and key bindings basically operate the same way in both
38 ;; buffers, summary by topic or by regular expression, rmail-reply-prefix
39 ;; variable, and a bury rmail buffer (wipe) command.
40 ;;
41
42 (require 'mail-utils)
43 (eval-when-compile (require 'mule-util)) ; for detect-coding-with-priority
44
45 (defvar deleted-head)
46 (defvar font-lock-fontified)
47 (defvar mail-abbrev-syntax-table)
48 (defvar mail-abbrevs)
49 (defvar messages-head)
50 (defvar rmail-use-spam-filter)
51 (defvar rsf-beep)
52 (defvar rsf-sleep-after-message)
53 (defvar total-messages)
54
55 ; These variables now declared in paths.el.
56 ;(defvar rmail-spool-directory "/usr/spool/mail/"
57 ; "This is the name of the directory used by the system mailer for\n\
58 ;delivering new mail. Its name should end with a slash.")
59 ;(defvar rmail-file-name
60 ; (expand-file-name "~/RMAIL")
61 ; "")
62
63 (defgroup rmail nil
64 "Mail reader for Emacs."
65 :group 'mail)
66
67 (defgroup rmail-retrieve nil
68 "Rmail retrieval options."
69 :prefix "rmail-"
70 :group 'rmail)
71
72 (defgroup rmail-files nil
73 "Rmail files."
74 :prefix "rmail-"
75 :group 'rmail)
76
77 (defgroup rmail-headers nil
78 "Rmail header options."
79 :prefix "rmail-"
80 :group 'rmail)
81
82 (defgroup rmail-reply nil
83 "Rmail reply options."
84 :prefix "rmail-"
85 :group 'rmail)
86
87 (defgroup rmail-summary nil
88 "Rmail summary options."
89 :prefix "rmail-"
90 :prefix "rmail-summary-"
91 :group 'rmail)
92
93 (defgroup rmail-output nil
94 "Output message to a file."
95 :prefix "rmail-output-"
96 :prefix "rmail-"
97 :group 'rmail)
98
99 (defgroup rmail-edit nil
100 "Rmail editing."
101 :prefix "rmail-edit-"
102 :group 'rmail)
103
104 (defgroup rmail-obsolete nil
105 "Rmail obsolete customization variables."
106 :group 'rmail)
107
108 (defcustom rmail-movemail-program nil
109 "If non-nil, the file name of the `movemail' program."
110 :group 'rmail-retrieve
111 :type '(choice (const nil) string))
112
113 (defcustom rmail-pop-password nil
114 "*Password to use when reading mail from POP server.
115 Please use `rmail-remote-password' instead."
116 :type '(choice (string :tag "Password")
117 (const :tag "Not Required" nil))
118 :group 'rmail-obsolete)
119
120 (defcustom rmail-pop-password-required nil
121 "*Non-nil if a password is required when reading mail from a POP server.
122 Please use rmail-remote-password-required instead."
123 :type 'boolean
124 :group 'rmail-obsolete)
125
126 (defcustom rmail-remote-password nil
127 "*Password to use when reading mail from a remote server.
128 This setting is ignored for mailboxes whose URL already contains a password."
129 :type '(choice (string :tag "Password")
130 (const :tag "Not Required" nil))
131 :set-after '(rmail-pop-password)
132 :set #'(lambda (symbol value)
133 (set-default symbol
134 (if (and (not value)
135 (boundp 'rmail-pop-password)
136 rmail-pop-password)
137 rmail-pop-password
138 value))
139 (setq rmail-pop-password nil))
140 :group 'rmail-retrieve
141 :version "22.1")
142
143 (defcustom rmail-remote-password-required nil
144 "*Non-nil if a password is required when reading mail from a remote server."
145 :type 'boolean
146 :set-after '(rmail-pop-password-required)
147 :set #'(lambda (symbol value)
148 (set-default symbol
149 (if (and (not value)
150 (boundp 'rmail-pop-password-required)
151 rmail-pop-password-required)
152 rmail-pop-password-required
153 value))
154 (setq rmail-pop-password-required nil))
155 :group 'rmail-retrieve
156 :version "22.1")
157
158 (defcustom rmail-movemail-flags nil
159 "*List of flags to pass to movemail.
160 Most commonly used to specify `-g' to enable GSS-API authentication
161 or `-k' to enable Kerberos authentication."
162 :type '(repeat string)
163 :group 'rmail-retrieve
164 :version "20.3")
165
166 (defvar rmail-remote-password-error "invalid usercode or password\\|
167 unknown user name or bad password\\|Authentication failed\\|MU_ERR_AUTH_FAILURE"
168 "Regular expression matching incorrect-password POP or IMAP server error
169 messages.
170 If you get an incorrect-password error that this expression does not match,
171 please report it with \\[report-emacs-bug].")
172
173 (defvar rmail-encoded-remote-password nil)
174
175 (defcustom rmail-preserve-inbox nil
176 "*Non-nil means leave incoming mail in the user's inbox--don't delete it."
177 :type 'boolean
178 :group 'rmail-retrieve)
179
180 (defcustom rmail-movemail-search-path nil
181 "*List of directories to search for movemail (in addition to `exec-path')."
182 :group 'rmail-retrieve
183 :type '(repeat (directory)))
184
185 (defun rmail-probe (prog)
186 "Determine what flavor of movemail PROG is.
187 We do this by executing it with `--version' and analyzing its output."
188 (with-temp-buffer
189 (let ((tbuf (current-buffer)))
190 (buffer-disable-undo tbuf)
191 (call-process prog nil tbuf nil "--version")
192 (if (not (buffer-modified-p tbuf))
193 ;; Should not happen...
194 nil
195 (goto-char (point-min))
196 (cond
197 ((looking-at ".*movemail: invalid option")
198 'emacs) ;; Possibly...
199 ((looking-at "movemail (GNU Mailutils .*)")
200 'mailutils)
201 (t
202 ;; FIXME:
203 'emacs))))))
204
205 (defun rmail-autodetect ()
206 "Determine and return the file name of the `movemail' program.
207 If `rmail-movemail-program' is non-nil, use it.
208 Otherwise, look for `movemail' in the directories in
209 `rmail-movemail-search-path', those in `exec-path', and `exec-directory'."
210 (if rmail-movemail-program
211 (rmail-probe rmail-movemail-program)
212 (catch 'scan
213 (dolist (dir (append rmail-movemail-search-path exec-path
214 (list exec-directory)))
215 (when (and dir (file-accessible-directory-p dir))
216 (let ((progname (expand-file-name "movemail" dir)))
217 (when (and (not (file-directory-p progname))
218 (file-executable-p progname))
219 (let ((x (rmail-probe progname)))
220 (when x
221 (setq rmail-movemail-program progname)
222 (throw 'scan x))))))))))
223
224 (defvar rmail-movemail-variant-in-use nil
225 "The movemail variant currently in use. Known variants are:
226
227 `emacs' Means any implementation, compatible with the native Emacs one.
228 This is the default;
229 `mailutils' Means GNU mailutils implementation, capable of handling full
230 mail URLs as the source mailbox;")
231
232 ;;;###autoload
233 (defun rmail-movemail-variant-p (&rest variants)
234 "Return t if the current movemail variant is any of VARIANTS.
235 Currently known variants are 'emacs and 'mailutils."
236 (when (not rmail-movemail-variant-in-use)
237 ;; Autodetect
238 (setq rmail-movemail-variant-in-use (rmail-autodetect)))
239 (not (null (member rmail-movemail-variant-in-use variants))))
240
241 ;;;###autoload
242 (defcustom rmail-dont-reply-to-names nil "\
243 *A regexp specifying addresses to prune from a reply message.
244 A value of nil means exclude your own email address as an address
245 plus whatever is specified by `rmail-default-dont-reply-to-names'."
246 :type '(choice regexp (const :tag "Your Name" nil))
247 :group 'rmail-reply)
248
249 ;;;###autoload
250 (defvar rmail-default-dont-reply-to-names "\\`info-" "\
251 A regular expression specifying part of the default value of the
252 variable `rmail-dont-reply-to-names', for when the user does not set
253 `rmail-dont-reply-to-names' explicitly. (The other part of the default
254 value is the user's email address and name.)
255 It is useful to set this variable in the site customization file.")
256
257 ;;;###autoload
258 (defcustom rmail-ignored-headers
259 (concat "^via:\\|^mail-from:\\|^origin:\\|^references:\\|^sender:"
260 "\\|^status:\\|^received:\\|^x400-originator:\\|^x400-recipients:"
261 "\\|^x400-received:\\|^x400-mts-identifier:\\|^x400-content-type:"
262 "\\|^\\(resent-\\|\\)message-id:\\|^summary-line:\\|^resent-date:"
263 "\\|^nntp-posting-host:\\|^path:\\|^x-char.*:\\|^x-face:\\|^face:"
264 "\\|^x-mailer:\\|^delivered-to:\\|^lines:"
265 "\\|^content-transfer-encoding:\\|^x-coding-system:"
266 "\\|^return-path:\\|^errors-to:\\|^return-receipt-to:"
267 "\\|^precedence:\\|^list-help:\\|^list-post:\\|^list-subscribe:"
268 "\\|^list-id:\\|^list-unsubscribe:\\|^list-archive:"
269 "\\|^content-length:\\|^nntp-posting-date:\\|^user-agent"
270 "\\|^importance:\\|^envelope-to:\\|^delivery-date\\|^openpgp:"
271 "\\|^mbox-line:\\|^cancel-lock:"
272 "\\|^resent-face:\\|^resent-x.*:\\|^resent-organization:\\|^resent-openpgp:"
273
274 "\\|^x-.*:")
275 "*Regexp to match header fields that Rmail should normally hide.
276 \(See also `rmail-nonignored-headers', which overrides this regexp.)
277 This variable is used for reformatting the message header,
278 which normally happens once for each message,
279 when you view the message for the first time in Rmail.
280 To make a change in this variable take effect
281 for a message that you have already viewed,
282 go to that message and type \\[rmail-toggle-header] twice."
283 :type 'regexp
284 :group 'rmail-headers)
285
286 (defcustom rmail-nonignored-headers "^x-spam-status:"
287 "*Regexp to match X header fields that Rmail should show.
288 This regexp overrides `rmail-ignored-headers'; if both this regexp
289 and that one match a certain header field, Rmail shows the field.
290
291 This variable is used for reformatting the message header,
292 which normally happens once for each message,
293 when you view the message for the first time in Rmail.
294 To make a change in this variable take effect
295 for a message that you have already viewed,
296 go to that message and type \\[rmail-toggle-header] twice."
297 :type 'regexp
298 :group 'rmail-headers)
299
300 ;;;###autoload
301 (defcustom rmail-displayed-headers nil
302 "*Regexp to match Header fields that Rmail should display.
303 If nil, display all header fields except those matched by
304 `rmail-ignored-headers'."
305 :type '(choice regexp (const :tag "All"))
306 :group 'rmail-headers)
307
308 ;;;###autoload
309 (defcustom rmail-retry-ignored-headers "^x-authentication-warning:" "\
310 *Headers that should be stripped when retrying a failed message."
311 :type '(choice regexp (const nil :tag "None"))
312 :group 'rmail-headers)
313
314 ;;;###autoload
315 (defcustom rmail-highlighted-headers "^From:\\|^Subject:" "\
316 *Regexp to match Header fields that Rmail should normally highlight.
317 A value of nil means don't highlight.
318 See also `rmail-highlight-face'."
319 :type 'regexp
320 :group 'rmail-headers)
321
322 ;;;###autoload
323 (defcustom rmail-highlight-face nil "\
324 *Face used by Rmail for highlighting headers."
325 :type '(choice (const :tag "Default" nil)
326 face)
327 :group 'rmail-headers)
328
329 ;;;###autoload
330 (defcustom rmail-delete-after-output nil "\
331 *Non-nil means automatically delete a message that is copied to a file."
332 :type 'boolean
333 :group 'rmail-files)
334
335 ;;;###autoload
336 (defcustom rmail-primary-inbox-list nil "\
337 *List of files which are inboxes for user's primary mail file `~/RMAIL'.
338 nil means the default, which is (\"/usr/spool/mail/$USER\")
339 \(the name varies depending on the operating system,
340 and the value of the environment variable MAIL overrides it)."
341 ;; Don't use backquote here, because we don't want to need it
342 ;; at load time.
343 :type (list 'choice '(const :tag "Default" nil)
344 (list 'repeat ':value (list (or (getenv "MAIL")
345 (concat "/var/spool/mail/"
346 (getenv "USER"))))
347 'file))
348 :group 'rmail-retrieve
349 :group 'rmail-files)
350
351 ;;;###autoload
352 (defcustom rmail-mail-new-frame nil
353 "*Non-nil means Rmail makes a new frame for composing outgoing mail.
354 This is handy if you want to preserve the window configuration of
355 the frame where you have the RMAIL buffer displayed."
356 :type 'boolean
357 :group 'rmail-reply)
358
359 ;;;###autoload
360 (defcustom rmail-secondary-file-directory "~/"
361 "*Directory for additional secondary Rmail files."
362 :type 'directory
363 :group 'rmail-files)
364 ;;;###autoload
365 (defcustom rmail-secondary-file-regexp "\\.xmail$"
366 "*Regexp for which files are secondary Rmail files."
367 :type 'regexp
368 :group 'rmail-files)
369
370 ;;;###autoload
371 (defcustom rmail-confirm-expunge 'y-or-n-p
372 "*Whether and how to ask for confirmation before expunging deleted messages."
373 :type '(choice (const :tag "No confirmation" nil)
374 (const :tag "Confirm with y-or-n-p" y-or-n-p)
375 (const :tag "Confirm with yes-or-no-p" yes-or-no-p))
376 :version "21.1"
377 :group 'rmail-files)
378
379 ;;;###autoload
380 (defvar rmail-mode-hook nil
381 "List of functions to call when Rmail is invoked.")
382
383 ;;;###autoload
384 (defvar rmail-get-new-mail-hook nil
385 "List of functions to call when Rmail has retrieved new mail.")
386
387 ;;;###autoload
388 (defcustom rmail-show-message-hook nil
389 "List of functions to call when Rmail displays a message."
390 :type 'hook
391 :options '(goto-address)
392 :group 'rmail)
393
394 ;;;###autoload
395 (defvar rmail-quit-hook nil
396 "List of functions to call when quitting out of Rmail.")
397
398 ;;;###autoload
399 (defvar rmail-delete-message-hook nil
400 "List of functions to call when Rmail deletes a message.
401 When the hooks are called, the message has been marked deleted but is
402 still the current message in the Rmail buffer.")
403
404 ;; These may be altered by site-init.el to match the format of mmdf files
405 ;; delimiting used on a given host (delim1 and delim2 from the config
406 ;; files).
407
408 (defvar rmail-mmdf-delim1 "^\001\001\001\001\n"
409 "Regexp marking the start of an mmdf message.")
410 (defvar rmail-mmdf-delim2 "^\001\001\001\001\n"
411 "Regexp marking the end of an mmdf message.")
412
413 (defcustom rmail-message-filter nil
414 "If non-nil, a filter function for new messages in RMAIL.
415 Called with region narrowed to the message, including headers,
416 before obeying `rmail-ignored-headers'."
417 :group 'rmail-headers
418 :type 'function)
419
420 (defcustom rmail-automatic-folder-directives nil
421 "List of directives specifying where to put a message.
422 Each element of the list is of the form:
423
424 (FOLDERNAME FIELD REGEXP [ FIELD REGEXP ] ... )
425
426 Where FOLDERNAME is the name of a BABYL format folder to put the
427 message. If any of the field regexp's are nil, then it is ignored.
428
429 If FOLDERNAME is \"/dev/null\", it is deleted.
430 If FOLDERNAME is nil then it is deleted, and skipped.
431
432 FIELD is the plain text name of a field in the message, such as
433 \"subject\" or \"from\". A FIELD of \"to\" will automatically include
434 all text from the \"cc\" field as well.
435
436 REGEXP is an expression to match in the preceeding specified FIELD.
437 FIELD/REGEXP pairs continue in the list.
438
439 examples:
440 (\"/dev/null\" \"from\" \"@spam.com\") ; delete all mail from spam.com
441 (\"RMS\" \"from\" \"rms@\") ; save all mail from RMS."
442 :group 'rmail
443 :version "21.1"
444 :type '(repeat (sexp :tag "Directive")))
445
446 (defvar rmail-reply-prefix "Re: "
447 "String to prepend to Subject line when replying to a message.")
448
449 ;; Some mailers use "Re(2):" or "Re^2:" or "Re: Re:" or "Re[2]:".
450 ;; This pattern should catch all the common variants.
451 (defvar rmail-reply-regexp "\\`\\(Re\\(([0-9]+)\\|\\[[0-9]+\\]\\|\\^[0-9]+\\)?: *\\)*"
452 "Regexp to delete from Subject line before inserting `rmail-reply-prefix'.")
453
454 (defcustom rmail-display-summary nil
455 "*If non-nil, Rmail always displays the summary buffer."
456 :group 'rmail-summary
457 :type 'boolean)
458 \f
459 (defvar rmail-inbox-list nil)
460 (put 'rmail-inbox-list 'permanent-local t)
461
462 (defvar rmail-keywords nil)
463 (put 'rmail-keywords 'permanent-local t)
464
465 (defvar rmail-buffer nil
466 "The RMAIL buffer related to the current buffer.
467 In an RMAIL buffer, this holds the RMAIL buffer itself.
468 In a summary buffer, this holds the RMAIL buffer it is a summary for.")
469 (put 'rmail-buffer 'permanent-local t)
470
471 ;; Message counters and markers. Deleted flags.
472
473 (defvar rmail-current-message nil)
474 (put 'rmail-current-message 'permanent-local t)
475
476 (defvar rmail-total-messages nil)
477 (put 'rmail-total-messages 'permanent-local t)
478
479 (defvar rmail-message-vector nil)
480 (put 'rmail-message-vector 'permanent-local t)
481
482 (defvar rmail-deleted-vector nil)
483 (put 'rmail-deleted-vector 'permanent-local t)
484
485 (defvar rmail-msgref-vector nil
486 "In an Rmail buffer, a vector whose Nth element is a list (N).
487 When expunging renumbers messages, these lists are modified
488 by substituting the new message number into the existing list.")
489 (put 'rmail-msgref-vector 'permanent-local t)
490
491 (defvar rmail-overlay-list nil)
492 (put 'rmail-overlay-list 'permanent-local t)
493
494 ;; These are used by autoloaded rmail-summary.
495
496 (defvar rmail-summary-buffer nil)
497 (put 'rmail-summary-buffer 'permanent-local t)
498 (defvar rmail-summary-vector nil)
499 (put 'rmail-summary-vector 'permanent-local t)
500
501 (defvar rmail-view-buffer nil
502 "Buffer which holds RMAIL message for MIME displaying.")
503 (put 'rmail-view-buffer 'permanent-local t)
504 \f
505 ;; `Sticky' default variables.
506
507 ;; Last individual label specified to a or k.
508 (defvar rmail-last-label nil)
509 (put 'rmail-last-label 'permanent-local t)
510
511 ;; Last set of values specified to C-M-n, C-M-p, C-M-s or C-M-l.
512 (defvar rmail-last-multi-labels nil)
513
514 (defvar rmail-last-regexp nil)
515 (put 'rmail-last-regexp 'permanent-local t)
516
517 (defcustom rmail-default-file "~/xmail"
518 "*Default file name for \\[rmail-output]."
519 :type 'file
520 :group 'rmail-files)
521 (defcustom rmail-default-rmail-file "~/XMAIL"
522 "*Default file name for \\[rmail-output-to-rmail-file]."
523 :type 'file
524 :group 'rmail-files)
525 (defcustom rmail-default-body-file "~/mailout"
526 "*Default file name for \\[rmail-output-body-to-file]."
527 :type 'file
528 :group 'rmail-files
529 :version "20.3")
530
531 ;; Mule and MIME related variables.
532
533 ;;;###autoload
534 (defvar rmail-file-coding-system nil
535 "Coding system used in RMAIL file.
536
537 This is set to nil by default.")
538
539 ;;;###autoload
540 (defcustom rmail-enable-mime nil
541 "*If non-nil, RMAIL uses MIME feature.
542 If the value is t, RMAIL automatically shows MIME decoded message.
543 If the value is neither t nor nil, RMAIL does not show MIME decoded message
544 until a user explicitly requires it."
545 :type '(choice (const :tag "on" t)
546 (const :tag "off" nil)
547 (other :tag "when asked" ask))
548 :group 'rmail)
549
550 (defvar rmail-enable-mime-composing nil
551 "*If non-nil, RMAIL uses `rmail-insert-mime-forwarded-message-function' to forward.")
552
553 ;;;###autoload
554 (defvar rmail-show-mime-function nil
555 "Function to show MIME decoded message of RMAIL file.
556 This function is called when `rmail-enable-mime' is non-nil.
557 It is called with no argument.")
558
559 ;;;###autoload
560 (defvar rmail-insert-mime-forwarded-message-function nil
561 "Function to insert a message in MIME format so it can be forwarded.
562 This function is called if `rmail-enable-mime' or
563 `rmail-enable-mime-composing' is non-nil.
564 It is called with one argument FORWARD-BUFFER, which is a
565 buffer containing the message to forward. The current buffer
566 is the outgoing mail buffer.")
567
568 ;;;###autoload
569 (defvar rmail-insert-mime-resent-message-function nil
570 "Function to insert a message in MIME format so it can be resent.
571 This function is called if `rmail-enable-mime' is non-nil.
572 It is called with one argument FORWARD-BUFFER, which is a
573 buffer containing the message to forward. The current buffer
574 is the outgoing mail buffer.")
575
576 ;;;###autoload
577 (defvar rmail-search-mime-message-function nil
578 "Function to check if a regexp matches a MIME message.
579 This function is called if `rmail-enable-mime' is non-nil.
580 It is called with two arguments MSG and REGEXP, where
581 MSG is the message number, REGEXP is the regular expression.")
582
583 ;;;###autoload
584 (defvar rmail-search-mime-header-function nil
585 "Function to check if a regexp matches a header of MIME message.
586 This function is called if `rmail-enable-mime' is non-nil.
587 It is called with three arguments MSG, REGEXP, and LIMIT, where
588 MSG is the message number,
589 REGEXP is the regular expression,
590 LIMIT is the position specifying the end of header.")
591
592 ;;;###autoload
593 (defvar rmail-mime-feature 'rmail-mime
594 "Feature to require to load MIME support in Rmail.
595 When starting Rmail, if `rmail-enable-mime' is non-nil,
596 this feature is required with `require'.")
597
598 ;;;###autoload
599 (defvar rmail-decode-mime-charset t
600 "*Non-nil means a message is decoded by MIME's charset specification.
601 If this variable is nil, or the message has not MIME specification,
602 the message is decoded as normal way.
603
604 If the variable `rmail-enable-mime' is non-nil, this variables is
605 ignored, and all the decoding work is done by a feature specified by
606 the variable `rmail-mime-feature'.")
607
608 ;;;###autoload
609 (defvar rmail-mime-charset-pattern
610 "^content-type:[ ]*text/plain;[ \t\n]*charset=\"?\\([^ \t\n\";]+\\)\"?"
611 "Regexp to match MIME-charset specification in a header of message.
612 The first parenthesized expression should match the MIME-charset name.")
613
614 \f
615 ;;; Regexp matching the delimiter of messages in UNIX mail format
616 ;;; (UNIX From lines), minus the initial ^. Note that if you change
617 ;;; this expression, you must change the code in rmail-nuke-pinhead-header
618 ;;; that knows the exact ordering of the \\( \\) subexpressions.
619 (defvar rmail-unix-mail-delimiter
620 (let ((time-zone-regexp
621 (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"
622 "\\|[-+]?[0-9][0-9][0-9][0-9]"
623 "\\|"
624 "\\) *")))
625 (concat
626 "From "
627
628 ;; Many things can happen to an RFC 822 mailbox before it is put into
629 ;; a `From' line. The leading phrase can be stripped, e.g.
630 ;; `Joe <@w.x:joe@y.z>' -> `<@w.x:joe@y.z>'. The <> can be stripped, e.g.
631 ;; `<@x.y:joe@y.z>' -> `@x.y:joe@y.z'. Everything starting with a CRLF
632 ;; can be removed, e.g.
633 ;; From: joe@y.z (Joe K
634 ;; User)
635 ;; can yield `From joe@y.z (Joe K Fri Mar 22 08:11:15 1996', and
636 ;; From: Joe User
637 ;; <joe@y.z>
638 ;; can yield `From Joe User Fri Mar 22 08:11:15 1996'.
639 ;; The mailbox can be removed or be replaced by white space, e.g.
640 ;; From: "Joe User"{space}{tab}
641 ;; <joe@y.z>
642 ;; can yield `From {space}{tab} Fri Mar 22 08:11:15 1996',
643 ;; where {space} and {tab} represent the Ascii space and tab characters.
644 ;; We want to match the results of any of these manglings.
645 ;; The following regexp rejects names whose first characters are
646 ;; obviously bogus, but after that anything goes.
647 "\\([^\0-\b\n-\r\^?].*\\)? "
648
649 ;; The time the message was sent.
650 "\\([^\0-\r \^?]+\\) +" ; day of the week
651 "\\([^\0-\r \^?]+\\) +" ; month
652 "\\([0-3]?[0-9]\\) +" ; day of month
653 "\\([0-2][0-9]:[0-5][0-9]\\(:[0-6][0-9]\\)?\\) *" ; time of day
654
655 ;; Perhaps a time zone, specified by an abbreviation, or by a
656 ;; numeric offset.
657 time-zone-regexp
658
659 ;; The year.
660 " \\([0-9][0-9]+\\) *"
661
662 ;; On some systems the time zone can appear after the year, too.
663 time-zone-regexp
664
665 ;; Old uucp cruft.
666 "\\(remote from .*\\)?"
667
668 "\n"))
669 nil)
670
671 (defvar rmail-font-lock-keywords
672 ;; These are all matched case-insensitively.
673 (eval-when-compile
674 (let* ((cite-chars "[>|}]")
675 (cite-prefix "a-z")
676 (cite-suffix (concat cite-prefix "0-9_.@-`'\"")))
677 (list '("^\\(From\\|Sender\\|Resent-From\\):"
678 . font-lock-function-name-face)
679 '("^Reply-To:.*$" . font-lock-function-name-face)
680 '("^Subject:" . font-lock-comment-face)
681 '("^X-Spam-Status:" . font-lock-keyword-face)
682 '("^\\(To\\|Apparently-To\\|Cc\\|Newsgroups\\):"
683 . font-lock-keyword-face)
684 ;; Use MATCH-ANCHORED to effectively anchor the regexp left side.
685 `(,cite-chars
686 (,(concat "\\=[ \t]*"
687 "\\(\\(\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
688 "\\(" cite-chars "[ \t]*\\)\\)+\\)"
689 "\\(.*\\)")
690 (beginning-of-line) (end-of-line)
691 (1 font-lock-comment-delimiter-face nil t)
692 (5 font-lock-comment-face nil t)))
693 '("^\\(X-[a-z0-9-]+\\|In-reply-to\\|Date\\):.*\\(\n[ \t]+.*\\)*$"
694 . font-lock-string-face))))
695 "Additional expressions to highlight in Rmail mode.")
696
697 ;; Perform BODY in the summary buffer
698 ;; in such a way that its cursor is properly updated in its own window.
699 (defmacro rmail-select-summary (&rest body)
700 `(let ((total rmail-total-messages))
701 (if (rmail-summary-displayed)
702 (let ((window (selected-window)))
703 (save-excursion
704 (unwind-protect
705 (progn
706 (pop-to-buffer rmail-summary-buffer)
707 ;; rmail-total-messages is a buffer-local var
708 ;; in the rmail buffer.
709 ;; This way we make it available for the body
710 ;; even tho the rmail buffer is not current.
711 (let ((rmail-total-messages total))
712 ,@body))
713 (select-window window))))
714 (save-excursion
715 (set-buffer rmail-summary-buffer)
716 (let ((rmail-total-messages total))
717 ,@body)))
718 (rmail-maybe-display-summary)))
719 \f
720 ;;;; *** Rmail Mode ***
721
722 ;; This variable is dynamically bound. The defvar is here to placate
723 ;; the byte compiler.
724
725 (defvar rmail-enable-multibyte nil)
726
727
728 (defun rmail-require-mime-maybe ()
729 "Require `rmail-mime-feature' if that is non-nil.
730 Signal an error and set `rmail-mime-feature' to nil if the feature
731 isn't provided."
732 (when rmail-enable-mime
733 (condition-case err
734 (require rmail-mime-feature)
735 (error
736 (message "Feature `%s' not provided" rmail-mime-feature)
737 (sit-for 1)
738 (setq rmail-enable-mime nil)))))
739
740
741 ;;;###autoload
742 (defun rmail (&optional file-name-arg)
743 "Read and edit incoming mail.
744 Moves messages into file named by `rmail-file-name' (a babyl format file)
745 and edits that file in RMAIL Mode.
746 Type \\[describe-mode] once editing that file, for a list of RMAIL commands.
747
748 May be called with file name as argument; then performs rmail editing on
749 that file, but does not copy any new mail into the file.
750 Interactively, if you supply a prefix argument, then you
751 have a chance to specify a file name with the minibuffer.
752
753 If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
754 (interactive (if current-prefix-arg
755 (list (read-file-name "Run rmail on RMAIL file: "))))
756 (rmail-require-mime-maybe)
757 (let* ((file-name (expand-file-name (or file-name-arg rmail-file-name)))
758 ;; Use find-buffer-visiting, not get-file-buffer, for those users
759 ;; who have find-file-visit-truename set to t.
760 (existed (find-buffer-visiting file-name))
761 ;; This binding is necessary because we must decide if we
762 ;; need code conversion while the buffer is unibyte
763 ;; (i.e. enable-multibyte-characters is nil).
764 (rmail-enable-multibyte
765 (if existed
766 (with-current-buffer existed enable-multibyte-characters)
767 (default-value 'enable-multibyte-characters)))
768 ;; Since the file may contain messages of different encodings
769 ;; at the tail (non-BYBYL part), we can't decode them at once
770 ;; on reading. So, at first, we read the file without text
771 ;; code conversion, then decode the messages one by one by
772 ;; rmail-decode-babyl-format or
773 ;; rmail-convert-to-babyl-format.
774 (coding-system-for-read (and rmail-enable-multibyte 'raw-text))
775 run-mail-hook msg-shown)
776 ;; Like find-file, but in the case where a buffer existed
777 ;; and the file was reverted, recompute the message-data.
778 ;; We used to bind enable-local-variables to nil here,
779 ;; but that should not be needed now that rmail-mode
780 ;; sets it locally to nil.
781 ;; (Binding a variable locally with let is not safe if it has
782 ;; buffer-local bindings.)
783 (if (and existed (not (verify-visited-file-modtime existed)))
784 (progn
785 (find-file file-name)
786 (if (and (verify-visited-file-modtime existed)
787 (eq major-mode 'rmail-mode))
788 (progn (rmail-forget-messages)
789 (rmail-set-message-counters))))
790 (switch-to-buffer
791 (let ((enable-local-variables nil))
792 (find-file-noselect file-name))))
793 (if (eq major-mode 'rmail-edit-mode)
794 (error "Exit Rmail Edit mode before getting new mail"))
795 (if (and existed (> (buffer-size) 0))
796 ;; Buffer not new and not empty; ensure in proper mode, but that's all.
797 (or (eq major-mode 'rmail-mode)
798 (progn (rmail-mode-2)
799 (setq run-mail-hook t)))
800 (setq run-mail-hook t)
801 (rmail-mode-2)
802 ;; Convert all or part to Babyl file if possible.
803 (rmail-convert-file)
804 (goto-char (point-max)))
805 ;; As we have read a file by raw-text, the buffer is set to
806 ;; unibyte. We must make it multibyte if necessary.
807 (if (and rmail-enable-multibyte
808 (not enable-multibyte-characters))
809 (set-buffer-multibyte t))
810 ;; If necessary, scan to find all the messages.
811 (rmail-maybe-set-message-counters)
812 (unwind-protect
813 (unless (and (not file-name-arg)
814 (rmail-get-new-mail))
815 (rmail-show-message (rmail-first-unseen-message)))
816 (progn
817 (if rmail-display-summary (rmail-summary))
818 (rmail-construct-io-menu)
819 (if run-mail-hook
820 (run-hooks 'rmail-mode-hook))))))
821
822 ;; Given the value of MAILPATH, return a list of inbox file names.
823 ;; This is turned off because it is not clear that the user wants
824 ;; all these inboxes to feed into the primary rmail file.
825 ; (defun rmail-convert-mailpath (string)
826 ; (let (idx list)
827 ; (while (setq idx (string-match "[%:]" string))
828 ; (let ((this (substring string 0 idx)))
829 ; (setq string (substring string (1+ idx)))
830 ; (setq list (cons (if (string-match "%" this)
831 ; (substring this 0 (string-match "%" this))
832 ; this)
833 ; list))))
834 ; list))
835
836 ; I have checked that adding "-*- rmail -*-" to the BABYL OPTIONS line
837 ; will not cause emacs 18.55 problems.
838
839 ;; This calls rmail-decode-babyl-format if the file is already Babyl.
840
841 (defun rmail-convert-file ()
842 (let (convert)
843 (widen)
844 (goto-char (point-min))
845 ;; If file doesn't start like a Babyl file,
846 ;; convert it to one, by adding a header and converting each message.
847 (cond ((looking-at "BABYL OPTIONS:"))
848 ((looking-at "Version: 5\n")
849 ;; Losing babyl file made by old version of Rmail.
850 ;; Just fix the babyl file header; don't make a new one,
851 ;; so we don't lose the Labels: file attribute, etc.
852 (let ((buffer-read-only nil))
853 (insert "BABYL OPTIONS: -*- rmail -*-\n")))
854 ((equal (point-min) (point-max))
855 ;; Empty RMAIL file. Just insert the header.
856 (rmail-insert-rmail-file-header))
857 (t
858 ;; Non-empty file in non-RMAIL format. Add header and convert.
859 (setq convert t)
860 (rmail-insert-rmail-file-header)))
861 ;; If file was not a Babyl file or if there are
862 ;; Unix format messages added at the end,
863 ;; convert file as necessary.
864 (if (or convert
865 (save-excursion
866 (goto-char (point-max))
867 (search-backward "\n\^_")
868 (forward-char 2)
869 (looking-at "\n*From ")))
870 (let ((buffer-read-only nil))
871 (message "Converting to Babyl format...")
872 ;; If file needs conversion, convert it all,
873 ;; except for the BABYL header.
874 ;; (rmail-convert-to-babyl-format would delete the header.)
875 (goto-char (point-min))
876 (search-forward "\n\^_" nil t)
877 (narrow-to-region (point) (point-max))
878 (rmail-convert-to-babyl-format)
879 (message "Converting to Babyl format...done"))
880 (if (and (not rmail-enable-mime)
881 rmail-enable-multibyte)
882 ;; We still have to decode BABYL part.
883 (rmail-decode-babyl-format)))))
884
885 (defun rmail-insert-rmail-file-header ()
886 (let ((buffer-read-only nil))
887 ;; -*-rmail-*- is here so that visiting the file normally
888 ;; recognizes it as an Rmail file.
889 (insert "BABYL OPTIONS: -*- rmail -*-
890 Version: 5
891 Labels:
892 Note: This is the header of an rmail file.
893 Note: If you are seeing it in rmail,
894 Note: it means the file has no messages in it.\n\^_")))
895
896 ;; Decode Babyl formatted part at the head of current buffer by
897 ;; rmail-file-coding-system, or if it is nil, do auto conversion.
898
899 (defun rmail-decode-babyl-format ()
900 (let ((modifiedp (buffer-modified-p))
901 (buffer-read-only nil)
902 (coding-system rmail-file-coding-system)
903 from to)
904 (goto-char (point-min))
905 (search-forward "\n\^_" nil t) ; Skip BABYL header.
906 (setq from (point))
907 (goto-char (point-max))
908 (search-backward "\n\^_" from 'mv)
909 (setq to (point))
910 (unless (and coding-system
911 (coding-system-p coding-system))
912 (setq coding-system
913 ;; If rmail-file-coding-system is nil, Emacs 21 writes
914 ;; RMAIL files in emacs-mule, Emacs 22 in utf-8, but
915 ;; earlier versions did that with the current buffer's
916 ;; encoding. So we want to favor detection of emacs-mule
917 ;; (whose normal priority is quite low) and utf-8, but
918 ;; still allow detection of other encodings if they won't
919 ;; fit. The call to with-coding-priority below achieves
920 ;; that.
921 (with-coding-priority '(emacs-mule utf-8)
922 (detect-coding-region from to 'highest))))
923 (unless (eq (coding-system-type coding-system) 'undecided)
924 (set-buffer-modified-p t) ; avoid locking when decoding
925 (let ((buffer-undo-list t))
926 (decode-coding-region from to coding-system))
927 (setq coding-system last-coding-system-used))
928 (set-buffer-modified-p modifiedp)
929 (setq buffer-file-coding-system nil)
930 (setq save-buffer-coding-system
931 (or coding-system 'undecided))))
932
933 (defvar rmail-mode-map nil)
934 (if rmail-mode-map
935 nil
936 (setq rmail-mode-map (make-keymap))
937 (suppress-keymap rmail-mode-map)
938 (define-key rmail-mode-map "a" 'rmail-add-label)
939 (define-key rmail-mode-map "b" 'rmail-bury)
940 (define-key rmail-mode-map "c" 'rmail-continue)
941 (define-key rmail-mode-map "d" 'rmail-delete-forward)
942 (define-key rmail-mode-map "\C-d" 'rmail-delete-backward)
943 (define-key rmail-mode-map "e" 'rmail-edit-current-message)
944 (define-key rmail-mode-map "f" 'rmail-forward)
945 (define-key rmail-mode-map "g" 'rmail-get-new-mail)
946 (define-key rmail-mode-map "h" 'rmail-summary)
947 (define-key rmail-mode-map "i" 'rmail-input)
948 (define-key rmail-mode-map "j" 'rmail-show-message)
949 (define-key rmail-mode-map "k" 'rmail-kill-label)
950 (define-key rmail-mode-map "l" 'rmail-summary-by-labels)
951 (define-key rmail-mode-map "\e\C-h" 'rmail-summary)
952 (define-key rmail-mode-map "\e\C-l" 'rmail-summary-by-labels)
953 (define-key rmail-mode-map "\e\C-r" 'rmail-summary-by-recipients)
954 (define-key rmail-mode-map "\e\C-s" 'rmail-summary-by-regexp)
955 (define-key rmail-mode-map "\e\C-t" 'rmail-summary-by-topic)
956 (define-key rmail-mode-map "m" 'rmail-mail)
957 (define-key rmail-mode-map "\em" 'rmail-retry-failure)
958 (define-key rmail-mode-map "n" 'rmail-next-undeleted-message)
959 (define-key rmail-mode-map "\en" 'rmail-next-message)
960 (define-key rmail-mode-map "\e\C-n" 'rmail-next-labeled-message)
961 (define-key rmail-mode-map "o" 'rmail-output-to-rmail-file)
962 (define-key rmail-mode-map "\C-o" 'rmail-output)
963 (define-key rmail-mode-map "p" 'rmail-previous-undeleted-message)
964 (define-key rmail-mode-map "\ep" 'rmail-previous-message)
965 (define-key rmail-mode-map "\e\C-p" 'rmail-previous-labeled-message)
966 (define-key rmail-mode-map "q" 'rmail-quit)
967 (define-key rmail-mode-map "r" 'rmail-reply)
968 ;; I find I can't live without the default M-r command -- rms.
969 ;; (define-key rmail-mode-map "\er" 'rmail-search-backwards)
970 (define-key rmail-mode-map "s" 'rmail-expunge-and-save)
971 (define-key rmail-mode-map "\es" 'rmail-search)
972 (define-key rmail-mode-map "t" 'rmail-toggle-header)
973 (define-key rmail-mode-map "u" 'rmail-undelete-previous-message)
974 (define-key rmail-mode-map "w" 'rmail-output-body-to-file)
975 (define-key rmail-mode-map "x" 'rmail-expunge)
976 (define-key rmail-mode-map "." 'rmail-beginning-of-message)
977 (define-key rmail-mode-map "/" 'rmail-end-of-message)
978 (define-key rmail-mode-map "<" 'rmail-first-message)
979 (define-key rmail-mode-map ">" 'rmail-last-message)
980 (define-key rmail-mode-map " " 'scroll-up)
981 (define-key rmail-mode-map "\177" 'scroll-down)
982 (define-key rmail-mode-map "?" 'describe-mode)
983 (define-key rmail-mode-map "\C-c\C-s\C-d" 'rmail-sort-by-date)
984 (define-key rmail-mode-map "\C-c\C-s\C-s" 'rmail-sort-by-subject)
985 (define-key rmail-mode-map "\C-c\C-s\C-a" 'rmail-sort-by-author)
986 (define-key rmail-mode-map "\C-c\C-s\C-r" 'rmail-sort-by-recipient)
987 (define-key rmail-mode-map "\C-c\C-s\C-c" 'rmail-sort-by-correspondent)
988 (define-key rmail-mode-map "\C-c\C-s\C-l" 'rmail-sort-by-lines)
989 (define-key rmail-mode-map "\C-c\C-s\C-k" 'rmail-sort-by-labels)
990 (define-key rmail-mode-map "\C-c\C-n" 'rmail-next-same-subject)
991 (define-key rmail-mode-map "\C-c\C-p" 'rmail-previous-same-subject)
992 )
993 \f
994 (define-key rmail-mode-map [menu-bar] (make-sparse-keymap))
995
996 (define-key rmail-mode-map [menu-bar classify]
997 (cons "Classify" (make-sparse-keymap "Classify")))
998
999 (define-key rmail-mode-map [menu-bar classify input-menu]
1000 nil)
1001
1002 (define-key rmail-mode-map [menu-bar classify output-menu]
1003 nil)
1004
1005 (define-key rmail-mode-map [menu-bar classify output-body]
1006 '("Output body to file..." . rmail-output-body-to-file))
1007
1008 (define-key rmail-mode-map [menu-bar classify output-inbox]
1009 '("Output (inbox)..." . rmail-output))
1010
1011 (define-key rmail-mode-map [menu-bar classify output]
1012 '("Output (Rmail)..." . rmail-output-to-rmail-file))
1013
1014 (define-key rmail-mode-map [menu-bar classify kill-label]
1015 '("Kill Label..." . rmail-kill-label))
1016
1017 (define-key rmail-mode-map [menu-bar classify add-label]
1018 '("Add Label..." . rmail-add-label))
1019
1020 (define-key rmail-mode-map [menu-bar summary]
1021 (cons "Summary" (make-sparse-keymap "Summary")))
1022
1023 (define-key rmail-mode-map [menu-bar summary senders]
1024 '("By Senders..." . rmail-summary-by-senders))
1025
1026 (define-key rmail-mode-map [menu-bar summary labels]
1027 '("By Labels..." . rmail-summary-by-labels))
1028
1029 (define-key rmail-mode-map [menu-bar summary recipients]
1030 '("By Recipients..." . rmail-summary-by-recipients))
1031
1032 (define-key rmail-mode-map [menu-bar summary topic]
1033 '("By Topic..." . rmail-summary-by-topic))
1034
1035 (define-key rmail-mode-map [menu-bar summary regexp]
1036 '("By Regexp..." . rmail-summary-by-regexp))
1037
1038 (define-key rmail-mode-map [menu-bar summary all]
1039 '("All" . rmail-summary))
1040
1041 (define-key rmail-mode-map [menu-bar mail]
1042 (cons "Mail" (make-sparse-keymap "Mail")))
1043
1044 (define-key rmail-mode-map [menu-bar mail rmail-get-new-mail]
1045 '("Get New Mail" . rmail-get-new-mail))
1046
1047 (define-key rmail-mode-map [menu-bar mail lambda]
1048 '("----"))
1049
1050 (define-key rmail-mode-map [menu-bar mail continue]
1051 '("Continue" . rmail-continue))
1052
1053 (define-key rmail-mode-map [menu-bar mail resend]
1054 '("Re-send..." . rmail-resend))
1055
1056 (define-key rmail-mode-map [menu-bar mail forward]
1057 '("Forward" . rmail-forward))
1058
1059 (define-key rmail-mode-map [menu-bar mail retry]
1060 '("Retry" . rmail-retry-failure))
1061
1062 (define-key rmail-mode-map [menu-bar mail reply]
1063 '("Reply" . rmail-reply))
1064
1065 (define-key rmail-mode-map [menu-bar mail mail]
1066 '("Mail" . rmail-mail))
1067
1068 (define-key rmail-mode-map [menu-bar delete]
1069 (cons "Delete" (make-sparse-keymap "Delete")))
1070
1071 (define-key rmail-mode-map [menu-bar delete expunge/save]
1072 '("Expunge/Save" . rmail-expunge-and-save))
1073
1074 (define-key rmail-mode-map [menu-bar delete expunge]
1075 '("Expunge" . rmail-expunge))
1076
1077 (define-key rmail-mode-map [menu-bar delete undelete]
1078 '("Undelete" . rmail-undelete-previous-message))
1079
1080 (define-key rmail-mode-map [menu-bar delete delete]
1081 '("Delete" . rmail-delete-forward))
1082
1083 (define-key rmail-mode-map [menu-bar move]
1084 (cons "Move" (make-sparse-keymap "Move")))
1085
1086 (define-key rmail-mode-map [menu-bar move search-back]
1087 '("Search Back..." . rmail-search-backwards))
1088
1089 (define-key rmail-mode-map [menu-bar move search]
1090 '("Search..." . rmail-search))
1091
1092 (define-key rmail-mode-map [menu-bar move previous]
1093 '("Previous Nondeleted" . rmail-previous-undeleted-message))
1094
1095 (define-key rmail-mode-map [menu-bar move next]
1096 '("Next Nondeleted" . rmail-next-undeleted-message))
1097
1098 (define-key rmail-mode-map [menu-bar move last]
1099 '("Last" . rmail-last-message))
1100
1101 (define-key rmail-mode-map [menu-bar move first]
1102 '("First" . rmail-first-message))
1103
1104 (define-key rmail-mode-map [menu-bar move previous]
1105 '("Previous" . rmail-previous-message))
1106
1107 (define-key rmail-mode-map [menu-bar move next]
1108 '("Next" . rmail-next-message))
1109 \f
1110 ;; Rmail mode is suitable only for specially formatted data.
1111 (put 'rmail-mode 'mode-class 'special)
1112
1113 (defun rmail-mode-kill-summary ()
1114 (if rmail-summary-buffer (kill-buffer rmail-summary-buffer)))
1115
1116 ;;;###autoload
1117 (defun rmail-mode ()
1118 "Rmail Mode is used by \\<rmail-mode-map>\\[rmail] for editing Rmail files.
1119 All normal editing commands are turned off.
1120 Instead, these commands are available:
1121
1122 \\[rmail-beginning-of-message] Move point to front of this message.
1123 \\[rmail-end-of-message] Move point to bottom of this message.
1124 \\[scroll-up] Scroll to next screen of this message.
1125 \\[scroll-down] Scroll to previous screen of this message.
1126 \\[rmail-next-undeleted-message] Move to Next non-deleted message.
1127 \\[rmail-previous-undeleted-message] Move to Previous non-deleted message.
1128 \\[rmail-next-message] Move to Next message whether deleted or not.
1129 \\[rmail-previous-message] Move to Previous message whether deleted or not.
1130 \\[rmail-first-message] Move to the first message in Rmail file.
1131 \\[rmail-last-message] Move to the last message in Rmail file.
1132 \\[rmail-show-message] Jump to message specified by numeric position in file.
1133 \\[rmail-search] Search for string and show message it is found in.
1134 \\[rmail-delete-forward] Delete this message, move to next nondeleted.
1135 \\[rmail-delete-backward] Delete this message, move to previous nondeleted.
1136 \\[rmail-undelete-previous-message] Undelete message. Tries current message, then earlier messages
1137 till a deleted message is found.
1138 \\[rmail-edit-current-message] Edit the current message. \\[rmail-cease-edit] to return to Rmail.
1139 \\[rmail-expunge] Expunge deleted messages.
1140 \\[rmail-expunge-and-save] Expunge and save the file.
1141 \\[rmail-quit] Quit Rmail: expunge, save, then switch to another buffer.
1142 \\[save-buffer] Save without expunging.
1143 \\[rmail-get-new-mail] Move new mail from system spool directory into this file.
1144 \\[rmail-mail] Mail a message (same as \\[mail-other-window]).
1145 \\[rmail-continue] Continue composing outgoing message started before.
1146 \\[rmail-reply] Reply to this message. Like \\[rmail-mail] but initializes some fields.
1147 \\[rmail-retry-failure] Send this message again. Used on a mailer failure message.
1148 \\[rmail-forward] Forward this message to another user.
1149 \\[rmail-output-to-rmail-file] Output this message to an Rmail file (append it).
1150 \\[rmail-output] Output this message to a Unix-format mail file (append it).
1151 \\[rmail-output-body-to-file] Save message body to a file. Default filename comes from Subject line.
1152 \\[rmail-input] Input Rmail file. Run Rmail on that file.
1153 \\[rmail-add-label] Add label to message. It will be displayed in the mode line.
1154 \\[rmail-kill-label] Kill label. Remove a label from current message.
1155 \\[rmail-next-labeled-message] Move to Next message with specified label
1156 (label defaults to last one specified).
1157 Standard labels: filed, unseen, answered, forwarded, deleted.
1158 Any other label is present only if you add it with \\[rmail-add-label].
1159 \\[rmail-previous-labeled-message] Move to Previous message with specified label
1160 \\[rmail-summary] Show headers buffer, with a one line summary of each message.
1161 \\[rmail-summary-by-labels] Summarize only messages with particular label(s).
1162 \\[rmail-summary-by-recipients] Summarize only messages with particular recipient(s).
1163 \\[rmail-summary-by-regexp] Summarize only messages with particular regexp(s).
1164 \\[rmail-summary-by-topic] Summarize only messages with subject line regexp(s).
1165 \\[rmail-toggle-header] Toggle display of complete header."
1166 (interactive)
1167 (let ((finding-rmail-file (not (eq major-mode 'rmail-mode))))
1168 (rmail-mode-2)
1169 (when (and finding-rmail-file
1170 (null coding-system-for-read)
1171 default-enable-multibyte-characters)
1172 (let ((rmail-enable-multibyte t))
1173 (rmail-require-mime-maybe)
1174 (rmail-convert-file)
1175 (goto-char (point-max))
1176 (set-buffer-multibyte t)))
1177 (rmail-set-message-counters)
1178 (rmail-show-message rmail-total-messages)
1179 (when finding-rmail-file
1180 (when rmail-display-summary
1181 (rmail-summary))
1182 (rmail-construct-io-menu))
1183 (run-mode-hooks 'rmail-mode-hook)))
1184
1185 (defun rmail-mode-2 ()
1186 (kill-all-local-variables)
1187 (rmail-mode-1)
1188 (rmail-perm-variables)
1189 (rmail-variables))
1190
1191 (defun rmail-mode-1 ()
1192 (setq major-mode 'rmail-mode)
1193 (setq mode-name "RMAIL")
1194 (setq buffer-read-only t)
1195 ;; No need to auto save RMAIL files in normal circumstances
1196 ;; because they contain no info except attribute changes
1197 ;; and deletion of messages.
1198 ;; The one exception is when messages are copied into an Rmail mode buffer.
1199 ;; rmail-output-to-rmail-file enables auto save when you do that.
1200 (setq buffer-auto-save-file-name nil)
1201 (setq mode-line-modified "--")
1202 (use-local-map rmail-mode-map)
1203 (set-syntax-table text-mode-syntax-table)
1204 (setq local-abbrev-table text-mode-abbrev-table))
1205
1206 ;; Set up the permanent locals associated with an Rmail file.
1207 (defun rmail-perm-variables ()
1208 (make-local-variable 'rmail-last-label)
1209 (make-local-variable 'rmail-last-regexp)
1210 (make-local-variable 'rmail-deleted-vector)
1211 (make-local-variable 'rmail-buffer)
1212 (setq rmail-buffer (current-buffer))
1213 (make-local-variable 'rmail-view-buffer)
1214 (setq rmail-view-buffer rmail-buffer)
1215 (make-local-variable 'rmail-summary-buffer)
1216 (make-local-variable 'rmail-summary-vector)
1217 (make-local-variable 'rmail-current-message)
1218 (make-local-variable 'rmail-total-messages)
1219 (make-local-variable 'rmail-overlay-list)
1220 (setq rmail-overlay-list nil)
1221 (make-local-variable 'rmail-message-vector)
1222 (make-local-variable 'rmail-msgref-vector)
1223 (make-local-variable 'rmail-inbox-list)
1224 (setq rmail-inbox-list (rmail-parse-file-inboxes))
1225 ;; Provide default set of inboxes for primary mail file ~/RMAIL.
1226 (and (null rmail-inbox-list)
1227 (or (equal buffer-file-name (expand-file-name rmail-file-name))
1228 (equal buffer-file-truename
1229 (abbreviate-file-name (file-truename rmail-file-name))))
1230 (setq rmail-inbox-list
1231 (or rmail-primary-inbox-list
1232 (list (or (getenv "MAIL")
1233 (concat rmail-spool-directory
1234 (user-login-name)))))))
1235 (make-local-variable 'rmail-keywords)
1236 ;; this gets generated as needed
1237 (setq rmail-keywords nil))
1238
1239 ;; Set up the non-permanent locals associated with Rmail mode.
1240 (defun rmail-variables ()
1241 (make-local-variable 'save-buffer-coding-system)
1242 ;; If we don't already have a value for save-buffer-coding-system,
1243 ;; get it from buffer-file-coding-system, and clear that
1244 ;; because it should be determined in rmail-show-message.
1245 (unless save-buffer-coding-system
1246 (setq save-buffer-coding-system (or buffer-file-coding-system 'undecided))
1247 (setq buffer-file-coding-system nil))
1248 ;; Don't let a local variables list in a message cause confusion.
1249 (make-local-variable 'local-enable-local-variables)
1250 (setq local-enable-local-variables nil)
1251 (make-local-variable 'revert-buffer-function)
1252 (setq revert-buffer-function 'rmail-revert)
1253 (make-local-variable 'font-lock-defaults)
1254 (setq font-lock-defaults
1255 '(rmail-font-lock-keywords
1256 t t nil nil
1257 (font-lock-maximum-size . nil)
1258 (font-lock-fontify-buffer-function . rmail-fontify-buffer-function)
1259 (font-lock-unfontify-buffer-function . rmail-unfontify-buffer-function)
1260 (font-lock-inhibit-thing-lock . (lazy-lock-mode fast-lock-mode))))
1261 (make-local-variable 'require-final-newline)
1262 (setq require-final-newline nil)
1263 (make-local-variable 'version-control)
1264 (setq version-control 'never)
1265 (make-local-variable 'kill-buffer-hook)
1266 (add-hook 'kill-buffer-hook 'rmail-mode-kill-summary)
1267 (make-local-variable 'file-precious-flag)
1268 (setq file-precious-flag t)
1269 (make-local-variable 'desktop-save-buffer)
1270 (setq desktop-save-buffer t))
1271
1272 ;; Handle M-x revert-buffer done in an rmail-mode buffer.
1273 (defun rmail-revert (arg noconfirm)
1274 (set-buffer rmail-buffer)
1275 (let* ((revert-buffer-function (default-value 'revert-buffer-function))
1276 (rmail-enable-multibyte enable-multibyte-characters)
1277 ;; See similar code in `rmail'.
1278 (coding-system-for-read (and rmail-enable-multibyte 'raw-text)))
1279 ;; Call our caller again, but this time it does the default thing.
1280 (if (revert-buffer arg noconfirm)
1281 ;; If the user said "yes", and we changed something,
1282 ;; reparse the messages.
1283 (progn
1284 (set-buffer rmail-buffer)
1285 (rmail-mode-2)
1286 ;; Convert all or part to Babyl file if possible.
1287 (rmail-convert-file)
1288 ;; We have read the file as raw-text, so the buffer is set to
1289 ;; unibyte. Make it multibyte if necessary.
1290 (if (and rmail-enable-multibyte
1291 (not enable-multibyte-characters))
1292 (set-buffer-multibyte t))
1293 (goto-char (point-max))
1294 (rmail-set-message-counters)
1295 (rmail-show-message rmail-total-messages)
1296 (run-hooks 'rmail-mode-hook)))))
1297
1298 ;; Return a list of files from this buffer's Mail: option.
1299 ;; Does not assume that messages have been parsed.
1300 ;; Just returns nil if buffer does not look like Babyl format.
1301 (defun rmail-parse-file-inboxes ()
1302 (save-excursion
1303 (save-restriction
1304 (widen)
1305 (goto-char 1)
1306 (cond ((looking-at "BABYL OPTIONS:")
1307 (search-forward "\n\^_" nil 'move)
1308 (narrow-to-region 1 (point))
1309 (goto-char 1)
1310 (if (search-forward "\nMail:" nil t)
1311 (progn
1312 (narrow-to-region (point) (progn (end-of-line) (point)))
1313 (goto-char (point-min))
1314 (mail-parse-comma-list))))))))
1315
1316 (defun rmail-expunge-and-save ()
1317 "Expunge and save RMAIL file."
1318 (interactive)
1319 (rmail-expunge)
1320 (set-buffer rmail-buffer)
1321 (save-buffer)
1322 (if (rmail-summary-exists)
1323 (rmail-select-summary (set-buffer-modified-p nil))))
1324
1325 (defun rmail-quit ()
1326 "Quit out of RMAIL.
1327 Hook `rmail-quit-hook' is run after expunging."
1328 (interactive)
1329 (rmail-expunge-and-save)
1330 (when (boundp 'rmail-quit-hook)
1331 (run-hooks 'rmail-quit-hook))
1332 ;; Don't switch to the summary buffer even if it was recently visible.
1333 (when rmail-summary-buffer
1334 (replace-buffer-in-windows rmail-summary-buffer)
1335 (bury-buffer rmail-summary-buffer))
1336 (if rmail-enable-mime
1337 (let ((obuf rmail-buffer)
1338 (ovbuf rmail-view-buffer))
1339 (set-buffer rmail-view-buffer)
1340 (quit-window)
1341 (replace-buffer-in-windows ovbuf)
1342 (replace-buffer-in-windows obuf)
1343 (bury-buffer obuf))
1344 (let ((obuf (current-buffer)))
1345 (quit-window)
1346 (replace-buffer-in-windows obuf))))
1347
1348 (defun rmail-bury ()
1349 "Bury current Rmail buffer and its summary buffer."
1350 (interactive)
1351 ;; This let var was called rmail-buffer, but that interfered
1352 ;; with the buffer-local var used in summary buffers.
1353 (let ((buffer-to-bury (current-buffer)))
1354 (if (rmail-summary-exists)
1355 (let (window)
1356 (while (setq window (get-buffer-window rmail-summary-buffer))
1357 (quit-window nil window))
1358 (bury-buffer rmail-summary-buffer)))
1359 (quit-window)))
1360
1361 (defun rmail-duplicate-message ()
1362 "Create a duplicated copy of the current message.
1363 The duplicate copy goes into the Rmail file just after the
1364 original copy."
1365 (interactive)
1366 (widen)
1367 (let ((buffer-read-only nil)
1368 (number rmail-current-message)
1369 (string (buffer-substring (rmail-msgbeg rmail-current-message)
1370 (rmail-msgend rmail-current-message))))
1371 (goto-char (rmail-msgend rmail-current-message))
1372 (insert string)
1373 (rmail-forget-messages)
1374 (rmail-show-message number)
1375 (message "Message duplicated")))
1376
1377 ;;;###autoload
1378 (defun rmail-input (filename)
1379 "Run Rmail on file FILENAME."
1380 (interactive "FRun rmail on RMAIL file: ")
1381 (rmail filename))
1382
1383
1384 ;; This used to scan subdirectories recursively, but someone pointed out
1385 ;; that if the user wants that, person can put all the files in one dir.
1386 ;; And the recursive scan was slow. So I took it out.
1387 ;; rms, Sep 1996.
1388 (defun rmail-find-all-files (start)
1389 "Return list of file in dir START that match `rmail-secondary-file-regexp'."
1390 (if (file-accessible-directory-p start)
1391 ;; Don't sort here.
1392 (let* ((case-fold-search t)
1393 (files (directory-files start t rmail-secondary-file-regexp)))
1394 ;; Sort here instead of in directory-files
1395 ;; because this list is usually much shorter.
1396 (sort files 'string<))))
1397
1398 (defun rmail-list-to-menu (menu-name l action &optional full-name)
1399 (let ((menu (make-sparse-keymap menu-name)))
1400 (mapcar
1401 (function (lambda (item)
1402 (let (command)
1403 (if (consp item)
1404 (progn
1405 (setq command
1406 (rmail-list-to-menu (car item) (cdr item)
1407 action
1408 (if full-name
1409 (concat full-name "/"
1410 (car item))
1411 (car item))))
1412 (setq name (car item)))
1413 (progn
1414 (setq name item)
1415 (setq command
1416 (list 'lambda () '(interactive)
1417 (list action
1418 (expand-file-name
1419 (if full-name
1420 (concat full-name "/" item)
1421 item)
1422 rmail-secondary-file-directory))))))
1423 (define-key menu (vector (intern name))
1424 (cons name command)))))
1425 (reverse l))
1426 menu))
1427
1428 ;; This command is always "disabled" when it appears in a menu.
1429 (put 'rmail-disable-menu 'menu-enable ''nil)
1430
1431 (defun rmail-construct-io-menu ()
1432 (let ((files (rmail-find-all-files rmail-secondary-file-directory)))
1433 (if files
1434 (progn
1435 (define-key rmail-mode-map [menu-bar classify input-menu]
1436 (cons "Input Rmail File"
1437 (rmail-list-to-menu "Input Rmail File"
1438 files
1439 'rmail-input)))
1440 (define-key rmail-mode-map [menu-bar classify output-menu]
1441 (cons "Output Rmail File"
1442 (rmail-list-to-menu "Output Rmail File"
1443 files
1444 'rmail-output-to-rmail-file))))
1445
1446 (define-key rmail-mode-map [menu-bar classify input-menu]
1447 '("Input Rmail File" . rmail-disable-menu))
1448 (define-key rmail-mode-map [menu-bar classify output-menu]
1449 '("Output Rmail File" . rmail-disable-menu)))))
1450
1451 \f
1452 ;;;; *** Rmail input ***
1453
1454 ;; RLK feature not added in this version:
1455 ;; argument specifies inbox file or files in various ways.
1456
1457 (defun rmail-get-new-mail (&optional file-name)
1458 "Move any new mail from this RMAIL file's inbox files.
1459 The inbox files can be specified with the file's Mail: option. The
1460 variable `rmail-primary-inbox-list' specifies the inboxes for your
1461 primary RMAIL file if it has no Mail: option. By default, this is
1462 your /usr/spool/mail/$USER.
1463
1464 You can also specify the file to get new mail from. In this case, the
1465 file of new mail is not changed or deleted. Noninteractively, you can
1466 pass the inbox file name as an argument. Interactively, a prefix
1467 argument causes us to read a file name and use that file as the inbox.
1468
1469 If the variable `rmail-preserve-inbox' is non-nil, new mail will
1470 always be left in inbox files rather than deleted.
1471
1472 This function runs `rmail-get-new-mail-hook' before saving the updated file.
1473 It returns t if it got any new messages."
1474 (interactive
1475 (list (if current-prefix-arg
1476 (read-file-name "Get new mail from file: "))))
1477 (run-hooks 'rmail-before-get-new-mail-hook)
1478 ;; If the disk file has been changed from under us,
1479 ;; revert to it before we get new mail.
1480 (or (verify-visited-file-modtime (current-buffer))
1481 (find-file (buffer-file-name)))
1482 (set-buffer rmail-buffer)
1483 (rmail-maybe-set-message-counters)
1484 (widen)
1485 ;; Get rid of all undo records for this buffer.
1486 (or (eq buffer-undo-list t)
1487 (setq buffer-undo-list nil))
1488 (let ((all-files (if file-name (list file-name)
1489 rmail-inbox-list))
1490 (rmail-enable-multibyte (default-value 'enable-multibyte-characters))
1491 found)
1492 (unwind-protect
1493 (progn
1494 (while all-files
1495 (let ((opoint (point))
1496 (new-messages 0)
1497 (rsf-number-of-spam 0)
1498 (delete-files ())
1499 ;; If buffer has not changed yet, and has not been saved yet,
1500 ;; don't replace the old backup file now.
1501 (make-backup-files (and make-backup-files (buffer-modified-p)))
1502 (buffer-read-only nil)
1503 ;; Don't make undo records for what we do in getting mail.
1504 (buffer-undo-list t)
1505 success
1506 ;; Files to insert this time around.
1507 files
1508 ;; Last names of those files.
1509 file-last-names)
1510 ;; Pull files off all-files onto files
1511 ;; as long as there is no name conflict.
1512 ;; A conflict happens when two inbox file names
1513 ;; have the same last component.
1514 (while (and all-files
1515 (not (member (file-name-nondirectory (car all-files))
1516 file-last-names)))
1517 (setq files (cons (car all-files) files)
1518 file-last-names
1519 (cons (file-name-nondirectory (car all-files)) files))
1520 (setq all-files (cdr all-files)))
1521 ;; Put them back in their original order.
1522 (setq files (nreverse files))
1523
1524 (goto-char (point-max))
1525 (skip-chars-backward " \t\n") ; just in case of brain damage
1526 (delete-region (point) (point-max)) ; caused by require-final-newline
1527 (save-excursion
1528 (save-restriction
1529 (narrow-to-region (point) (point))
1530 ;; Read in the contents of the inbox files,
1531 ;; renaming them as necessary,
1532 ;; and adding to the list of files to delete eventually.
1533 (if file-name
1534 (rmail-insert-inbox-text files nil)
1535 (setq delete-files (rmail-insert-inbox-text files t)))
1536 ;; Scan the new text and convert each message to babyl format.
1537 (goto-char (point-min))
1538 (unwind-protect
1539 (save-excursion
1540 (setq new-messages (rmail-convert-to-babyl-format)
1541 success t))
1542 ;; Try to delete the garbage just inserted.
1543 (or success (delete-region (point-min) (point-max)))
1544 ;; If we could not convert the file's inboxes,
1545 ;; rename the files we tried to read
1546 ;; so we won't over and over again.
1547 (if (and (not file-name) (not success))
1548 (let ((delfiles delete-files)
1549 (count 0))
1550 (while delfiles
1551 (while (file-exists-p (format "RMAILOSE.%d" count))
1552 (setq count (1+ count)))
1553 (rename-file (car delfiles)
1554 (format "RMAILOSE.%d" count))
1555 (setq delfiles (cdr delfiles))))))
1556 (or (zerop new-messages)
1557 (let (success)
1558 (widen)
1559 (search-backward "\n\^_" nil t)
1560 (narrow-to-region (point) (point-max))
1561 (goto-char (1+ (point-min)))
1562 (rmail-count-new-messages)
1563 (run-hooks 'rmail-get-new-mail-hook)
1564 (save-buffer)))
1565 ;; Delete the old files, now that babyl file is saved.
1566 (while delete-files
1567 (condition-case ()
1568 ;; First, try deleting.
1569 (condition-case ()
1570 (delete-file (car delete-files))
1571 (file-error
1572 ;; If we can't delete it, truncate it.
1573 (write-region (point) (point) (car delete-files))))
1574 (file-error nil))
1575 (setq delete-files (cdr delete-files)))))
1576 (if (= new-messages 0)
1577 (progn (goto-char opoint)
1578 (if (or file-name rmail-inbox-list)
1579 (message "(No new mail has arrived)")))
1580 ;; check new messages to see if any of them is spam:
1581 (if (and (featurep 'rmail-spam-filter)
1582 rmail-use-spam-filter)
1583 (let*
1584 ((old-messages (- rmail-total-messages new-messages))
1585 (rsf-scanned-message-number (1+ old-messages))
1586 ;; save deletion flags of old messages: vector starts
1587 ;; at zero (is one longer that no of messages),
1588 ;; therefore take 1+ old-messages
1589 (save-deleted
1590 (substring rmail-deleted-vector 0 (1+
1591 old-messages))))
1592 ;; set all messages to undeleted
1593 (setq rmail-deleted-vector
1594 (make-string (1+ rmail-total-messages) ?\ ))
1595 (while (<= rsf-scanned-message-number
1596 rmail-total-messages)
1597 (progn
1598 (if (not (rmail-spam-filter rsf-scanned-message-number))
1599 (progn (setq rsf-number-of-spam (1+ rsf-number-of-spam)))
1600 )
1601 (setq rsf-scanned-message-number (1+ rsf-scanned-message-number))
1602 ))
1603 (if (> rsf-number-of-spam 0)
1604 (progn
1605 (when (rmail-expunge-confirmed)
1606 (rmail-only-expunge t))
1607 ))
1608 (setq rmail-deleted-vector
1609 (concat
1610 save-deleted
1611 (make-string (- rmail-total-messages old-messages)
1612 ?\ )))
1613 ))
1614 (if (rmail-summary-exists)
1615 (rmail-select-summary
1616 (rmail-update-summary)))
1617 (message "%d new message%s read%s"
1618 new-messages (if (= 1 new-messages) "" "s")
1619 ;; print out a message on number of spam messages found:
1620 (if (and (featurep 'rmail-spam-filter)
1621 rmail-use-spam-filter
1622 (> rsf-number-of-spam 0))
1623 (if (= 1 new-messages)
1624 ", and found to be a spam message"
1625 (if (> rsf-number-of-spam 1)
1626 (format ", %d of which found to be spam messages"
1627 rsf-number-of-spam)
1628 ", one of which found to be a spam message"))
1629 ""))
1630 (if (and (featurep 'rmail-spam-filter)
1631 rmail-use-spam-filter
1632 (> rsf-number-of-spam 0))
1633 (progn (if rsf-beep (beep t))
1634 (sleep-for rsf-sleep-after-message)))
1635
1636 ;; Move to the first new message
1637 ;; unless we have other unseen messages before it.
1638 (rmail-show-message (rmail-first-unseen-message))
1639 (run-hooks 'rmail-after-get-new-mail-hook)
1640 (setq found t))))
1641 found)
1642 ;; Don't leave the buffer screwed up if we get a disk-full error.
1643 (or found (rmail-show-message)))))
1644
1645 (defun rmail-parse-url (file)
1646 "Parse the supplied URL. Return (list MAILBOX-NAME REMOTE PASSWORD GOT-PASSWORD)
1647 WHERE MAILBOX-NAME is the name of the mailbox suitable as argument to the
1648 actual version of `movemail', REMOTE is non-nil if MAILBOX-NAME refers to
1649 a remote mailbox, PASSWORD is the password if it should be
1650 supplied as a separate argument to `movemail' or nil otherwise, GOT-PASSWORD
1651 is non-nil if the user has supplied the password interactively.
1652 "
1653 (cond
1654 ((string-match "^\\([^:]+\\)://\\(\\([^:@]+\\)\\(:\\([^@]+\\)\\)?@\\)?.*" file)
1655 (let (got-password supplied-password
1656 (proto (match-string 1 file))
1657 (user (match-string 3 file))
1658 (pass (match-string 5 file))
1659 (host (substring file (or (match-end 2)
1660 (+ 3 (match-end 1))))))
1661
1662 (if (not pass)
1663 (when rmail-remote-password-required
1664 (setq got-password (not (rmail-have-password)))
1665 (setq supplied-password (rmail-get-remote-password
1666 (string-equal proto "imap")))))
1667
1668 (if (rmail-movemail-variant-p 'emacs)
1669 (if (string-equal proto "pop")
1670 (list (concat "po:" user ":" host)
1671 t
1672 (or pass supplied-password)
1673 got-password)
1674 (error "Emacs movemail does not support %s protocol" proto))
1675 (list file
1676 (or (string-equal proto "pop") (string-equal proto "imap"))
1677 supplied-password
1678 got-password))))
1679
1680 ((string-match "^po:\\([^:]+\\)\\(:\\(.*\\)\\)?" file)
1681 (let (got-password supplied-password
1682 (proto "pop")
1683 (user (match-string 1 file))
1684 (host (match-string 3 file)))
1685
1686 (when rmail-remote-password-required
1687 (setq got-password (not (rmail-have-password)))
1688 (setq supplied-password (rmail-get-remote-password nil)))
1689
1690 (list file "pop" supplied-password got-password)))
1691
1692 (t
1693 (list file nil nil nil))))
1694
1695 (defun rmail-insert-inbox-text (files renamep)
1696 ;; Detect a locked file now, so that we avoid moving mail
1697 ;; out of the real inbox file. (That could scare people.)
1698 (or (memq (file-locked-p buffer-file-name) '(nil t))
1699 (error "RMAIL file %s is locked"
1700 (file-name-nondirectory buffer-file-name)))
1701 (let (file tofile delete-files movemail popmail got-password password)
1702 (while files
1703 ;; Handle remote mailbox names specially; don't expand as filenames
1704 ;; in case the userid contains a directory separator.
1705 (setq file (car files))
1706 (let ((url-data (rmail-parse-url file)))
1707 (setq file (nth 0 url-data))
1708 (setq popmail (nth 1 url-data))
1709 (setq password (nth 2 url-data))
1710 (setq got-password (nth 3 url-data)))
1711
1712 (if popmail
1713 (setq renamep t)
1714 (setq file (file-truename
1715 (substitute-in-file-name (expand-file-name file)))))
1716 (setq tofile (expand-file-name
1717 ;; Generate name to move to from inbox name,
1718 ;; in case of multiple inboxes that need moving.
1719 (concat ".newmail-"
1720 (file-name-nondirectory
1721 (if (memq system-type '(windows-nt cygwin))
1722 ;; cannot have "po:" in file name
1723 (substring file 3)
1724 file)))
1725 ;; Use the directory of this rmail file
1726 ;; because it's a nuisance to use the homedir
1727 ;; if that is on a full disk and this rmail
1728 ;; file isn't.
1729 (file-name-directory
1730 (expand-file-name buffer-file-name))))
1731 ;; Always use movemail to rename the file,
1732 ;; since there can be mailboxes in various directories.
1733 (if (not popmail)
1734 (progn
1735 ;; On some systems, /usr/spool/mail/foo is a directory
1736 ;; and the actual inbox is /usr/spool/mail/foo/foo.
1737 (if (file-directory-p file)
1738 (setq file (expand-file-name (user-login-name)
1739 file)))))
1740 (cond (popmail
1741 (message "Getting mail from the remote server ..."))
1742 ((and (file-exists-p tofile)
1743 (/= 0 (nth 7 (file-attributes tofile))))
1744 (message "Getting mail from %s..." tofile))
1745 ((and (file-exists-p file)
1746 (/= 0 (nth 7 (file-attributes file))))
1747 (message "Getting mail from %s..." file)))
1748 ;; Set TOFILE if have not already done so, and
1749 ;; rename or copy the file FILE to TOFILE if and as appropriate.
1750 (cond ((not renamep)
1751 (setq tofile file))
1752 ((or (file-exists-p tofile) (and (not popmail)
1753 (not (file-exists-p file))))
1754 nil)
1755 (t
1756 (with-temp-buffer
1757 (let ((errors (current-buffer)))
1758 (buffer-disable-undo errors)
1759 (let ((args
1760 (append
1761 (list (or rmail-movemail-program
1762 (expand-file-name "movemail"
1763 exec-directory))
1764 nil errors nil)
1765 (if rmail-preserve-inbox
1766 (list "-p")
1767 nil)
1768 (if (rmail-movemail-variant-p 'mailutils)
1769 (append (list "--emacs") rmail-movemail-flags)
1770 rmail-movemail-flags)
1771 (list file tofile)
1772 (if password (list password) nil))))
1773 (apply 'call-process args))
1774 (if (not (buffer-modified-p errors))
1775 ;; No output => movemail won
1776 nil
1777 (set-buffer errors)
1778 (subst-char-in-region (point-min) (point-max)
1779 ?\n ?\ )
1780 (goto-char (point-max))
1781 (skip-chars-backward " \t")
1782 (delete-region (point) (point-max))
1783 (goto-char (point-min))
1784 (if (looking-at "movemail: ")
1785 (delete-region (point-min) (match-end 0)))
1786 (beep t)
1787 ;; If we just read the password, most likely it is
1788 ;; wrong. Otherwise, see if there is a specific
1789 ;; reason to think that the problem is a wrong passwd.
1790 (if (or got-password
1791 (re-search-forward rmail-remote-password-error
1792 nil t))
1793 (rmail-set-remote-password nil))
1794
1795 ;; If using Mailutils, remove initial error code
1796 ;; abbreviation
1797 (when (rmail-movemail-variant-p 'mailutils)
1798 (goto-char (point-min))
1799 (when (looking-at "[A-Z][A-Z0-9_]*:")
1800 (delete-region (point-min) (match-end 0))))
1801
1802 (message "movemail: %s"
1803 (buffer-substring (point-min)
1804 (point-max)))
1805
1806 (sit-for 3)
1807 nil)))))
1808
1809 ;; At this point, TOFILE contains the name to read:
1810 ;; Either the alternate name (if we renamed)
1811 ;; or the actual inbox (if not renaming).
1812 (if (file-exists-p tofile)
1813 (let ((coding-system-for-read 'no-conversion)
1814 size)
1815 (goto-char (point-max))
1816 (setq size (nth 1 (insert-file-contents tofile)))
1817 (goto-char (point-max))
1818 (or (= (preceding-char) ?\n)
1819 (zerop size)
1820 (insert ?\n))
1821 (if (not (and rmail-preserve-inbox (string= file tofile)))
1822 (setq delete-files (cons tofile delete-files)))))
1823 (message "")
1824 (setq files (cdr files)))
1825 delete-files))
1826
1827 ;; Decode the region specified by FROM and TO by CODING.
1828 ;; If CODING is nil or an invalid coding system, decode by `undecided'.
1829 (defun rmail-decode-region (from to coding)
1830 (if (or (not coding) (not (coding-system-p coding)))
1831 (setq coding 'undecided))
1832 ;; Use -dos decoding, to remove ^M characters left from base64 or
1833 ;; rogue qp-encoded text.
1834 (decode-coding-region from to
1835 (coding-system-change-eol-conversion coding 1))
1836 ;; Don't reveal the fact we used -dos decoding, as users generally
1837 ;; will not expect the RMAIL buffer to use DOS EOL format.
1838 (setq buffer-file-coding-system
1839 (setq last-coding-system-used
1840 (coding-system-change-eol-conversion coding 0))))
1841
1842 ;; the rmail-break-forwarded-messages feature is not implemented
1843 (defun rmail-convert-to-babyl-format ()
1844 (let ((count 0) start
1845 (case-fold-search nil)
1846 (invalid-input-resync
1847 (function (lambda ()
1848 (message "Invalid Babyl format in inbox!")
1849 (sit-for 3)
1850 ;; Try to get back in sync with a real message.
1851 (if (re-search-forward
1852 (concat rmail-mmdf-delim1 "\\|^From") nil t)
1853 (beginning-of-line)
1854 (goto-char (point-max)))))))
1855 (goto-char (point-min))
1856 (save-restriction
1857 (while (not (eobp))
1858 (setq start (point))
1859 (cond ((looking-at "BABYL OPTIONS:");Babyl header
1860 (if (search-forward "\n\^_" nil t)
1861 ;; If we find the proper terminator, delete through there.
1862 (delete-region (point-min) (point))
1863 (funcall invalid-input-resync)
1864 (delete-region (point-min) (point))))
1865 ;; Babyl format message
1866 ((looking-at "\^L")
1867 (or (search-forward "\n\^_" nil t)
1868 (funcall invalid-input-resync))
1869 (setq count (1+ count))
1870 ;; Make sure there is no extra white space after the ^_
1871 ;; at the end of the message.
1872 ;; Narrowing will make sure that whatever follows the junk
1873 ;; will be treated properly.
1874 (delete-region (point)
1875 (save-excursion
1876 (skip-chars-forward " \t\n")
1877 (point)))
1878 (save-excursion
1879 (let* ((header-end
1880 (progn
1881 (save-excursion
1882 (goto-char start)
1883 (forward-line 1)
1884 (if (looking-at "0")
1885 (forward-line 1)
1886 (forward-line 2))
1887 (save-restriction
1888 (narrow-to-region (point) (point-max))
1889 (rfc822-goto-eoh)
1890 (point)))))
1891 (case-fold-search t)
1892 (quoted-printable-header-field-end
1893 (save-excursion
1894 (goto-char start)
1895 (re-search-forward
1896 "^content-transfer-encoding:\\(\n?[\t ]\\)*quoted-printable\\(\n?[\t ]\\)*"
1897 header-end t)))
1898 (base64-header-field-end
1899 (save-excursion
1900 (goto-char start)
1901 (re-search-forward
1902 "^content-transfer-encoding:\\(\n?[\t ]\\)*base64\\(\n?[\t ]\\)*"
1903 header-end t))))
1904 (if quoted-printable-header-field-end
1905 (save-excursion
1906 (unless
1907 (mail-unquote-printable-region header-end (point) nil t t)
1908 (message "Malformed MIME quoted-printable message"))
1909 ;; Change "quoted-printable" to "8bit",
1910 ;; to reflect the decoding we just did.
1911 (goto-char quoted-printable-header-field-end)
1912 (delete-region (point) (search-backward ":"))
1913 (insert ": 8bit")))
1914 (if base64-header-field-end
1915 (save-excursion
1916 (when
1917 (condition-case nil
1918 (progn
1919 (base64-decode-region (1+ header-end)
1920 (- (point) 2))
1921 t)
1922 (error nil))
1923 ;; Change "base64" to "8bit", to reflect the
1924 ;; decoding we just did.
1925 (goto-char base64-header-field-end)
1926 (delete-region (point) (search-backward ":"))
1927 (insert ": 8bit"))))
1928 (setq last-coding-system-used nil)
1929 (or rmail-enable-mime
1930 (not rmail-enable-multibyte)
1931 (let ((mime-charset
1932 (if (and rmail-decode-mime-charset
1933 (save-excursion
1934 (goto-char start)
1935 (search-forward "\n\n" nil t)
1936 (let ((case-fold-search t))
1937 (re-search-backward
1938 rmail-mime-charset-pattern
1939 start t))))
1940 (intern (downcase (match-string 1))))))
1941 (rmail-decode-region start (point) mime-charset)))))
1942 ;; Add an X-Coding-System: header if we don't have one.
1943 (save-excursion
1944 (goto-char start)
1945 (forward-line 1)
1946 (if (looking-at "0")
1947 (forward-line 1)
1948 (forward-line 2))
1949 (or (save-restriction
1950 (narrow-to-region (point) (point-max))
1951 (rfc822-goto-eoh)
1952 (goto-char (point-min))
1953 (re-search-forward "^X-Coding-System:" nil t))
1954 (insert "X-Coding-System: "
1955 (symbol-name last-coding-system-used)
1956 "\n")))
1957 (narrow-to-region (point) (point-max))
1958 (and (= 0 (% count 10))
1959 (message "Converting to Babyl format...%d" count)))
1960 ;;*** MMDF format
1961 ((let ((case-fold-search t))
1962 (looking-at rmail-mmdf-delim1))
1963 (let ((case-fold-search t))
1964 (replace-match "\^L\n0, unseen,,\n*** EOOH ***\n")
1965 (re-search-forward rmail-mmdf-delim2 nil t)
1966 (replace-match "\^_"))
1967 (save-excursion
1968 (save-restriction
1969 (narrow-to-region start (1- (point)))
1970 (goto-char (point-min))
1971 (while (search-forward "\n\^_" nil t); single char "\^_"
1972 (replace-match "\n^_")))); 2 chars: "^" and "_"
1973 (setq last-coding-system-used nil)
1974 (or rmail-enable-mime
1975 (not rmail-enable-multibyte)
1976 (decode-coding-region start (point) 'undecided))
1977 (save-excursion
1978 (goto-char start)
1979 (forward-line 3)
1980 (insert "X-Coding-System: "
1981 (symbol-name last-coding-system-used)
1982 "\n"))
1983 (narrow-to-region (point) (point-max))
1984 (setq count (1+ count))
1985 (and (= 0 (% count 10))
1986 (message "Converting to Babyl format...%d" count)))
1987 ;;*** Mail format
1988 ((looking-at "^From ")
1989 (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
1990 (rmail-nuke-pinhead-header)
1991 ;; If this message has a Content-Length field,
1992 ;; skip to the end of the contents.
1993 (let* ((header-end (save-excursion
1994 (and (re-search-forward "\n\n" nil t)
1995 (1- (point)))))
1996 (case-fold-search t)
1997 (quoted-printable-header-field-end
1998 (save-excursion
1999 (re-search-forward
2000 "^content-transfer-encoding:\\(\n?[\t ]\\)*quoted-printable\\(\n?[\t ]\\)*"
2001 header-end t)))
2002 (base64-header-field-end
2003 (save-excursion
2004 (re-search-forward
2005 "^content-transfer-encoding:\\(\n?[\t ]\\)*base64\\(\n?[\t ]\\)*"
2006 header-end t)))
2007 (size
2008 ;; Get the numeric value from the Content-Length field.
2009 (save-excursion
2010 ;; Back up to end of prev line,
2011 ;; in case the Content-Length field comes first.
2012 (forward-char -1)
2013 (and (search-forward "\ncontent-length: "
2014 header-end t)
2015 (let ((beg (point))
2016 (eol (progn (end-of-line) (point))))
2017 (string-to-number (buffer-substring beg eol)))))))
2018 (and size
2019 (if (and (natnump size)
2020 (<= (+ header-end size) (point-max))
2021 ;; Make sure this would put us at a position
2022 ;; that we could continue from.
2023 (save-excursion
2024 (goto-char (+ header-end size))
2025 (skip-chars-forward "\n")
2026 (or (eobp)
2027 (and (looking-at "BABYL OPTIONS:")
2028 (search-forward "\n\^_" nil t))
2029 (and (looking-at "\^L")
2030 (search-forward "\n\^_" nil t))
2031 (let ((case-fold-search t))
2032 (looking-at rmail-mmdf-delim1))
2033 (looking-at "From "))))
2034 (goto-char (+ header-end size))
2035 (message "Ignoring invalid Content-Length field")
2036 (sit-for 1 0 t)))
2037 (if (let ((case-fold-search nil))
2038 (re-search-forward
2039 (concat "^[\^_]?\\("
2040 rmail-unix-mail-delimiter
2041 "\\|"
2042 rmail-mmdf-delim1 "\\|"
2043 "^BABYL OPTIONS:\\|"
2044 "\^L\n[01],\\)") nil t))
2045 (goto-char (match-beginning 1))
2046 (goto-char (point-max)))
2047 (setq count (1+ count))
2048 (if quoted-printable-header-field-end
2049 (save-excursion
2050 (unless
2051 (mail-unquote-printable-region header-end (point) nil t t)
2052 (message "Malformed MIME quoted-printable message"))
2053 ;; Change "quoted-printable" to "8bit",
2054 ;; to reflect the decoding we just did.
2055 (goto-char quoted-printable-header-field-end)
2056 (delete-region (point) (search-backward ":"))
2057 (insert ": 8bit")))
2058 (if base64-header-field-end
2059 (save-excursion
2060 (when
2061 (condition-case nil
2062 (progn
2063 (base64-decode-region
2064 (1+ header-end)
2065 (save-excursion
2066 ;; Prevent base64-decode-region
2067 ;; from removing newline characters.
2068 (skip-chars-backward "\n\t ")
2069 (point)))
2070 t)
2071 (error nil))
2072 ;; Change "base64" to "8bit", to reflect the
2073 ;; decoding we just did.
2074 (goto-char base64-header-field-end)
2075 (delete-region (point) (search-backward ":"))
2076 (insert ": 8bit")))))
2077
2078 (save-excursion
2079 (save-restriction
2080 (narrow-to-region start (point))
2081 (goto-char (point-min))
2082 (while (search-forward "\n\^_" nil t); single char
2083 (replace-match "\n^_")))); 2 chars: "^" and "_"
2084 ;; This is for malformed messages that don't end in newline.
2085 ;; There shouldn't be any, but some users say occasionally
2086 ;; there are some.
2087 (or (bolp) (newline))
2088 (insert ?\^_)
2089 (setq last-coding-system-used nil)
2090 (or rmail-enable-mime
2091 (not rmail-enable-multibyte)
2092 (let ((mime-charset
2093 (if (and rmail-decode-mime-charset
2094 (save-excursion
2095 (goto-char start)
2096 (search-forward "\n\n" nil t)
2097 (let ((case-fold-search t))
2098 (re-search-backward
2099 rmail-mime-charset-pattern
2100 start t))))
2101 (intern (downcase (match-string 1))))))
2102 (rmail-decode-region start (point) mime-charset)))
2103 (save-excursion
2104 (goto-char start)
2105 (forward-line 3)
2106 (insert "X-Coding-System: "
2107 (symbol-name last-coding-system-used)
2108 "\n"))
2109 (narrow-to-region (point) (point-max))
2110 (and (= 0 (% count 10))
2111 (message "Converting to Babyl format...%d" count)))
2112 ;;
2113 ;; This kludge is because some versions of sendmail.el
2114 ;; insert an extra newline at the beginning that shouldn't
2115 ;; be there. sendmail.el has been fixed, but old versions
2116 ;; may still be in use. -- rms, 7 May 1993.
2117 ((eolp) (delete-char 1))
2118 (t (error "Cannot convert to babyl format")))))
2119 count))
2120
2121 ;; Delete the "From ..." line, creating various other headers with
2122 ;; information from it if they don't already exist. Now puts the
2123 ;; original line into a mail-from: header line for debugging and for
2124 ;; use by the rmail-output function.
2125 (defun rmail-nuke-pinhead-header ()
2126 (save-excursion
2127 (save-restriction
2128 (let ((start (point))
2129 (end (progn
2130 (condition-case ()
2131 (search-forward "\n\n")
2132 (error
2133 (goto-char (point-max))
2134 (insert "\n\n")))
2135 (point)))
2136 has-from has-date)
2137 (narrow-to-region start end)
2138 (let ((case-fold-search t))
2139 (goto-char start)
2140 (setq has-from (search-forward "\nFrom:" nil t))
2141 (goto-char start)
2142 (setq has-date (and (search-forward "\nDate:" nil t) (point)))
2143 (goto-char start))
2144 (let ((case-fold-search nil))
2145 (if (re-search-forward (concat "^" rmail-unix-mail-delimiter) nil t)
2146 (replace-match
2147 (concat
2148 "Mail-from: \\&"
2149 ;; Keep and reformat the date if we don't
2150 ;; have a Date: field.
2151 (if has-date
2152 ""
2153 (concat
2154 "Date: \\2, \\4 \\3 \\9 \\5 "
2155
2156 ;; The timezone could be matched by group 7 or group 10.
2157 ;; If neither of them matched, assume EST, since only
2158 ;; Easterners would be so sloppy.
2159 ;; It's a shame the substitution can't use "\\10".
2160 (cond
2161 ((/= (match-beginning 7) (match-end 7)) "\\7")
2162 ((/= (match-beginning 10) (match-end 10))
2163 (buffer-substring (match-beginning 10)
2164 (match-end 10)))
2165 (t "EST"))
2166 "\n"))
2167 ;; Keep and reformat the sender if we don't
2168 ;; have a From: field.
2169 (if has-from
2170 ""
2171 "From: \\1\n"))
2172 t)))))))
2173 \f
2174 ;;;; *** Rmail Message Formatting and Header Manipulation ***
2175
2176 (defun rmail-reformat-message (beg end)
2177 (goto-char beg)
2178 (forward-line 1)
2179 (if (/= (following-char) ?0)
2180 (error "Bad format in RMAIL file"))
2181 (let ((inhibit-read-only t)
2182 (delta (- (buffer-size) end)))
2183 (delete-char 1)
2184 (insert ?1)
2185 (forward-line 1)
2186 (let ((case-fold-search t))
2187 (while (looking-at "Summary-line:\\|Mail-From:")
2188 (forward-line 1)))
2189 (if (looking-at "\\*\\*\\* EOOH \\*\\*\\*\n")
2190 (delete-region (point)
2191 (progn (forward-line 1) (point))))
2192 (let ((str (buffer-substring (point)
2193 (save-excursion (search-forward "\n\n" end 'move)
2194 (point)))))
2195 (insert str "*** EOOH ***\n")
2196 (narrow-to-region (point) (- (buffer-size) delta)))
2197 (goto-char (point-min))
2198 (if rmail-message-filter (funcall rmail-message-filter))
2199 (if (or rmail-displayed-headers rmail-ignored-headers)
2200 (rmail-clear-headers))))
2201
2202 (defun rmail-clear-headers (&optional ignored-headers)
2203 "Delete all header fields that Rmail should not show.
2204 If the optional argument IGNORED-HEADERS is non-nil,
2205 delete all header fields whose names match that regexp.
2206 Otherwise, if `rmail-displayed-headers' is non-nil,
2207 delete all header fields *except* those whose names match that regexp.
2208 Otherwise, delete all header fields whose names match `rmail-ignored-headers'
2209 unless they also match `rmail-nonignored-headers'."
2210 (when (search-forward "\n\n" nil t)
2211 (forward-char -1)
2212 (let ((case-fold-search t)
2213 (buffer-read-only nil))
2214 (if (and rmail-displayed-headers (null ignored-headers))
2215 (save-restriction
2216 (narrow-to-region (point-min) (point))
2217 (let (lim next)
2218 (goto-char (point-min))
2219 (while (and (not (eobp))
2220 (save-excursion
2221 (if (re-search-forward "\n[^ \t]" nil t)
2222 (setq lim (match-beginning 0)
2223 next (1+ lim))
2224 (setq lim nil next (point-max)))))
2225 (if (save-excursion
2226 (re-search-forward rmail-displayed-headers lim t))
2227 (goto-char next)
2228 (delete-region (point) next))))
2229 (goto-char (point-min)))
2230 (or ignored-headers (setq ignored-headers rmail-ignored-headers))
2231 (save-restriction
2232 (narrow-to-region (point-min) (point))
2233 (goto-char (point-min))
2234 (while (and ignored-headers
2235 (re-search-forward ignored-headers nil t))
2236 (beginning-of-line)
2237 (if (looking-at rmail-nonignored-headers)
2238 (forward-line 1)
2239 (delete-region (point)
2240 (save-excursion
2241 (if (re-search-forward "\n[^ \t]" nil t)
2242 (1- (point))
2243 (point-max)))))))))))
2244
2245 (defun rmail-msg-is-pruned ()
2246 (rmail-maybe-set-message-counters)
2247 (save-restriction
2248 (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
2249 (save-excursion
2250 (goto-char (point-min))
2251 (forward-line 1)
2252 (= (following-char) ?1))))
2253
2254 (defun rmail-msg-restore-non-pruned-header ()
2255 (let ((old-point (point))
2256 new-point
2257 new-start
2258 (inhibit-read-only t))
2259 (save-excursion
2260 (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
2261 (goto-char (point-min))
2262 (forward-line 1)
2263 ;; Change 1 to 0.
2264 (delete-char 1)
2265 (insert ?0)
2266 ;; Insert new EOOH line at the proper place.
2267 (forward-line 1)
2268 (let ((case-fold-search t))
2269 (while (looking-at "Summary-Line:\\|Mail-From:")
2270 (forward-line 1)))
2271 (insert "*** EOOH ***\n")
2272 (setq new-start (point))
2273 ;; Delete the old reformatted header.
2274 (forward-char -1)
2275 (search-forward "\n*** EOOH ***\n")
2276 (forward-line -1)
2277 (let ((start (point)))
2278 (search-forward "\n\n")
2279 (if (and (<= start old-point)
2280 (<= old-point (point)))
2281 (setq new-point new-start))
2282 (delete-region start (point)))
2283 ;; Narrow to after the new EOOH line.
2284 (narrow-to-region new-start (point-max)))
2285 (if new-point
2286 (goto-char new-point))))
2287
2288 (defun rmail-msg-prune-header ()
2289 (let ((new-point
2290 (= (point) (point-min))))
2291 (save-excursion
2292 (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
2293 (rmail-reformat-message (point-min) (point-max)))
2294 (if new-point
2295 (goto-char (point-min)))))
2296
2297 (defun rmail-toggle-header (&optional arg)
2298 "Show original message header if pruned header currently shown, or vice versa.
2299 With argument ARG, show the message header pruned if ARG is greater than zero;
2300 otherwise, show it in full."
2301 (interactive "P")
2302 (let* ((pruned (with-current-buffer rmail-buffer
2303 (rmail-msg-is-pruned)))
2304 (prune (if arg
2305 (> (prefix-numeric-value arg) 0)
2306 (not pruned))))
2307 (if (eq pruned prune)
2308 t
2309 (set-buffer rmail-buffer)
2310 (rmail-maybe-set-message-counters)
2311 (if rmail-enable-mime
2312 (let ((buffer-read-only nil))
2313 (if pruned
2314 (rmail-msg-restore-non-pruned-header)
2315 (rmail-msg-prune-header))
2316 (funcall rmail-show-mime-function))
2317 (let* ((buffer-read-only nil)
2318 (window (get-buffer-window (current-buffer)))
2319 (at-point-min (= (point) (point-min)))
2320 (all-headers-visible (= (window-start window) (point-min)))
2321 (on-header
2322 (save-excursion
2323 (and (not (search-backward "\n\n" nil t))
2324 (progn
2325 (end-of-line)
2326 (re-search-backward "^[-A-Za-z0-9]+:" nil t))
2327 (match-string 0))))
2328 (old-screen-line
2329 (rmail-count-screen-lines (window-start window) (point))))
2330 (if pruned
2331 (rmail-msg-restore-non-pruned-header)
2332 (rmail-msg-prune-header))
2333 (cond (at-point-min
2334 (goto-char (point-min)))
2335 (on-header
2336 (goto-char (point-min))
2337 (search-forward "\n\n")
2338 (or (re-search-backward
2339 (concat "^" (regexp-quote on-header)) nil t)
2340 (goto-char (point-min))))
2341 (t
2342 (save-selected-window
2343 (select-window window)
2344 (recenter old-screen-line)
2345 (if (and all-headers-visible
2346 (not (= (window-start) (point-min))))
2347 (recenter (- (window-height) 2))))))))
2348 (rmail-highlight-headers))))
2349
2350 (defun rmail-narrow-to-non-pruned-header ()
2351 "Narrow to the whole (original) header of the current message."
2352 (let (start end)
2353 (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
2354 (goto-char (point-min))
2355 (forward-line 1)
2356 (if (= (following-char) ?1)
2357 (progn
2358 (forward-line 1)
2359 (setq start (point))
2360 (search-forward "*** EOOH ***\n")
2361 (setq end (match-beginning 0)))
2362 (forward-line 2)
2363 (setq start (point))
2364 (search-forward "\n\n")
2365 (setq end (1- (point))))
2366 (narrow-to-region start end)
2367 (goto-char start)))
2368
2369 ;; Lifted from repos-count-screen-lines.
2370 ;; Return number of screen lines between START and END.
2371 (defun rmail-count-screen-lines (start end)
2372 (save-excursion
2373 (save-restriction
2374 (narrow-to-region start end)
2375 (goto-char (point-min))
2376 (vertical-motion (- (point-max) (point-min))))))
2377 \f
2378 ;;;; *** Rmail Attributes and Keywords ***
2379
2380 ;; Make a string describing current message's attributes and keywords
2381 ;; and set it up as the name of a minor mode
2382 ;; so it will appear in the mode line.
2383 (defun rmail-display-labels ()
2384 (let ((blurb "") (beg (point-min-marker)) (end (point-max-marker)))
2385 (save-excursion
2386 (unwind-protect
2387 (progn
2388 (widen)
2389 (goto-char (rmail-msgbeg rmail-current-message))
2390 (forward-line 1)
2391 (if (looking-at "[01],")
2392 (progn
2393 (narrow-to-region (point) (progn (end-of-line) (point)))
2394 ;; Truly valid BABYL format requires a space before each
2395 ;; attribute or keyword name. Put them in if missing.
2396 (let (buffer-read-only)
2397 (goto-char (point-min))
2398 (while (search-forward "," nil t)
2399 (or (looking-at "[ ,]") (eobp)
2400 (insert " "))))
2401 (goto-char (point-max))
2402 (if (search-backward ",," nil 'move)
2403 (progn
2404 (if (> (point) (1+ (point-min)))
2405 (setq blurb (buffer-substring (+ 1 (point-min)) (point))))
2406 (if (> (- (point-max) (point)) 2)
2407 (setq blurb
2408 (concat blurb
2409 ";"
2410 (buffer-substring (+ (point) 3)
2411 (1- (point-max)))))))))))
2412 ;; Note: we don't use save-restriction because that does not work right
2413 ;; if changes are made outside the saved restriction
2414 ;; before that restriction is restored.
2415 (narrow-to-region beg end)
2416 (set-marker beg nil)
2417 (set-marker end nil)))
2418 (while (string-match " +," blurb)
2419 (setq blurb (concat (substring blurb 0 (match-beginning 0)) ","
2420 (substring blurb (match-end 0)))))
2421 (while (string-match ", +" blurb)
2422 (setq blurb (concat (substring blurb 0 (match-beginning 0)) ","
2423 (substring blurb (match-end 0)))))
2424 (setq mode-line-process
2425 (format " %d/%d%s"
2426 rmail-current-message rmail-total-messages blurb))
2427 ;; If rmail-enable-mime is non-nil, we may have to update
2428 ;; `mode-line-process' of rmail-view-buffer too.
2429 (if (and rmail-enable-mime
2430 (not (eq (current-buffer) rmail-view-buffer))
2431 (buffer-live-p rmail-view-buffer))
2432 (let ((mlp mode-line-process))
2433 (with-current-buffer rmail-view-buffer
2434 (setq mode-line-process mlp))))))
2435
2436 ;; Turn an attribute of a message on or off according to STATE.
2437 ;; ATTR is the name of the attribute, as a string.
2438 ;; MSGNUM is message number to change; nil means current message.
2439 (defun rmail-set-attribute (attr state &optional msgnum)
2440 (set-buffer rmail-buffer)
2441 (let ((omax (point-max-marker))
2442 (omin (point-min-marker))
2443 (buffer-read-only nil))
2444 (or msgnum (setq msgnum rmail-current-message))
2445 (if (> msgnum 0)
2446 (unwind-protect
2447 (save-excursion
2448 (widen)
2449 (goto-char (+ 3 (rmail-msgbeg msgnum)))
2450 (let ((curstate
2451 (not
2452 (null (search-backward (concat ", " attr ",")
2453 (prog1 (point) (end-of-line)) t)))))
2454 (or (eq curstate (not (not state)))
2455 (if curstate
2456 (delete-region (point) (1- (match-end 0)))
2457 (beginning-of-line)
2458 (forward-char 2)
2459 (insert " " attr ","))))
2460 (if (string= attr "deleted")
2461 (rmail-set-message-deleted-p msgnum state)))
2462 ;; Note: we don't use save-restriction because that does not work right
2463 ;; if changes are made outside the saved restriction
2464 ;; before that restriction is restored.
2465 (narrow-to-region omin omax)
2466 (set-marker omin nil)
2467 (set-marker omax nil)
2468 (if (= msgnum rmail-current-message)
2469 (rmail-display-labels))))))
2470
2471 ;; Return t if the attributes/keywords line of msg number MSG
2472 ;; contains a match for the regexp LABELS.
2473 (defun rmail-message-labels-p (msg labels)
2474 (save-excursion
2475 (save-restriction
2476 (widen)
2477 (goto-char (rmail-msgbeg msg))
2478 (forward-char 3)
2479 (re-search-backward labels (prog1 (point) (end-of-line)) t))))
2480 \f
2481 ;;;; *** Rmail Message Selection And Support ***
2482
2483 (defun rmail-msgend (n)
2484 (marker-position (aref rmail-message-vector (1+ n))))
2485
2486 (defun rmail-msgbeg (n)
2487 (marker-position (aref rmail-message-vector n)))
2488
2489 (defun rmail-widen-to-current-msgbeg (function)
2490 "Call FUNCTION with point at start of internal data of current message.
2491 Assumes that bounds were previously narrowed to display the message in Rmail.
2492 The bounds are widened enough to move point where desired, then narrowed
2493 again afterward.
2494
2495 FUNCTION may not change the visible text of the message, but it may
2496 change the invisible header text."
2497 (save-excursion
2498 (unwind-protect
2499 (progn
2500 (narrow-to-region (rmail-msgbeg rmail-current-message)
2501 (point-max))
2502 (goto-char (point-min))
2503 (funcall function))
2504 ;; Note: we don't use save-restriction because that does not work right
2505 ;; if changes are made outside the saved restriction
2506 ;; before that restriction is restored.
2507 (narrow-to-region (rmail-msgbeg rmail-current-message)
2508 (rmail-msgend rmail-current-message)))))
2509
2510 (defun rmail-forget-messages ()
2511 (unwind-protect
2512 (if (vectorp rmail-message-vector)
2513 (let* ((i 0)
2514 (v rmail-message-vector)
2515 (n (length v)))
2516 (while (< i n)
2517 (move-marker (aref v i) nil)
2518 (setq i (1+ i)))))
2519 (setq rmail-message-vector nil)
2520 (setq rmail-msgref-vector nil)
2521 (setq rmail-deleted-vector nil)))
2522
2523 (defun rmail-maybe-set-message-counters ()
2524 (if (not (and rmail-deleted-vector
2525 rmail-message-vector
2526 rmail-current-message
2527 rmail-total-messages))
2528 (rmail-set-message-counters)))
2529
2530 (defun rmail-count-new-messages (&optional nomsg)
2531 (let* ((case-fold-search nil)
2532 (total-messages 0)
2533 (messages-head nil)
2534 (deleted-head nil))
2535 (or nomsg (message "Counting new messages..."))
2536 (goto-char (point-max))
2537 ;; Put at the end of messages-head
2538 ;; the entry for message N+1, which marks
2539 ;; the end of message N. (N = number of messages).
2540 (search-backward "\n\^_")
2541 (forward-char 1)
2542 (setq messages-head (list (point-marker)))
2543 (rmail-set-message-counters-counter (point-min))
2544 (setq rmail-current-message (1+ rmail-total-messages))
2545 (setq rmail-total-messages
2546 (+ rmail-total-messages total-messages))
2547 (setq rmail-message-vector
2548 (vconcat rmail-message-vector (cdr messages-head)))
2549 (aset rmail-message-vector
2550 rmail-current-message (car messages-head))
2551 (setq rmail-deleted-vector
2552 (concat rmail-deleted-vector deleted-head))
2553 (setq rmail-summary-vector
2554 (vconcat rmail-summary-vector (make-vector total-messages nil)))
2555 (setq rmail-msgref-vector
2556 (vconcat rmail-msgref-vector (make-vector total-messages nil)))
2557 ;; Fill in the new elements of rmail-msgref-vector.
2558 (let ((i (1+ (- rmail-total-messages total-messages))))
2559 (while (<= i rmail-total-messages)
2560 (aset rmail-msgref-vector i (list i))
2561 (setq i (1+ i))))
2562 (goto-char (point-min))
2563 (or nomsg (message "Counting new messages...done (%d)" total-messages))))
2564
2565 (defun rmail-set-message-counters ()
2566 (rmail-forget-messages)
2567 (save-excursion
2568 (save-restriction
2569 (widen)
2570 (let* ((point-save (point))
2571 (total-messages 0)
2572 (messages-after-point)
2573 (case-fold-search nil)
2574 (messages-head nil)
2575 (deleted-head nil))
2576 (message "Counting messages...")
2577 (goto-char (point-max))
2578 ;; Put at the end of messages-head
2579 ;; the entry for message N+1, which marks
2580 ;; the end of message N. (N = number of messages).
2581 (search-backward "\n\^_" nil t)
2582 (if (/= (point) (point-max)) (forward-char 1))
2583 (setq messages-head (list (point-marker)))
2584 (rmail-set-message-counters-counter (min (point) point-save))
2585 (setq messages-after-point total-messages)
2586 (rmail-set-message-counters-counter)
2587 (setq rmail-total-messages total-messages)
2588 (setq rmail-current-message
2589 (min total-messages
2590 (max 1 (- total-messages messages-after-point))))
2591 (setq rmail-message-vector
2592 (apply 'vector (cons (point-min-marker) messages-head))
2593 rmail-deleted-vector (concat "0" deleted-head)
2594 rmail-summary-vector (make-vector rmail-total-messages nil)
2595 rmail-msgref-vector (make-vector (1+ rmail-total-messages) nil))
2596 (let ((i 0))
2597 (while (<= i rmail-total-messages)
2598 (aset rmail-msgref-vector i (list i))
2599 (setq i (1+ i))))
2600 (message "Counting messages...done")))))
2601
2602 (defun rmail-set-message-counters-counter (&optional stop)
2603 (let ((start (point))
2604 next)
2605 (while (search-backward "\n\^_\^L" stop t)
2606 ;; Detect messages that have been added with DOS line endings and
2607 ;; convert the line endings for such messages.
2608 (setq next (point))
2609 (if (looking-at "\n\^_\^L\r\n")
2610 (let ((buffer-read-only nil)
2611 (buffer-undo t))
2612 (message "Counting messages...(converting line endings)")
2613 (save-excursion
2614 (goto-char start)
2615 (while (search-backward "\r\n" next t)
2616 (delete-char 1)))))
2617 (setq start next)
2618 (forward-char 1)
2619 (setq messages-head (cons (point-marker) messages-head))
2620 (save-excursion
2621 (setq deleted-head
2622 (cons (if (search-backward ", deleted,"
2623 (prog1 (point)
2624 (forward-line 2))
2625 t)
2626 ?D ?\ )
2627 deleted-head)))
2628 (if (zerop (% (setq total-messages (1+ total-messages)) 20))
2629 (message "Counting messages...%d" total-messages)))))
2630
2631 (defun rmail-beginning-of-message ()
2632 "Show current message starting from the beginning."
2633 (interactive)
2634 (let ((rmail-show-message-hook
2635 (list (function (lambda ()
2636 (goto-char (point-min)))))))
2637 (rmail-show-message rmail-current-message)))
2638
2639 (defun rmail-end-of-message ()
2640 "Show bottom of current message."
2641 (interactive)
2642 (let ((rmail-show-message-hook
2643 (list (function (lambda ()
2644 (goto-char (point-max))
2645 (recenter (1- (window-height))))))))
2646 (rmail-show-message rmail-current-message)))
2647
2648 (defun rmail-unknown-mail-followup-to ()
2649 "Handle a \"Mail-Followup-To\" header field with an unknown mailing list.
2650 Ask the user whether to add that list name to `mail-mailing-lists'."
2651 (save-restriction
2652 (rmail-narrow-to-non-pruned-header)
2653 (let ((mail-followup-to (mail-fetch-field "mail-followup-to" nil t)))
2654 (when mail-followup-to
2655 (let ((addresses
2656 (split-string
2657 (mail-strip-quoted-names mail-followup-to)
2658 ",[[:space:]]+" t)))
2659 (dolist (addr addresses)
2660 (when (and (not (member addr mail-mailing-lists))
2661 (not
2662 ;; taken from rmailsum.el
2663 (string-match
2664 (or rmail-user-mail-address-regexp
2665 (concat "^\\("
2666 (regexp-quote (user-login-name))
2667 "\\($\\|@\\)\\|"
2668 (regexp-quote
2669 (or user-mail-address
2670 (concat (user-login-name) "@"
2671 (or mail-host-address
2672 (system-name)))))
2673 "\\>\\)"))
2674 addr))
2675 (y-or-n-p
2676 (format "Add `%s' to `mail-mailing-lists'? "
2677 addr)))
2678 (customize-save-variable 'mail-mailing-lists
2679 (cons addr mail-mailing-lists)))))))))
2680
2681 (defun rmail-show-message (&optional n no-summary)
2682 "Show message number N (prefix argument), counting from start of file.
2683 If summary buffer is currently displayed, update current message there also."
2684 (interactive "p")
2685 (or (eq major-mode 'rmail-mode)
2686 (switch-to-buffer rmail-buffer))
2687 (rmail-maybe-set-message-counters)
2688 (widen)
2689 (if (zerop rmail-total-messages)
2690 (progn (narrow-to-region (point-min) (1- (point-max)))
2691 (goto-char (point-min))
2692 (setq mode-line-process nil))
2693 (let (blurb coding-system)
2694 (if (not n)
2695 (setq n rmail-current-message)
2696 (cond ((<= n 0)
2697 (setq n 1
2698 rmail-current-message 1
2699 blurb "No previous message"))
2700 ((> n rmail-total-messages)
2701 (setq n rmail-total-messages
2702 rmail-current-message rmail-total-messages
2703 blurb "No following message"))
2704 (t
2705 (setq rmail-current-message n))))
2706 (let ((beg (rmail-msgbeg n)))
2707 (goto-char beg)
2708 (forward-line 1)
2709 (save-excursion
2710 (let ((end (rmail-msgend n)))
2711 (save-restriction
2712 (if (prog1 (= (following-char) ?0)
2713 (forward-line 2)
2714 ;; If there's a Summary-line in the (otherwise empty)
2715 ;; header, we didn't yet get past the EOOH line.
2716 (if (looking-at "^\\*\\*\\* EOOH \\*\\*\\*\n")
2717 (forward-line 1))
2718 (narrow-to-region (point) end))
2719 (rfc822-goto-eoh)
2720 (search-forward "\n*** EOOH ***\n" end t))
2721 (narrow-to-region beg (point))
2722 (goto-char (point-min))
2723 (if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
2724 (let ((coding-system (intern (match-string 1))))
2725 (condition-case nil
2726 (progn
2727 (check-coding-system coding-system)
2728 (setq buffer-file-coding-system coding-system))
2729 (error
2730 (setq buffer-file-coding-system nil))))
2731 (setq buffer-file-coding-system nil)))))
2732 ;; Clear the "unseen" attribute when we show a message.
2733 (rmail-set-attribute "unseen" nil)
2734 (let ((end (rmail-msgend n)))
2735 ;; Reformat the header, or else find the reformatted header.
2736 (if (= (following-char) ?0)
2737 (rmail-reformat-message beg end)
2738 (search-forward "\n*** EOOH ***\n" end t)
2739 (narrow-to-region (point) end)))
2740 (goto-char (point-min))
2741 (walk-windows
2742 (function (lambda (window)
2743 (if (eq (window-buffer window) (current-buffer))
2744 (set-window-point window (point)))))
2745 nil t)
2746 (rmail-display-labels)
2747 (if (eq rmail-enable-mime t)
2748 (funcall rmail-show-mime-function)
2749 (setq rmail-view-buffer rmail-buffer))
2750 (when mail-mailing-lists
2751 (rmail-unknown-mail-followup-to))
2752 (rmail-highlight-headers)
2753 (if transient-mark-mode (deactivate-mark))
2754 (run-hooks 'rmail-show-message-hook)
2755 ;; If there is a summary buffer, try to move to this message
2756 ;; in that buffer. But don't complain if this message
2757 ;; is not mentioned in the summary.
2758 ;; Don't do this at all if we were called on behalf
2759 ;; of cursor motion in the summary buffer.
2760 (and (rmail-summary-exists) (not no-summary)
2761 (let ((curr-msg rmail-current-message))
2762 (rmail-select-summary
2763 (rmail-summary-goto-msg curr-msg t t))))
2764 (with-current-buffer rmail-buffer
2765 (rmail-auto-file))
2766 (if blurb
2767 (message blurb))))))
2768
2769 (defun rmail-redecode-body (coding)
2770 "Decode the body of the current message using coding system CODING.
2771 This is useful with mail messages that have malformed or missing
2772 charset= headers.
2773
2774 This function assumes that the current message is already decoded
2775 and displayed in the RMAIL buffer, but the coding system used to
2776 decode it was incorrect. It then encodes the message back to its
2777 original form, and decodes it again, using the coding system CODING.
2778
2779 Note that if Emacs erroneously auto-detected one of the iso-2022
2780 encodings in the message, this function might fail because the escape
2781 sequences that switch between character sets and also single-shift and
2782 locking-shift codes are impossible to recover. This function is meant
2783 to be used to fix messages encoded with 8-bit encodings, such as
2784 iso-8859, koi8-r, etc."
2785 (interactive "zCoding system for re-decoding this message: ")
2786 (when (not rmail-enable-mime)
2787 (or (eq major-mode 'rmail-mode)
2788 (switch-to-buffer rmail-buffer))
2789 (save-excursion
2790 (let ((pruned (rmail-msg-is-pruned)))
2791 (unwind-protect
2792 (let ((msgbeg (rmail-msgbeg rmail-current-message))
2793 (msgend (rmail-msgend rmail-current-message))
2794 x-coding-header)
2795 ;; We need the message headers pruned (we later restore
2796 ;; the pruned stat to what it was, see the end of
2797 ;; unwind-protect form).
2798 (or pruned
2799 (rmail-toggle-header 1))
2800 (narrow-to-region msgbeg msgend)
2801 (goto-char (point-min))
2802 (when (search-forward "\n*** EOOH ***\n" (point-max) t)
2803 (narrow-to-region msgbeg (point)))
2804 (goto-char (point-min))
2805 (if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
2806 (let ((old-coding (intern (match-string 1)))
2807 (buffer-read-only nil))
2808 (check-coding-system old-coding)
2809 ;; Make sure the new coding system uses the same EOL
2810 ;; conversion, to prevent ^M characters from popping
2811 ;; up all over the place.
2812 (setq coding
2813 (coding-system-change-eol-conversion
2814 coding
2815 (coding-system-eol-type old-coding)))
2816 (setq x-coding-header (point-marker))
2817 (narrow-to-region msgbeg msgend)
2818 (encode-coding-region (point) msgend old-coding)
2819 (decode-coding-region (point) msgend coding)
2820 (setq last-coding-system-used coding)
2821 ;; Rewrite the coding-system header according
2822 ;; to what we did.
2823 (goto-char x-coding-header)
2824 (delete-region (point)
2825 (save-excursion
2826 (beginning-of-line)
2827 (point)))
2828 (insert "X-Coding-System: "
2829 (symbol-name last-coding-system-used))
2830 (set-marker x-coding-header nil)
2831 (rmail-show-message))
2832 (error "No X-Coding-System header found")))
2833 (or pruned
2834 (rmail-toggle-header 0)))))))
2835
2836 ;; Find all occurrences of certain fields, and highlight them.
2837 (defun rmail-highlight-headers ()
2838 ;; Do this only if the system supports faces.
2839 (if (and (fboundp 'internal-find-face)
2840 rmail-highlighted-headers)
2841 (save-excursion
2842 (search-forward "\n\n" nil 'move)
2843 (save-restriction
2844 (narrow-to-region (point-min) (point))
2845 (let ((case-fold-search t)
2846 (inhibit-read-only t)
2847 ;; Highlight with boldface if that is available.
2848 ;; Otherwise use the `highlight' face.
2849 (face (or rmail-highlight-face
2850 (if (face-differs-from-default-p 'bold)
2851 'bold 'highlight)))
2852 ;; List of overlays to reuse.
2853 (overlays rmail-overlay-list))
2854 (goto-char (point-min))
2855 (while (re-search-forward rmail-highlighted-headers nil t)
2856 (skip-chars-forward " \t")
2857 (let ((beg (point))
2858 overlay)
2859 (while (progn (forward-line 1)
2860 (looking-at "[ \t]")))
2861 ;; Back up over newline, then trailing spaces or tabs
2862 (forward-char -1)
2863 (while (member (preceding-char) '(? ?\t))
2864 (forward-char -1))
2865 (if overlays
2866 ;; Reuse an overlay we already have.
2867 (progn
2868 (setq overlay (car overlays)
2869 overlays (cdr overlays))
2870 (overlay-put overlay 'face face)
2871 (move-overlay overlay beg (point)))
2872 ;; Make a new overlay and add it to
2873 ;; rmail-overlay-list.
2874 (setq overlay (make-overlay beg (point)))
2875 (overlay-put overlay 'face face)
2876 (setq rmail-overlay-list
2877 (cons overlay rmail-overlay-list))))))))))
2878
2879 (defun rmail-auto-file ()
2880 "Automatically move a message into a sub-folder based on criteria.
2881 Called when a new message is displayed."
2882 (if (or (rmail-message-labels-p rmail-current-message "filed")
2883 (not (string= (buffer-file-name)
2884 (expand-file-name rmail-file-name))))
2885 ;; Do nothing if it's already been filed.
2886 nil
2887 ;; Find out some basics (common fields)
2888 (let ((from (mail-fetch-field "from"))
2889 (subj (mail-fetch-field "subject"))
2890 (to (concat (mail-fetch-field "to") "," (mail-fetch-field "cc")))
2891 (d rmail-automatic-folder-directives)
2892 (directive-loop nil)
2893 (folder nil))
2894 (while d
2895 (setq folder (car (car d))
2896 directive-loop (cdr (car d)))
2897 (while (and (car directive-loop)
2898 (let ((f (cond
2899 ((string= (car directive-loop) "from") from)
2900 ((string= (car directive-loop) "to") to)
2901 ((string= (car directive-loop) "subject") subj)
2902 (t (mail-fetch-field (car directive-loop))))))
2903 (and f (string-match (car (cdr directive-loop)) f))))
2904 (setq directive-loop (cdr (cdr directive-loop))))
2905 ;; If there are no directives left, then it was a complete match.
2906 (if (null directive-loop)
2907 (if (null folder)
2908 (rmail-delete-forward)
2909 (if (string= "/dev/null" folder)
2910 (rmail-delete-message)
2911 (rmail-output-to-rmail-file folder 1 t)
2912 (setq d nil))))
2913 (setq d (cdr d))))))
2914
2915 (defun rmail-next-message (n)
2916 "Show following message whether deleted or not.
2917 With prefix arg N, moves forward N messages, or backward if N is negative."
2918 (interactive "p")
2919 (set-buffer rmail-buffer)
2920 (rmail-maybe-set-message-counters)
2921 (rmail-show-message (+ rmail-current-message n)))
2922
2923 (defun rmail-previous-message (n)
2924 "Show previous message whether deleted or not.
2925 With prefix arg N, moves backward N messages, or forward if N is negative."
2926 (interactive "p")
2927 (rmail-next-message (- n)))
2928
2929 (defun rmail-next-undeleted-message (n)
2930 "Show following non-deleted message.
2931 With prefix arg N, moves forward N non-deleted messages,
2932 or backward if N is negative.
2933
2934 Returns t if a new message is being shown, nil otherwise."
2935 (interactive "p")
2936 (set-buffer rmail-buffer)
2937 (rmail-maybe-set-message-counters)
2938 (let ((lastwin rmail-current-message)
2939 (current rmail-current-message))
2940 (while (and (> n 0) (< current rmail-total-messages))
2941 (setq current (1+ current))
2942 (if (not (rmail-message-deleted-p current))
2943 (setq lastwin current n (1- n))))
2944 (while (and (< n 0) (> current 1))
2945 (setq current (1- current))
2946 (if (not (rmail-message-deleted-p current))
2947 (setq lastwin current n (1+ n))))
2948 (if (/= lastwin rmail-current-message)
2949 (progn (rmail-show-message lastwin)
2950 t)
2951 (if (< n 0)
2952 (message "No previous nondeleted message"))
2953 (if (> n 0)
2954 (message "No following nondeleted message"))
2955 nil)))
2956
2957 (defun rmail-previous-undeleted-message (n)
2958 "Show previous non-deleted message.
2959 With prefix argument N, moves backward N non-deleted messages,
2960 or forward if N is negative."
2961 (interactive "p")
2962 (rmail-next-undeleted-message (- n)))
2963
2964 (defun rmail-first-message ()
2965 "Show first message in file."
2966 (interactive)
2967 (rmail-maybe-set-message-counters)
2968 (rmail-show-message 1))
2969
2970 (defun rmail-last-message ()
2971 "Show last message in file."
2972 (interactive)
2973 (rmail-maybe-set-message-counters)
2974 (rmail-show-message rmail-total-messages))
2975
2976 (defun rmail-what-message ()
2977 (let ((where (point))
2978 (low 1)
2979 (high rmail-total-messages)
2980 (mid (/ rmail-total-messages 2)))
2981 (while (> (- high low) 1)
2982 (if (>= where (rmail-msgbeg mid))
2983 (setq low mid)
2984 (setq high mid))
2985 (setq mid (+ low (/ (- high low) 2))))
2986 (if (>= where (rmail-msgbeg high)) high low)))
2987
2988 (defun rmail-message-recipients-p (msg recipients &optional primary-only)
2989 (save-restriction
2990 (goto-char (rmail-msgbeg msg))
2991 (search-forward "\n*** EOOH ***\n")
2992 (narrow-to-region (point) (progn (search-forward "\n\n") (point)))
2993 (or (string-match recipients (or (mail-fetch-field "To") ""))
2994 (string-match recipients (or (mail-fetch-field "From") ""))
2995 (if (not primary-only)
2996 (string-match recipients (or (mail-fetch-field "Cc") ""))))))
2997
2998 (defun rmail-message-regexp-p (n regexp)
2999 "Return t, if for message number N, regexp REGEXP matches in the header."
3000 (let ((beg (rmail-msgbeg n))
3001 (end (rmail-msgend n)))
3002 (goto-char beg)
3003 (forward-line 1)
3004 (save-excursion
3005 (save-restriction
3006 (if (prog1 (= (following-char) ?0)
3007 (forward-line 2)
3008 ;; If there's a Summary-line in the (otherwise empty)
3009 ;; header, we didn't yet get past the EOOH line.
3010 (when (looking-at "^\\*\\*\\* EOOH \\*\\*\\*\n")
3011 (forward-line 1))
3012 (setq beg (point))
3013 (narrow-to-region (point) end))
3014 (progn
3015 (rfc822-goto-eoh)
3016 (setq end (point)))
3017 (setq beg (point))
3018 (search-forward "\n*** EOOH ***\n" end t)
3019 (setq end (1+ (match-beginning 0)))))
3020 (goto-char beg)
3021 (if rmail-enable-mime
3022 (funcall rmail-search-mime-header-function n regexp end)
3023 (re-search-forward regexp end t)))))
3024
3025 (defun rmail-search-message (msg regexp)
3026 "Return non-nil, if for message number MSG, regexp REGEXP matches."
3027 (goto-char (rmail-msgbeg msg))
3028 (if rmail-enable-mime
3029 (funcall rmail-search-mime-message-function msg regexp)
3030 (re-search-forward regexp (rmail-msgend msg) t)))
3031
3032 (defvar rmail-search-last-regexp nil)
3033 (defun rmail-search (regexp &optional n)
3034 "Show message containing next match for REGEXP (but not the current msg).
3035 Prefix argument gives repeat count; negative argument means search
3036 backwards (through earlier messages).
3037 Interactively, empty argument means use same regexp used last time."
3038 (interactive
3039 (let* ((reversep (< (prefix-numeric-value current-prefix-arg) 0))
3040 (prompt
3041 (concat (if reversep "Reverse " "") "Rmail search (regexp"))
3042 regexp)
3043 (setq prompt
3044 (concat prompt
3045 (if rmail-search-last-regexp
3046 (concat ", default "
3047 rmail-search-last-regexp "): ")
3048 "): ")))
3049 (setq regexp (read-string prompt))
3050 (cond ((not (equal regexp ""))
3051 (setq rmail-search-last-regexp regexp))
3052 ((not rmail-search-last-regexp)
3053 (error "No previous Rmail search string")))
3054 (list rmail-search-last-regexp
3055 (prefix-numeric-value current-prefix-arg))))
3056 (or n (setq n 1))
3057 (message "%sRmail search for %s..."
3058 (if (< n 0) "Reverse " "")
3059 regexp)
3060 (set-buffer rmail-buffer)
3061 (rmail-maybe-set-message-counters)
3062 (let ((omin (point-min))
3063 (omax (point-max))
3064 (opoint (point))
3065 win
3066 (reversep (< n 0))
3067 (msg rmail-current-message))
3068 (unwind-protect
3069 (progn
3070 (widen)
3071 (while (/= n 0)
3072 ;; Check messages one by one, advancing message number up or down
3073 ;; but searching forward through each message.
3074 (if reversep
3075 (while (and (null win) (> msg 1))
3076 (setq msg (1- msg)
3077 win (rmail-search-message msg regexp)))
3078 (while (and (null win) (< msg rmail-total-messages))
3079 (setq msg (1+ msg)
3080 win (rmail-search-message msg regexp))))
3081 (setq n (+ n (if reversep 1 -1)))))
3082 (if win
3083 (progn
3084 (rmail-show-message msg)
3085 ;; Search forward (if this is a normal search) or backward
3086 ;; (if this is a reverse search) through this message to
3087 ;; position point. This search may fail because REGEXP
3088 ;; was found in the hidden portion of this message. In
3089 ;; that case, move point to the beginning of visible
3090 ;; portion.
3091 (if reversep
3092 (progn
3093 (goto-char (point-max))
3094 (re-search-backward regexp nil 'move))
3095 (goto-char (point-min))
3096 (re-search-forward regexp nil t))
3097 (message "%sRmail search for %s...done"
3098 (if reversep "Reverse " "")
3099 regexp))
3100 (goto-char opoint)
3101 (narrow-to-region omin omax)
3102 (ding)
3103 (message "Search failed: %s" regexp)))))
3104
3105 (defun rmail-search-backwards (regexp &optional n)
3106 "Show message containing previous match for REGEXP.
3107 Prefix argument gives repeat count; negative argument means search
3108 forward (through later messages).
3109 Interactively, empty argument means use same regexp used last time."
3110 (interactive
3111 (let* ((reversep (>= (prefix-numeric-value current-prefix-arg) 0))
3112 (prompt
3113 (concat (if reversep "Reverse " "") "Rmail search (regexp"))
3114 regexp)
3115 (setq prompt
3116 (concat prompt
3117 (if rmail-search-last-regexp
3118 (concat ", default "
3119 rmail-search-last-regexp "): ")
3120 "): ")))
3121 (setq regexp (read-string prompt))
3122 (cond ((not (equal regexp ""))
3123 (setq rmail-search-last-regexp regexp))
3124 ((not rmail-search-last-regexp)
3125 (error "No previous Rmail search string")))
3126 (list rmail-search-last-regexp
3127 (prefix-numeric-value current-prefix-arg))))
3128 (rmail-search regexp (- (or n 1))))
3129
3130 ;; Show the first message which has the `unseen' attribute.
3131 (defun rmail-first-unseen-message ()
3132 (rmail-maybe-set-message-counters)
3133 (let ((current 1)
3134 found)
3135 (save-restriction
3136 (widen)
3137 (while (and (not found) (<= current rmail-total-messages))
3138 (if (rmail-message-labels-p current ", ?\\(unseen\\),")
3139 (setq found current))
3140 (setq current (1+ current))))
3141 ;; Let the caller show the message.
3142 ;; (if found
3143 ;; (rmail-show-message found))
3144 found))
3145
3146 (defun rmail-next-same-subject (n)
3147 "Go to the next mail message having the same subject header.
3148 With prefix argument N, do this N times.
3149 If N is negative, go backwards instead."
3150 (interactive "p")
3151 (let ((subject (mail-fetch-field "Subject"))
3152 (forward (> n 0))
3153 (i rmail-current-message)
3154 (case-fold-search t)
3155 search-regexp found)
3156 (if (string-match "\\`[ \t]+" subject)
3157 (setq subject (substring subject (match-end 0))))
3158 (if (string-match "Re:[ \t]*" subject)
3159 (setq subject (substring subject (match-end 0))))
3160 (if (string-match "[ \t]+\\'" subject)
3161 (setq subject (substring subject 0 (match-beginning 0))))
3162 (setq search-regexp (concat "^Subject: *\\(Re:[ \t]*\\)?"
3163 (regexp-quote subject)
3164 "[ \t]*\n"))
3165 (save-excursion
3166 (save-restriction
3167 (widen)
3168 (while (and (/= n 0)
3169 (if forward
3170 (< i rmail-total-messages)
3171 (> i 1)))
3172 (let (done)
3173 (while (and (not done)
3174 (if forward
3175 (< i rmail-total-messages)
3176 (> i 1)))
3177 (setq i (if forward (1+ i) (1- i)))
3178 (goto-char (rmail-msgbeg i))
3179 (search-forward "\n*** EOOH ***\n")
3180 (let ((beg (point)) end)
3181 (search-forward "\n\n")
3182 (setq end (point))
3183 (goto-char beg)
3184 (setq done (re-search-forward search-regexp end t))))
3185 (if done (setq found i)))
3186 (setq n (if forward (1- n) (1+ n))))))
3187 (if found
3188 (rmail-show-message found)
3189 (error "No %s message with same subject"
3190 (if forward "following" "previous")))))
3191
3192 (defun rmail-previous-same-subject (n)
3193 "Go to the previous mail message having the same subject header.
3194 With prefix argument N, do this N times.
3195 If N is negative, go forwards instead."
3196 (interactive "p")
3197 (rmail-next-same-subject (- n)))
3198 \f
3199 ;;;; *** Rmail Message Deletion Commands ***
3200
3201 (defun rmail-message-deleted-p (n)
3202 (= (aref rmail-deleted-vector n) ?D))
3203
3204 (defun rmail-set-message-deleted-p (n state)
3205 (aset rmail-deleted-vector n (if state ?D ?\ )))
3206
3207 (defun rmail-delete-message ()
3208 "Delete this message and stay on it."
3209 (interactive)
3210 (rmail-set-attribute "deleted" t)
3211 (run-hooks 'rmail-delete-message-hook))
3212
3213 (defun rmail-undelete-previous-message ()
3214 "Back up to deleted message, select it, and undelete it."
3215 (interactive)
3216 (set-buffer rmail-buffer)
3217 (let ((msg rmail-current-message))
3218 (while (and (> msg 0)
3219 (not (rmail-message-deleted-p msg)))
3220 (setq msg (1- msg)))
3221 (if (= msg 0)
3222 (error "No previous deleted message")
3223 (if (/= msg rmail-current-message)
3224 (rmail-show-message msg))
3225 (rmail-set-attribute "deleted" nil)
3226 (if (rmail-summary-exists)
3227 (save-excursion
3228 (set-buffer rmail-summary-buffer)
3229 (rmail-summary-mark-undeleted msg)))
3230 (rmail-maybe-display-summary))))
3231
3232 (defun rmail-delete-forward (&optional backward)
3233 "Delete this message and move to next nondeleted one.
3234 Deleted messages stay in the file until the \\[rmail-expunge] command is given.
3235 With prefix argument, delete and move backward.
3236
3237 Returns t if a new message is displayed after the delete, or nil otherwise."
3238 (interactive "P")
3239 (rmail-set-attribute "deleted" t)
3240 (run-hooks 'rmail-delete-message-hook)
3241 (let ((del-msg rmail-current-message))
3242 (if (rmail-summary-exists)
3243 (rmail-select-summary
3244 (rmail-summary-mark-deleted del-msg)))
3245 (prog1 (rmail-next-undeleted-message (if backward -1 1))
3246 (rmail-maybe-display-summary))))
3247
3248 (defun rmail-delete-backward ()
3249 "Delete this message and move to previous nondeleted one.
3250 Deleted messages stay in the file until the \\[rmail-expunge] command is given."
3251 (interactive)
3252 (rmail-delete-forward t))
3253
3254 ;; Compute the message number a given message would have after expunging.
3255 ;; The present number of the message is OLDNUM.
3256 ;; DELETEDVEC should be rmail-deleted-vector.
3257 ;; The value is nil for a message that would be deleted.
3258 (defun rmail-msg-number-after-expunge (deletedvec oldnum)
3259 (if (or (null oldnum) (= (aref deletedvec oldnum) ?D))
3260 nil
3261 (let ((i 0)
3262 (newnum 0))
3263 (while (< i oldnum)
3264 (if (/= (aref deletedvec i) ?D)
3265 (setq newnum (1+ newnum)))
3266 (setq i (1+ i)))
3267 newnum)))
3268
3269 (defun rmail-expunge-confirmed ()
3270 "Return t if deleted message should be expunged. If necessary, ask the user.
3271 See also user-option `rmail-confirm-expunge'."
3272 (set-buffer rmail-buffer)
3273 (or (not (stringp rmail-deleted-vector))
3274 (not (string-match "D" rmail-deleted-vector))
3275 (null rmail-confirm-expunge)
3276 (funcall rmail-confirm-expunge
3277 "Erase deleted messages from Rmail file? ")))
3278
3279 (defun rmail-only-expunge (&optional dont-show)
3280 "Actually erase all deleted messages in the file."
3281 (interactive)
3282 (set-buffer rmail-buffer)
3283 (message "Expunging deleted messages...")
3284 ;; Discard all undo records for this buffer.
3285 (or (eq buffer-undo-list t)
3286 (setq buffer-undo-list nil))
3287 (rmail-maybe-set-message-counters)
3288 (let* ((omax (- (buffer-size) (point-max)))
3289 (omin (- (buffer-size) (point-min)))
3290 (opoint (if (and (> rmail-current-message 0)
3291 (rmail-message-deleted-p rmail-current-message))
3292 0
3293 (if rmail-enable-mime
3294 (with-current-buffer rmail-view-buffer
3295 (- (point)(point-min)))
3296 (- (point) (point-min)))))
3297 (messages-head (cons (aref rmail-message-vector 0) nil))
3298 (messages-tail messages-head)
3299 ;; Don't make any undo records for the expunging.
3300 (buffer-undo-list t)
3301 (win))
3302 (unwind-protect
3303 (save-excursion
3304 (widen)
3305 (goto-char (point-min))
3306 (let ((counter 0)
3307 (number 1)
3308 (total rmail-total-messages)
3309 (new-message-number rmail-current-message)
3310 (new-summary nil)
3311 (new-msgref (list (list 0)))
3312 (rmailbuf (current-buffer))
3313 (buffer-read-only nil)
3314 (messages rmail-message-vector)
3315 (deleted rmail-deleted-vector)
3316 (summary rmail-summary-vector))
3317 (setq rmail-total-messages nil
3318 rmail-current-message nil
3319 rmail-message-vector nil
3320 rmail-deleted-vector nil
3321 rmail-summary-vector nil)
3322
3323 (while (<= number total)
3324 (if (= (aref deleted number) ?D)
3325 (progn
3326 (delete-region
3327 (marker-position (aref messages number))
3328 (marker-position (aref messages (1+ number))))
3329 (move-marker (aref messages number) nil)
3330 (if (> new-message-number counter)
3331 (setq new-message-number (1- new-message-number))))
3332 (setq counter (1+ counter))
3333 (setq messages-tail
3334 (setcdr messages-tail
3335 (cons (aref messages number) nil)))
3336 (setq new-summary
3337 (cons (if (= counter number) (aref summary (1- number)))
3338 new-summary))
3339 (setq new-msgref
3340 (cons (aref rmail-msgref-vector number)
3341 new-msgref))
3342 (setcar (car new-msgref) counter))
3343 (if (zerop (% (setq number (1+ number)) 20))
3344 (message "Expunging deleted messages...%d" number)))
3345 (setq messages-tail
3346 (setcdr messages-tail
3347 (cons (aref messages number) nil)))
3348 (setq rmail-current-message new-message-number
3349 rmail-total-messages counter
3350 rmail-message-vector (apply 'vector messages-head)
3351 rmail-deleted-vector (make-string (1+ counter) ?\ )
3352 rmail-summary-vector (vconcat (nreverse new-summary))
3353 rmail-msgref-vector (apply 'vector (nreverse new-msgref))
3354 win t)))
3355 (message "Expunging deleted messages...done")
3356 (if (not win)
3357 (narrow-to-region (- (buffer-size) omin) (- (buffer-size) omax)))
3358 (if (not dont-show)
3359 (rmail-show-message
3360 (if (zerop rmail-current-message) 1 nil)))
3361 (if rmail-enable-mime
3362 (goto-char (+ (point-min) opoint))
3363 (goto-char (+ (point) opoint))))))
3364
3365 (defun rmail-expunge ()
3366 "Erase deleted messages from Rmail file and summary buffer."
3367 (interactive)
3368 (when (rmail-expunge-confirmed)
3369 (rmail-only-expunge)
3370 (if (rmail-summary-exists)
3371 (rmail-select-summary (rmail-update-summary)))))
3372 \f
3373 ;;;; *** Rmail Mailing Commands ***
3374
3375 (defun rmail-start-mail (&optional noerase to subject in-reply-to cc
3376 replybuffer sendactions same-window others)
3377 (let (yank-action)
3378 (if replybuffer
3379 (setq yank-action (list 'insert-buffer replybuffer)))
3380 (setq others (cons (cons "cc" cc) others))
3381 (setq others (cons (cons "in-reply-to" in-reply-to) others))
3382 (if same-window
3383 (compose-mail to subject others
3384 noerase nil
3385 yank-action sendactions)
3386 (if rmail-mail-new-frame
3387 (prog1
3388 (compose-mail to subject others
3389 noerase 'switch-to-buffer-other-frame
3390 yank-action sendactions)
3391 ;; This is not a standard frame parameter;
3392 ;; nothing except sendmail.el looks at it.
3393 (modify-frame-parameters (selected-frame)
3394 '((mail-dedicated-frame . t))))
3395 (compose-mail to subject others
3396 noerase 'switch-to-buffer-other-window
3397 yank-action sendactions)))))
3398
3399 (defun rmail-mail ()
3400 "Send mail in another window.
3401 While composing the message, use \\[mail-yank-original] to yank the
3402 original message into it."
3403 (interactive)
3404 (rmail-start-mail nil nil nil nil nil rmail-view-buffer))
3405
3406 (defun rmail-continue ()
3407 "Continue composing outgoing message previously being composed."
3408 (interactive)
3409 (rmail-start-mail t))
3410
3411 (defun rmail-reply (just-sender)
3412 "Reply to the current message.
3413 Normally include CC: to all other recipients of original message;
3414 prefix argument means ignore them. While composing the reply,
3415 use \\[mail-yank-original] to yank the original message into it."
3416 (interactive "P")
3417 (let (from reply-to cc subject date to message-id references
3418 resent-to resent-cc resent-reply-to
3419 (msgnum rmail-current-message))
3420 (save-excursion
3421 (save-restriction
3422 (if rmail-enable-mime
3423 (narrow-to-region
3424 (goto-char (point-min))
3425 (if (search-forward "\n\n" nil 'move)
3426 (1+ (match-beginning 0))
3427 (point)))
3428 (widen)
3429 (goto-char (rmail-msgbeg rmail-current-message))
3430 (forward-line 1)
3431 (if (= (following-char) ?0)
3432 (narrow-to-region
3433 (progn (forward-line 2)
3434 (point))
3435 (progn (search-forward "\n\n" (rmail-msgend rmail-current-message)
3436 'move)
3437 (point)))
3438 (narrow-to-region (point)
3439 (progn (search-forward "\n*** EOOH ***\n")
3440 (beginning-of-line) (point)))))
3441 (setq from (mail-fetch-field "from")
3442 reply-to (or (mail-fetch-field "mail-reply-to" nil t)
3443 (mail-fetch-field "reply-to" nil t)
3444 from)
3445 subject (mail-fetch-field "subject")
3446 date (mail-fetch-field "date")
3447 message-id (mail-fetch-field "message-id")
3448 references (mail-fetch-field "references" nil nil t)
3449 resent-reply-to (mail-fetch-field "resent-reply-to" nil t)
3450 resent-cc (and (not just-sender)
3451 (mail-fetch-field "resent-cc" nil t))
3452 resent-to (or (mail-fetch-field "resent-to" nil t) "")
3453 ;;; resent-subject (mail-fetch-field "resent-subject")
3454 ;;; resent-date (mail-fetch-field "resent-date")
3455 ;;; resent-message-id (mail-fetch-field "resent-message-id")
3456 )
3457 (unless just-sender
3458 (if (mail-fetch-field "mail-followup-to" nil t)
3459 ;; If this header field is present, use it instead of the To and CC fields.
3460 (setq to (mail-fetch-field "mail-followup-to" nil t))
3461 (setq cc (or (mail-fetch-field "cc" nil t) "")
3462 to (or (mail-fetch-field "to" nil t) ""))))
3463
3464 ))
3465
3466 ;; Merge the resent-to and resent-cc into the to and cc.
3467 (if (and resent-to (not (equal resent-to "")))
3468 (if (not (equal to ""))
3469 (setq to (concat to ", " resent-to))
3470 (setq to resent-to)))
3471 (if (and resent-cc (not (equal resent-cc "")))
3472 (if (not (equal cc ""))
3473 (setq cc (concat cc ", " resent-cc))
3474 (setq cc resent-cc)))
3475 ;; Add `Re: ' to subject if not there already.
3476 (and (stringp subject)
3477 (setq subject
3478 (concat rmail-reply-prefix
3479 (if (let ((case-fold-search t))
3480 (string-match rmail-reply-regexp subject))
3481 (substring subject (match-end 0))
3482 subject))))
3483 (rmail-start-mail
3484 nil
3485 ;; Using mail-strip-quoted-names is undesirable with newer mailers
3486 ;; since they can handle the names unstripped.
3487 ;; I don't know whether there are other mailers that still
3488 ;; need the names to be stripped.
3489 ;;; (mail-strip-quoted-names reply-to)
3490 ;; Remove unwanted names from reply-to, since Mail-Followup-To
3491 ;; header causes all the names in it to wind up in reply-to, not
3492 ;; in cc. But if what's left is an empty list, use the original.
3493 (let* ((reply-to-list (rmail-dont-reply-to reply-to)))
3494 (if (string= reply-to-list "") reply-to reply-to-list))
3495 subject
3496 (rmail-make-in-reply-to-field from date message-id)
3497 (if just-sender
3498 nil
3499 ;; mail-strip-quoted-names is NOT necessary for rmail-dont-reply-to
3500 ;; to do its job.
3501 (let* ((cc-list (rmail-dont-reply-to
3502 (mail-strip-quoted-names
3503 (if (null cc) to (concat to ", " cc))))))
3504 (if (string= cc-list "") nil cc-list)))
3505 rmail-view-buffer
3506 (list (list 'rmail-mark-message
3507 rmail-buffer
3508 (with-current-buffer rmail-buffer
3509 (aref rmail-msgref-vector msgnum))
3510 "answered"))
3511 nil
3512 (list (cons "References" (concat (mapconcat 'identity references " ")
3513 " " message-id))))))
3514
3515 (defun rmail-mark-message (buffer msgnum-list attribute)
3516 "Give BUFFER's message number in MSGNUM-LIST the attribute ATTRIBUTE.
3517 This is use in the send-actions for message buffers.
3518 MSGNUM-LIST is a list of the form (MSGNUM)
3519 which is an element of rmail-msgref-vector."
3520 (save-excursion
3521 (set-buffer buffer)
3522 (if (car msgnum-list)
3523 (rmail-set-attribute attribute t (car msgnum-list)))))
3524
3525 (defun rmail-make-in-reply-to-field (from date message-id)
3526 (cond ((not from)
3527 (if message-id
3528 message-id
3529 nil))
3530 (mail-use-rfc822
3531 (require 'rfc822)
3532 (let ((tem (car (rfc822-addresses from))))
3533 (if message-id
3534 (if (or (not tem)
3535 (string-match
3536 (regexp-quote (if (string-match "@[^@]*\\'" tem)
3537 (substring tem 0
3538 (match-beginning 0))
3539 tem))
3540 message-id))
3541 ;; missing From, or Message-ID is sufficiently informative
3542 message-id
3543 (concat message-id " (" tem ")"))
3544 ;; Copy TEM, discarding text properties.
3545 (setq tem (copy-sequence tem))
3546 (set-text-properties 0 (length tem) nil tem)
3547 (setq tem (copy-sequence tem))
3548 ;; Use prin1 to fake RFC822 quoting
3549 (let ((field (prin1-to-string tem)))
3550 (if date
3551 (concat field "'s message of " date)
3552 field)))))
3553 ((let* ((foo "[^][\000-\037()<>@,;:\\\" ]+")
3554 (bar "[^][\000-\037()<>@,;:\\\"]+"))
3555 ;; These strings both match all non-ASCII characters.
3556 (or (string-match (concat "\\`[ \t]*\\(" bar
3557 "\\)\\(<" foo "@" foo ">\\)?[ \t]*\\'")
3558 ;; "Unix Loser <Foo@bar.edu>" => "Unix Loser"
3559 from)
3560 (string-match (concat "\\`[ \t]*<" foo "@" foo ">[ \t]*(\\("
3561 bar "\\))[ \t]*\\'")
3562 ;; "<Bugs@bar.edu>" (Losing Unix) => "Losing Unix"
3563 from)))
3564 (let ((start (match-beginning 1))
3565 (end (match-end 1)))
3566 ;; Trim whitespace which above regexp match allows
3567 (while (and (< start end)
3568 (memq (aref from start) '(?\t ?\ )))
3569 (setq start (1+ start)))
3570 (while (and (< start end)
3571 (memq (aref from (1- end)) '(?\t ?\ )))
3572 (setq end (1- end)))
3573 (let ((field (substring from start end)))
3574 (if date (setq field (concat "message from " field " on " date)))
3575 (if message-id
3576 ;; "<AA259@bar.edu> (message from Unix Loser on 1-Apr-89)"
3577 (concat message-id " (" field ")")
3578 field))))
3579 (t
3580 ;; If we can't kludge it simply, do it correctly
3581 (let ((mail-use-rfc822 t))
3582 (rmail-make-in-reply-to-field from date message-id)))))
3583 \f
3584 (defun rmail-forward (resend)
3585 "Forward the current message to another user.
3586 With prefix argument, \"resend\" the message instead of forwarding it;
3587 see the documentation of `rmail-resend'."
3588 (interactive "P")
3589 (if resend
3590 (call-interactively 'rmail-resend)
3591 (let ((forward-buffer rmail-buffer)
3592 (msgnum rmail-current-message)
3593 (subject (concat "["
3594 (let ((from (or (mail-fetch-field "From")
3595 (mail-fetch-field ">From"))))
3596 (if from
3597 (concat (mail-strip-quoted-names from) ": ")
3598 ""))
3599 (or (mail-fetch-field "Subject") "")
3600 "]")))
3601 (if (rmail-start-mail
3602 nil nil subject nil nil nil
3603 (list (list 'rmail-mark-message
3604 forward-buffer
3605 (with-current-buffer rmail-buffer
3606 (aref rmail-msgref-vector msgnum))
3607 "forwarded"))
3608 ;; If only one window, use it for the mail buffer.
3609 ;; Otherwise, use another window for the mail buffer
3610 ;; so that the Rmail buffer remains visible
3611 ;; and sending the mail will get back to it.
3612 (and (not rmail-mail-new-frame) (one-window-p t)))
3613 ;; The mail buffer is now current.
3614 (save-excursion
3615 ;; Insert after header separator--before signature if any.
3616 (goto-char (mail-text-start))
3617 (if (or rmail-enable-mime rmail-enable-mime-composing)
3618 (funcall rmail-insert-mime-forwarded-message-function
3619 forward-buffer)
3620 (insert "------- Start of forwarded message -------\n")
3621 ;; Quote lines with `- ' if they start with `-'.
3622 (let ((beg (point)) end)
3623 (setq end (point-marker))
3624 (set-marker-insertion-type end t)
3625 (insert-buffer-substring forward-buffer)
3626 (goto-char beg)
3627 (while (re-search-forward "^-" end t)
3628 (beginning-of-line)
3629 (insert "- ")
3630 (forward-line 1))
3631 (goto-char end)
3632 (skip-chars-backward "\n")
3633 (if (< (point) end)
3634 (forward-char 1))
3635 (delete-region (point) end)
3636 (set-marker end nil))
3637 (insert "------- End of forwarded message -------\n"))
3638 (push-mark))))))
3639 \f
3640 (defun rmail-resend (address &optional from comment mail-alias-file)
3641 "Resend current message to ADDRESSES.
3642 ADDRESSES should be a single address, a string consisting of several
3643 addresses separated by commas, or a list of addresses.
3644
3645 Optional FROM is the address to resend the message from, and
3646 defaults from the value of `user-mail-address'.
3647 Optional COMMENT is a string to insert as a comment in the resent message.
3648 Optional ALIAS-FILE is alternate aliases file to be used by sendmail,
3649 typically for purposes of moderating a list."
3650 (interactive "sResend to: ")
3651 (require 'sendmail)
3652 (require 'mailalias)
3653 (unless (or (eq rmail-view-buffer (current-buffer))
3654 (eq rmail-buffer (current-buffer)))
3655 (error "Not an Rmail buffer"))
3656 (if (not from) (setq from user-mail-address))
3657 (let ((tembuf (generate-new-buffer " sendmail temp"))
3658 (case-fold-search nil)
3659 (mail-personal-alias-file
3660 (or mail-alias-file mail-personal-alias-file))
3661 (mailbuf rmail-buffer))
3662 (unwind-protect
3663 (with-current-buffer tembuf
3664 ;;>> Copy message into temp buffer
3665 (if rmail-enable-mime
3666 (funcall rmail-insert-mime-resent-message-function mailbuf)
3667 (insert-buffer-substring mailbuf))
3668 (goto-char (point-min))
3669 ;; Delete any Sender field, since that's not specifiable.
3670 ; Only delete Sender fields in the actual header.
3671 (re-search-forward "^$" nil 'move)
3672 ; Using "while" here rather than "if" because some buggy mail
3673 ; software may have inserted multiple Sender fields.
3674 (while (re-search-backward "^Sender:" nil t)
3675 (let (beg)
3676 (setq beg (point))
3677 (forward-line 1)
3678 (while (looking-at "[ \t]")
3679 (forward-line 1))
3680 (delete-region beg (point))))
3681 ; Go back to the beginning of the buffer so the Resent- fields
3682 ; are inserted there.
3683 (goto-char (point-min))
3684 ;;>> Insert resent-from:
3685 (insert "Resent-From: " from "\n")
3686 (insert "Resent-Date: " (mail-rfc822-date) "\n")
3687 ;;>> Insert resent-to: and bcc if need be.
3688 (let ((before (point)))
3689 (if mail-self-blind
3690 (insert "Resent-Bcc: " (user-login-name) "\n"))
3691 (insert "Resent-To: " (if (stringp address)
3692 address
3693 (mapconcat 'identity address ",\n\t"))
3694 "\n")
3695 ;; Expand abbrevs in the recipients.
3696 (save-excursion
3697 (if (featurep 'mailabbrev)
3698 (let ((end (point-marker))
3699 (local-abbrev-table mail-abbrevs)
3700 (old-syntax-table (syntax-table)))
3701 (if (and (not (vectorp mail-abbrevs))
3702 (file-exists-p mail-personal-alias-file))
3703 (build-mail-abbrevs))
3704 (unless mail-abbrev-syntax-table
3705 (mail-abbrev-make-syntax-table))
3706 (set-syntax-table mail-abbrev-syntax-table)
3707 (goto-char before)
3708 (while (and (< (point) end)
3709 (progn (forward-word 1)
3710 (<= (point) end)))
3711 (expand-abbrev))
3712 (set-syntax-table old-syntax-table))
3713 (expand-mail-aliases before (point)))))
3714 ;;>> Set up comment, if any.
3715 (if (and (sequencep comment) (not (zerop (length comment))))
3716 (let ((before (point))
3717 after)
3718 (insert comment)
3719 (or (eolp) (insert "\n"))
3720 (setq after (point))
3721 (goto-char before)
3722 (while (< (point) after)
3723 (insert "Resent-Comment: ")
3724 (forward-line 1))))
3725 ;; Don't expand aliases in the destination fields
3726 ;; of the original message.
3727 (let (mail-aliases)
3728 (funcall send-mail-function)))
3729 (kill-buffer tembuf))
3730 (with-current-buffer rmail-buffer
3731 (rmail-set-attribute "resent" t rmail-current-message))))
3732 \f
3733 (defvar mail-unsent-separator
3734 (concat "^ *---+ +Unsent message follows +---+ *$\\|"
3735 "^ *---+ +Returned message +---+ *$\\|"
3736 "^ *---+ *Returned mail follows *---+ *$\\|"
3737 "^Start of returned message$\\|"
3738 "^---+ Below this line is a copy of the message.$\\|"
3739 "^ *---+ +Original message +---+ *$\\|"
3740 "^ *--+ +begin message +--+ *$\\|"
3741 "^ *---+ +Original message follows +---+ *$\\|"
3742 "^ *---+ +Your message follows +---+ *$\\|"
3743 "^|? *---+ +Message text follows: +---+ *|?$\\|"
3744 "^ *---+ +This is a copy of \\w+ message, including all the headers.*---+ *$")
3745 "A regexp that matches the separator before the text of a failed message.")
3746
3747 (defvar mail-mime-unsent-header "^Content-Type: message/rfc822 *$"
3748 "A regexp that matches the header of a MIME body part with a failed message.")
3749
3750 (defun rmail-retry-failure ()
3751 "Edit a mail message which is based on the contents of the current message.
3752 For a message rejected by the mail system, extract the interesting headers and
3753 the body of the original message.
3754 If the failed message is a MIME multipart message, it is searched for a
3755 body part with a header which matches the variable `mail-mime-unsent-header'.
3756 Otherwise, the variable `mail-unsent-separator' should match the string that
3757 delimits the returned original message.
3758 The variable `rmail-retry-ignored-headers' is a regular expression
3759 specifying headers which should not be copied into the new message."
3760 (interactive)
3761 (require 'mail-utils)
3762 (let ((rmail-this-buffer (current-buffer))
3763 (msgnum rmail-current-message)
3764 bounce-start bounce-end bounce-indent resending
3765 ;; Fetch any content-type header in current message
3766 ;; Must search thru the whole unpruned header.
3767 (content-type
3768 (save-excursion
3769 (save-restriction
3770 (rmail-narrow-to-non-pruned-header)
3771 (mail-fetch-field "Content-Type") ))))
3772 (save-excursion
3773 (goto-char (point-min))
3774 (let ((case-fold-search t))
3775 (if (and content-type
3776 (string-match
3777 ";[\n\t ]*boundary=\"?\\([-0-9a-z'()+_,./:=? ]+\\)\"?"
3778 content-type))
3779 ;; Handle a MIME multipart bounce message.
3780 (let ((codestring
3781 (concat "\n--"
3782 (substring content-type (match-beginning 1)
3783 (match-end 1)))))
3784 (unless (re-search-forward mail-mime-unsent-header nil t)
3785 (error "Cannot find beginning of header in failed message"))
3786 (unless (search-forward "\n\n" nil t)
3787 (error "Cannot find start of Mime data in failed message"))
3788 (setq bounce-start (point))
3789 (if (search-forward codestring nil t)
3790 (setq bounce-end (match-beginning 0))
3791 (setq bounce-end (point-max))))
3792 ;; Non-MIME bounce.
3793 (or (re-search-forward mail-unsent-separator nil t)
3794 (error "Cannot parse this as a failure message"))
3795 (skip-chars-forward "\n")
3796 ;; Support a style of failure message in which the original
3797 ;; message is indented, and included within lines saying
3798 ;; `Start of returned message' and `End of returned message'.
3799 (if (looking-at " +Received:")
3800 (progn
3801 (setq bounce-start (point))
3802 (skip-chars-forward " ")
3803 (setq bounce-indent (- (current-column)))
3804 (goto-char (point-max))
3805 (re-search-backward "^End of returned message$" nil t)
3806 (setq bounce-end (point)))
3807 ;; One message contained a few random lines before
3808 ;; the old message header. The first line of the
3809 ;; message started with two hyphens. A blank line
3810 ;; followed these random lines. The same line
3811 ;; beginning with two hyphens was possibly marking
3812 ;; the end of the message.
3813 (if (looking-at "^--")
3814 (let ((boundary (buffer-substring-no-properties
3815 (point)
3816 (progn (end-of-line) (point)))))
3817 (search-forward "\n\n")
3818 (skip-chars-forward "\n")
3819 (setq bounce-start (point))
3820 (goto-char (point-max))
3821 (search-backward (concat "\n\n" boundary) bounce-start t)
3822 (setq bounce-end (point)))
3823 (setq bounce-start (point)
3824 bounce-end (point-max)))
3825 (unless (search-forward "\n\n" nil t)
3826 (error "Cannot find end of header in failed message"))))))
3827 ;; We have found the message that bounced, within the current message.
3828 ;; Now start sending new message; default header fields from original.
3829 ;; Turn off the usual actions for initializing the message body
3830 ;; because we want to get only the text from the failure message.
3831 (let (mail-signature mail-setup-hook)
3832 (if (rmail-start-mail nil nil nil nil nil rmail-this-buffer
3833 (list (list 'rmail-mark-message
3834 rmail-this-buffer
3835 (aref rmail-msgref-vector msgnum)
3836 "retried")))
3837 ;; Insert original text as initial text of new draft message.
3838 ;; Bind inhibit-read-only since the header delimiter
3839 ;; of the previous message was probably read-only.
3840 (let ((inhibit-read-only t)
3841 rmail-displayed-headers
3842 rmail-ignored-headers)
3843 (erase-buffer)
3844 (insert-buffer-substring rmail-this-buffer
3845 bounce-start bounce-end)
3846 (goto-char (point-min))
3847 (if bounce-indent
3848 (indent-rigidly (point-min) (point-max) bounce-indent))
3849 (rmail-clear-headers rmail-retry-ignored-headers)
3850 (rmail-clear-headers "^sender:\\|^return-path:\\|^received:")
3851 (mail-sendmail-delimit-header)
3852 (save-restriction
3853 (narrow-to-region (point-min) (mail-header-end))
3854 (setq resending (mail-fetch-field "resent-to"))
3855 (if mail-self-blind
3856 (if resending
3857 (insert "Resent-Bcc: " (user-login-name) "\n")
3858 (insert "BCC: " (user-login-name) "\n"))))
3859 (goto-char (point-min))
3860 (mail-position-on-field (if resending "Resent-To" "To") t))))))
3861 \f
3862 (defun rmail-summary-exists ()
3863 "Non-nil iff in an RMAIL buffer and an associated summary buffer exists.
3864 In fact, the non-nil value returned is the summary buffer itself."
3865 (and rmail-summary-buffer (buffer-name rmail-summary-buffer)
3866 rmail-summary-buffer))
3867
3868 (defun rmail-summary-displayed ()
3869 "t iff in RMAIL buffer and an associated summary buffer is displayed."
3870 (and rmail-summary-buffer (get-buffer-window rmail-summary-buffer)))
3871
3872 (defcustom rmail-redisplay-summary nil
3873 "*Non-nil means Rmail should show the summary when it changes.
3874 This has an effect only if a summary buffer exists."
3875 :type 'boolean
3876 :group 'rmail-summary)
3877
3878 (defcustom rmail-summary-window-size nil
3879 "*Non-nil means specify the height for an Rmail summary window."
3880 :type '(choice (const :tag "Disabled" nil) integer)
3881 :group 'rmail-summary)
3882
3883 ;; Put the summary buffer back on the screen, if user wants that.
3884 (defun rmail-maybe-display-summary ()
3885 (let ((selected (selected-window))
3886 window)
3887 ;; If requested, make sure the summary is displayed.
3888 (and rmail-summary-buffer (buffer-name rmail-summary-buffer)
3889 rmail-redisplay-summary
3890 (if (get-buffer-window rmail-summary-buffer 0)
3891 ;; It's already in some frame; show that one.
3892 (let ((frame (window-frame
3893 (get-buffer-window rmail-summary-buffer 0))))
3894 (make-frame-visible frame)
3895 (raise-frame frame))
3896 (display-buffer rmail-summary-buffer)))
3897 ;; If requested, set the height of the summary window.
3898 (and rmail-summary-buffer (buffer-name rmail-summary-buffer)
3899 rmail-summary-window-size
3900 (setq window (get-buffer-window rmail-summary-buffer))
3901 ;; Don't try to change the size if just one window in frame.
3902 (not (eq window (frame-root-window (window-frame window))))
3903 (unwind-protect
3904 (progn
3905 (select-window window)
3906 (enlarge-window (- rmail-summary-window-size (window-height))))
3907 (select-window selected)))))
3908 \f
3909 ;;;; *** Rmail Local Fontification ***
3910
3911 (defun rmail-fontify-buffer-function ()
3912 ;; This function's symbol is bound to font-lock-fontify-buffer-function.
3913 (add-hook 'rmail-show-message-hook 'rmail-fontify-message nil t)
3914 ;; If we're already showing a message, fontify it now.
3915 (if rmail-current-message (rmail-fontify-message))
3916 ;; Prevent Font Lock mode from kicking in.
3917 (setq font-lock-fontified t))
3918
3919 (defun rmail-unfontify-buffer-function ()
3920 ;; This function's symbol is bound to font-lock-fontify-unbuffer-function.
3921 (let ((modified (buffer-modified-p))
3922 (buffer-undo-list t) (inhibit-read-only t)
3923 before-change-functions after-change-functions
3924 buffer-file-name buffer-file-truename)
3925 (save-restriction
3926 (widen)
3927 (remove-hook 'rmail-show-message-hook 'rmail-fontify-message t)
3928 (remove-text-properties (point-min) (point-max) '(rmail-fontified nil))
3929 (font-lock-default-unfontify-buffer)
3930 (and (not modified) (buffer-modified-p) (set-buffer-modified-p nil)))))
3931
3932 (defun rmail-fontify-message ()
3933 ;; Fontify the current message if it is not already fontified.
3934 (if (text-property-any (point-min) (point-max) 'rmail-fontified nil)
3935 (let ((modified (buffer-modified-p))
3936 (buffer-undo-list t) (inhibit-read-only t)
3937 before-change-functions after-change-functions
3938 buffer-file-name buffer-file-truename)
3939 (save-excursion
3940 (save-match-data
3941 (add-text-properties (point-min) (point-max) '(rmail-fontified t))
3942 (font-lock-fontify-region (point-min) (point-max))
3943 (and (not modified) (buffer-modified-p) (set-buffer-modified-p nil)))))))
3944 \f
3945 ;;; Speedbar support for RMAIL files.
3946 (eval-when-compile (require 'speedbar))
3947
3948 (defvar rmail-speedbar-match-folder-regexp "^[A-Z0-9]+\\(\\.[A-Z0-9]+\\)?$"
3949 "*This regex is used to match folder names to be displayed in speedbar.
3950 Enabling this will permit speedbar to display your folders for easy
3951 browsing, and moving of messages.")
3952
3953 (defvar rmail-speedbar-last-user nil
3954 "The last user to be displayed in the speedbar.")
3955
3956 (defvar rmail-speedbar-key-map nil
3957 "Keymap used when in rmail display mode.")
3958
3959 (defun rmail-install-speedbar-variables ()
3960 "Install those variables used by speedbar to enhance rmail."
3961 (if rmail-speedbar-key-map
3962 nil
3963 (setq rmail-speedbar-key-map (speedbar-make-specialized-keymap))
3964
3965 (define-key rmail-speedbar-key-map "e" 'speedbar-edit-line)
3966 (define-key rmail-speedbar-key-map "r" 'speedbar-edit-line)
3967 (define-key rmail-speedbar-key-map "\C-m" 'speedbar-edit-line)
3968 (define-key rmail-speedbar-key-map "M"
3969 'rmail-speedbar-move-message-to-folder-on-line)))
3970
3971 (defvar rmail-speedbar-menu-items
3972 '(["Read Folder" speedbar-edit-line t]
3973 ["Move message to folder" rmail-speedbar-move-message-to-folder-on-line
3974 (save-excursion (beginning-of-line)
3975 (looking-at "<M> "))])
3976 "Additional menu-items to add to speedbar frame.")
3977
3978 ;; Make sure our special speedbar major mode is loaded
3979 (if (featurep 'speedbar)
3980 (rmail-install-speedbar-variables)
3981 (add-hook 'speedbar-load-hook 'rmail-install-speedbar-variables))
3982
3983 (defun rmail-speedbar-buttons (buffer)
3984 "Create buttons for BUFFER containing rmail messages.
3985 Click on the address under Reply to: to reply to this person.
3986 Under Folders: Click a name to read it, or on the <M> to move the
3987 current message into that RMAIL folder."
3988 (let ((from nil))
3989 (save-excursion
3990 (set-buffer buffer)
3991 (goto-char (point-min))
3992 (if (not (re-search-forward "^Reply-To: " nil t))
3993 (if (not (re-search-forward "^From:? " nil t))
3994 (setq from t)))
3995 (if from
3996 nil
3997 (setq from (buffer-substring (point) (save-excursion
3998 (end-of-line)
3999 (point))))))
4000 (goto-char (point-min))
4001 (if (and (looking-at "Reply to:")
4002 (equal from rmail-speedbar-last-user))
4003 nil
4004 (setq rmail-speedbar-last-user from)
4005 (erase-buffer)
4006 (insert "Reply To:\n")
4007 (if (stringp from)
4008 (speedbar-insert-button from 'speedbar-directory-face 'highlight
4009 'rmail-speedbar-button 'rmail-reply))
4010 (insert "Folders:\n")
4011 (let* ((case-fold-search nil)
4012 (df (directory-files (save-excursion (set-buffer buffer)
4013 default-directory)
4014 nil rmail-speedbar-match-folder-regexp)))
4015 (while df
4016 (speedbar-insert-button "<M>" 'speedbar-button-face 'highlight
4017 'rmail-speedbar-move-message (car df))
4018 (speedbar-insert-button (car df) 'speedbar-file-face 'highlight
4019 'rmail-speedbar-find-file nil t)
4020 (setq df (cdr df)))))))
4021
4022 (defun rmail-speedbar-button (text token indent)
4023 "Execute an rmail command specified by TEXT.
4024 The command used is TOKEN. INDENT is not used."
4025 (speedbar-with-attached-buffer
4026 (funcall token t)))
4027
4028 (defun rmail-speedbar-find-file (text token indent)
4029 "Load in the rmail file TEXT.
4030 TOKEN and INDENT are not used."
4031 (speedbar-with-attached-buffer
4032 (message "Loading in RMAIL file %s..." text)
4033 (find-file text)))
4034
4035 (defun rmail-speedbar-move-message-to-folder-on-line ()
4036 "If the current line is a folder, move current message to it."
4037 (interactive)
4038 (save-excursion
4039 (beginning-of-line)
4040 (if (re-search-forward "<M> " (save-excursion (end-of-line) (point)) t)
4041 (progn
4042 (forward-char -2)
4043 (speedbar-do-function-pointer)))))
4044
4045 (defun rmail-speedbar-move-message (text token indent)
4046 "From button TEXT, copy current message to the rmail file specified by TOKEN.
4047 TEXT and INDENT are not used."
4048 (speedbar-with-attached-buffer
4049 (message "Moving message to %s" token)
4050 (rmail-output-to-rmail-file token)))
4051
4052 ; Functions for setting, getting and encoding the POP password.
4053 ; The password is encoded to prevent it from being easily accessible
4054 ; to "prying eyes." Obviously, this encoding isn't "real security,"
4055 ; nor is it meant to be.
4056
4057 ;;;###autoload
4058 (defun rmail-set-remote-password (password)
4059 "Set PASSWORD to be used for retrieving mail from a POP or IMAP server."
4060 (interactive "sPassword: ")
4061 (if password
4062 (setq rmail-encoded-remote-password
4063 (rmail-encode-string password (emacs-pid)))
4064 (setq rmail-remote-password nil)
4065 (setq rmail-encoded-remote-password nil)))
4066
4067 (defun rmail-get-remote-password (imap)
4068 "Get the password for retrieving mail from a POP or IMAP server. If none
4069 has been set, then prompt the user for one."
4070 (when (not rmail-encoded-remote-password)
4071 (if (not rmail-remote-password)
4072 (setq rmail-remote-password
4073 (read-passwd (if imap
4074 "IMAP password: "
4075 "POP password: "))))
4076 (rmail-set-remote-password rmail-remote-password)
4077 (setq rmail-remote-password nil))
4078 (rmail-encode-string rmail-encoded-remote-password (emacs-pid)))
4079
4080 (defun rmail-have-password ()
4081 (or rmail-remote-password rmail-encoded-remote-password))
4082
4083 (defun rmail-encode-string (string mask)
4084 "Encode STRING with integer MASK, by taking the exclusive OR of the
4085 lowest byte in the mask with the first character of string, the
4086 second-lowest-byte with the second character of the string, etc.,
4087 restarting at the lowest byte of the mask whenever it runs out.
4088 Returns the encoded string. Calling the function again with an
4089 encoded string (and the same mask) will decode the string."
4090 (setq mask (abs mask)) ; doesn't work if negative
4091 (let* ((string-vector (string-to-vector string)) (i 0)
4092 (len (length string-vector)) (curmask mask) charmask)
4093 (while (< i len)
4094 (if (= curmask 0)
4095 (setq curmask mask))
4096 (setq charmask (% curmask 256))
4097 (setq curmask (lsh curmask -8))
4098 (aset string-vector i (logxor charmask (aref string-vector i)))
4099 (setq i (1+ i)))
4100 (concat string-vector)))
4101
4102 ;;;; Desktop support
4103
4104 (defun rmail-restore-desktop-buffer (desktop-buffer-file-name
4105 desktop-buffer-name
4106 desktop-buffer-misc)
4107 "Restore an rmail buffer specified in a desktop file."
4108 (condition-case error
4109 (progn
4110 (rmail-input desktop-buffer-file-name)
4111 (if (eq major-mode 'rmail-mode)
4112 (current-buffer)
4113 rmail-buffer))
4114 (file-locked
4115 (kill-buffer (current-buffer))
4116 nil)))
4117
4118 (add-to-list 'desktop-buffer-mode-handlers
4119 '(rmail-mode . rmail-restore-desktop-buffer))
4120
4121 (provide 'rmail)
4122
4123 ;;; arch-tag: cff0a950-57fe-4f73-a86e-91ff75afd06c
4124 ;;; rmail.el ends here