;;; rmail.el --- main code of "RMAIL" mail reader for Emacs
;; Copyright (C) 1985, 1986, 1987, 1988, 1993, 1994, 1995, 1996, 1997, 1998,
-;; 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+;; 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: mail
(defvar rsf-beep)
(defvar rsf-sleep-after-message)
(defvar total-messages)
+(defvar tool-bar-map)
; These variables now declared in paths.el.
;(defvar rmail-spool-directory "/usr/spool/mail/"
"\\|^list-id:\\|^list-unsubscribe:\\|^list-archive:"
"\\|^content-length:\\|^nntp-posting-date:\\|^user-agent"
"\\|^importance:\\|^envelope-to:\\|^delivery-date\\|^openpgp:"
- "\\|^mbox-line:\\|^cancel-lock:"
+ "\\|^mbox-line:\\|^cancel-lock:\\|^DomainKey-Signature:"
"\\|^resent-face:\\|^resent-x.*:\\|^resent-organization:\\|^resent-openpgp:"
"\\|^x-.*:")
:type 'regexp
:group 'rmail-headers)
+(defface rmail-highlight
+ '((t :default highlight))
+ "Face to use for highlighting the most important header fields."
+ :group 'rmail-headers
+ :version "22.1")
+
;;;###autoload
-(defcustom rmail-highlight-face nil "\
+(defcustom rmail-highlight-face 'rmail-highlight "\
*Face used by Rmail for highlighting headers."
:type '(choice (const :tag "Default" nil)
face)
Called with region narrowed to the message, including headers,
before obeying `rmail-ignored-headers'."
:group 'rmail-headers
- :type 'function)
+ :type '(choice (const nil) function))
(defcustom rmail-automatic-folder-directives nil
"List of directives specifying where to put a message.
;; Some mailers use "Re(2):" or "Re^2:" or "Re: Re:" or "Re[2]:".
;; This pattern should catch all the common variants.
+;; rms: I deleted the change to delete tags in square brackets
+;; because they mess up RT tags.
(defvar rmail-reply-regexp "\\`\\(Re\\(([0-9]+)\\|\\[[0-9]+\\]\\|\\^[0-9]+\\)?: *\\)*"
"Regexp to delete from Subject line before inserting `rmail-reply-prefix'.")
"*If non-nil, RMAIL uses MIME feature.
If the value is t, RMAIL automatically shows MIME decoded message.
If the value is neither t nor nil, RMAIL does not show MIME decoded message
-until a user explicitly requires it."
+until a user explicitly requires it.
+
+Even if the value is non-nil, you can't use MIME feature
+if the feature specified by `rmail-mime-feature' is not available
+in your session."
:type '(choice (const :tag "on" t)
(const :tag "off" nil)
(other :tag "when asked" ask))
(defvar rmail-mime-feature 'rmail-mime
"Feature to require to load MIME support in Rmail.
When starting Rmail, if `rmail-enable-mime' is non-nil,
-this feature is required with `require'.")
+this feature is required with `require'.
+
+The default value is `rmail-mime'. This feature is provided by
+the rmail-mime package available at <http://www.m17n.org/rmail-mime/>.")
;;;###autoload
(defvar rmail-decode-mime-charset t
;;;###autoload
(defvar rmail-mime-charset-pattern
- "^content-type:[ ]*text/plain;[ \t\n]*charset=\"?\\([^ \t\n\";]+\\)\"?"
+ (concat "^content-type:[ \t]*text/plain;"
+ "\\(?:[ \t\n]*\\(?:format\\|delsp\\)=\"?[-a-z0-9]+\"?;\\)*"
+ "[ \t\n]*charset=\"?\\([^ \t\n\";]+\\)\"?")
"Regexp to match MIME-charset specification in a header of message.
The first parenthesized expression should match the MIME-charset name.")
(condition-case err
(require rmail-mime-feature)
(error
- (message "Feature `%s' not provided" rmail-mime-feature)
- (sit-for 1)
+ (display-warning
+ :warning
+ (format "Although MIME support is requested
+by setting `rmail-enable-mime' to non-nil, the required feature
+`%s' (the value of `rmail-mime-feature')
+is not available in the current session.
+So, the MIME support is turned off for the moment."
+ rmail-mime-feature))
(setq rmail-enable-mime nil)))))
(unless (and coding-system
(coding-system-p coding-system))
(setq coding-system
- ;; Emacs 21.1 and later writes RMAIL files in emacs-mule, but
- ;; earlier versions did that with the current buffer's encoding.
- ;; So we want to favor detection of emacs-mule (whose normal
- ;; priority is quite low), but still allow detection of other
- ;; encodings if emacs-mule won't fit. The call to
- ;; detect-coding-with-priority below achieves that.
- (car (detect-coding-with-priority
- from to
- '((coding-category-emacs-mule . emacs-mule))))))
- (unless (memq coding-system
- '(undecided undecided-unix))
+ ;; If rmail-file-coding-system is nil, Emacs 21 writes
+ ;; RMAIL files in emacs-mule, Emacs 22 in utf-8, but
+ ;; earlier versions did that with the current buffer's
+ ;; encoding. So we want to favor detection of emacs-mule
+ ;; (whose normal priority is quite low) and utf-8, but
+ ;; still allow detection of other encodings if they won't
+ ;; fit. The call to with-coding-priority below achieves
+ ;; that.
+ (with-coding-priority '(emacs-mule utf-8)
+ (detect-coding-region from to 'highest))))
+ (unless (eq (coding-system-type coding-system) 'undecided)
(set-buffer-modified-p t) ; avoid locking when decoding
(let ((buffer-undo-list t))
(decode-coding-region from to coding-system))
(define-key rmail-mode-map [menu-bar move next]
'("Next" . rmail-next-message))
+
+;; Rmail toolbar
+(defvar rmail-tool-bar-map
+ (if (display-graphic-p)
+ (let ((map (make-sparse-keymap)))
+ (tool-bar-local-item-from-menu 'rmail-get-new-mail "mail/inbox"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-next-undeleted-message "right-arrow"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-previous-undeleted-message "left-arrow"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-search "search"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-input "open"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-mail "mail/compose"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-reply "mail/reply-all"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-forward "mail/forward"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-delete-forward "close"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-output "mail/move"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-output-body-to-file "mail/save"
+ map rmail-mode-map)
+ (tool-bar-local-item-from-menu 'rmail-expunge "delete"
+ map rmail-mode-map)
+ map)))
+
+
\f
;; Rmail mode is suitable only for specially formatted data.
(put 'rmail-mode 'mode-class 'special)
(concat rmail-spool-directory
(user-login-name)))))))
(make-local-variable 'rmail-keywords)
+ (set (make-local-variable 'tool-bar-map) rmail-tool-bar-map)
;; this gets generated as needed
(setq rmail-keywords nil))
(if (and (featurep 'rmail-spam-filter)
rmail-use-spam-filter
(> rsf-number-of-spam 0))
- (if (= 1 new-messages)
- ", and found to be a spam message"
- (if (> rsf-number-of-spam 1)
- (format ", %d of which found to be spam messages"
- rsf-number-of-spam)
- ", one of which found to be a spam message"))
+ (cond ((= 1 new-messages)
+ ", and appears to be spam")
+ ((= rsf-number-of-spam new-messages)
+ ", and all appear to be spam")
+ ((> rsf-number-of-spam 1)
+ (format ", and %d appear to be spam"
+ rsf-number-of-spam))
+ (t
+ ", and 1 appears to be spam"))
""))
(if (and (featurep 'rmail-spam-filter)
rmail-use-spam-filter
(defun rmail-convert-to-babyl-format ()
(let ((count 0) start
(case-fold-search nil)
+ (buffer-undo-list t)
(invalid-input-resync
(function (lambda ()
(message "Invalid Babyl format in inbox!")
;; may still be in use. -- rms, 7 May 1993.
((eolp) (delete-char 1))
(t (error "Cannot convert to babyl format")))))
+ (setq buffer-undo-list nil)
count))
;; Delete the "From ..." line, creating various other headers with
(if blurb
(message blurb))))))
-(defun rmail-redecode-body (coding)
+(defun rmail-redecode-body (coding &optional raw)
"Decode the body of the current message using coding system CODING.
This is useful with mail messages that have malformed or missing
charset= headers.
decode it was incorrect. It then encodes the message back to its
original form, and decodes it again, using the coding system CODING.
+Optional argument RAW, if non-nil, means don't encode the message
+before decoding it with the new CODING. This is useful if the current
+message text was produced by some function which invokes `insert',
+since `insert' leaves unibyte character codes 128 through 255 unconverted
+to multibyte. One example of such a situation is when the text was
+produced by `base64-decode-region'.
+
+Interactively, invoke the function with a prefix argument to set RAW
+non-nil.
+
Note that if Emacs erroneously auto-detected one of the iso-2022
encodings in the message, this function might fail because the escape
sequences that switch between character sets and also single-shift and
(or (eq major-mode 'rmail-mode)
(switch-to-buffer rmail-buffer))
(save-excursion
- (let ((pruned (rmail-msg-is-pruned)))
+ (let ((pruned (rmail-msg-is-pruned))
+ (raw (or raw current-prefix-arg)))
(unwind-protect
(let ((msgbeg (rmail-msgbeg rmail-current-message))
(msgend (rmail-msgend rmail-current-message))
(coding-system-change-eol-conversion
coding
(coding-system-eol-type old-coding)))
+ ;; If old-coding is `undecided', encode-coding-region
+ ;; will not encode the text at all. Find a proper
+ ;; non-trivial encoding to use.
+ (if (memq (coding-system-base old-coding) '(nil undecided))
+ (setq old-coding
+ (car (find-coding-systems-region msgbeg msgend))))
(setq x-coding-header (point-marker))
(narrow-to-region msgbeg msgend)
- (encode-coding-region (point) msgend old-coding)
+ (and (null raw)
+ ;; If old and new encoding are the same, it
+ ;; clearly doesn't make sense to encode.
+ (not (coding-system-equal
+ (coding-system-base old-coding)
+ (coding-system-base coding)))
+ ;; If the body includes only eight-bit-*
+ ;; characters, encoding might fail, e.g. with
+ ;; UTF-8, and isn't needed anyway.
+ (> (length (delq 'ascii
+ (delq 'eight-bit-graphic
+ (delq 'eight-bit-control
+ (find-charset-region
+ msgbeg msgend)))))
+ 0)
+ (encode-coding-region (point) msgend old-coding))
(decode-coding-region (point) msgend coding)
(setq last-coding-system-used coding)
;; Rewrite the coding-system header according
;; (rmail-show-message found))
found))
-(defun rmail-next-same-subject (n)
- "Go to the next mail message having the same subject header.
-With prefix argument N, do this N times.
-If N is negative, go backwards instead."
- (interactive "p")
- (let ((subject (mail-fetch-field "Subject"))
- (forward (> n 0))
- (i rmail-current-message)
- (case-fold-search t)
- search-regexp found)
+(defun rmail-current-subject ()
+ "Return the current subject.
+The subject is stripped of leading and trailing whitespace, and
+of typical reply prefixes such as Re:."
+ (let ((subject (or (mail-fetch-field "Subject") "")))
(if (string-match "\\`[ \t]+" subject)
(setq subject (substring subject (match-end 0))))
- (if (string-match "\\`\\(Re:[ \t]*\\)+" subject)
+ (if (string-match rmail-reply-regexp subject)
(setq subject (substring subject (match-end 0))))
(if (string-match "[ \t]+\\'" subject)
(setq subject (substring subject 0 (match-beginning 0))))
+ subject))
+
+(defun rmail-current-subject-regexp ()
+ "Return a regular expression matching the current subject.
+The regular expression matches the subject header line of
+messages about the same subject. The subject itself is stripped
+of leading and trailing whitespace, of typical reply prefixes
+such as Re: and whitespace within the subject is replaced by a
+regular expression matching whitespace in general in order to
+take into account that subject header lines may include newlines
+and more whitespace. The returned regular expressions contains
+`rmail-reply-regexp' and ends with a newline."
+ (let ((subject (rmail-current-subject)))
;; If Subject is long, mailers will break it into several lines at
;; arbitrary places, so replace whitespace with a regexp that will
;; match any sequence of spaces, TABs, and newlines.
(setq subject (regexp-quote subject))
(setq subject
(replace-regexp-in-string "[ \t\n]+" "[ \t\n]+" subject t t))
- (setq search-regexp (concat "^Subject: *\\(Re:[ \t]*\\)*"
- subject "[ \t]*\n"))
+ (concat "^Subject: "
+ (if (string= "\\`" (substring rmail-reply-regexp 0 2))
+ (substring rmail-reply-regexp 2)
+ rmail-reply-regexp)
+ subject "[ \t]*\n")))
+
+(defun rmail-next-same-subject (n)
+ "Go to the next mail message having the same subject header.
+With prefix argument N, do this N times.
+If N is negative, go backwards instead."
+ (interactive "p")
+ (let ((search-regexp (rmail-current-subject-regexp))
+ (forward (> n 0))
+ (i rmail-current-message)
+ (case-fold-search t)
+ found)
(save-excursion
(save-restriction
(widen)