-;;; rmail.el --- main code of "RMAIL" mail reader for Emacs.
+;;; rmail.el --- main code of "RMAIL" mail reader for Emacs
;; Copyright (C) 1985,86,87,88,93,94,95,96,97,98,2000, 2001
;; Free Software Foundation, Inc.
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
+;;; Commentary:
+
;;; Code:
;; Souped up by shane@mit-ajax based on ideas of rlk@athena.mit.edu
;;
(require 'mail-utils)
+(eval-when-compile (require 'mule-util)) ; for detect-coding-with-priority
; These variables now declared in paths.el.
;(defvar rmail-spool-directory "/usr/spool/mail/"
:prefix "rmail-"
:group 'rmail)
+(defgroup rmail-edit nil
+ "Rmail editing."
+ :prefix "rmail-edit-"
+ :group 'rmail)
+
(defcustom rmail-movemail-program nil
"If non-nil, name of program for fetching new mail."
;;;###autoload
(defcustom rmail-dont-reply-to-names nil "\
-*A regexp specifying names to prune of reply to messages.
-A value of nil means exclude your own login name as an address
+*A regexp specifying addresses to prune from a reply message.
+A value of nil means exclude your own email address as an address
plus whatever is specified by `rmail-default-dont-reply-to-names'."
:type '(choice regexp (const :tag "Your Name" nil))
:group 'rmail-reply)
A regular expression specifying part of the value of the default value of
the variable `rmail-dont-reply-to-names', for when the user does not set
`rmail-dont-reply-to-names' explicitly. (The other part of the default
-value is the user's name.)
+value is the user's email address and name.)
It is useful to set this variable in the site customization file.")
;;;###autoload
:group 'rmail
:version "21.1"
:type '(repeat (sexp :tag "Directive")))
-
+
(defvar rmail-reply-prefix "Re: "
"String to prepend to Subject line when replying to a message.")
(other :tag "when asked" ask))
:group 'rmail)
+(defvar rmail-enable-mime-composing nil
+ "*If non-nil, RMAIL uses `rmail-insert-mime-forwarded-message-function' to forward.")
+
;;;###autoload
(defvar rmail-show-mime-function nil
- "Function to show MIME decoded message of RMAIL file.")
+ "Function to show MIME decoded message of RMAIL file.
+This function is called when `rmail-enable-mime' is non-nil.
+It is called with no argument.")
+
+;;;###autoload
+(defvar rmail-insert-mime-forwarded-message-function nil
+ "Function to insert a message in MIME format so it can be forwarded.
+This function is called if `rmail-enable-mime' or
+`rmail-enable-mime-composing' is non-nil.
+It is called with one argument FORWARD-BUFFER, which is a
+buffer containing the message to forward. The current buffer
+is the outgoing mail buffer.")
+
+;;;###autoload
+(defvar rmail-insert-mime-resent-message-function nil
+ "Function to insert a message in MIME format so it can be resent.
+This function is called if `rmail-enable-mime' is non-nil.
+It is called with one argument FORWARD-BUFFER, which is a
+buffer containing the message to forward. The current buffer
+is the outgoing mail buffer.")
+
+;;;###autoload
+(defvar rmail-search-mime-message-function nil
+ "Function to check if a regexp matches a MIME message.
+This function is called if `rmail-enable-mime' is non-nil.
+It is called with two arguments MSG and REGEXP, where
+MSG is the message number, REGEXP is the regular expression.")
+
+;;;###autoload
+(defvar rmail-search-mime-header-function nil
+ "Function to check if a regexp matches a header of MIME message.
+This function is called if `rmail-enable-mime' is non-nil.
+It is called with four arguments MSG, REGEXP, and LIMIT, where
+MSG is the message number,
+REGEXP is the regular expression,
+LIMIT is the position specifying the end of header.")
;;;###autoload
(defvar rmail-mime-feature 'rmail-mime
(let* ((cite-chars "[>|}]")
(cite-prefix "A-Za-z")
(cite-suffix (concat cite-prefix "0-9_.@-`'\"")))
- (list '("^\\(From\\|Sender\\):" . font-lock-function-name-face)
+ (list '("^\\(From\\|Sender\\|Resent-[Ff]rom\\):" . font-lock-function-name-face)
'("^Reply-To:.*$" . font-lock-function-name-face)
'("^Subject:" . font-lock-comment-face)
'("^\\(To\\|Apparently-To\\|Cc\\|Newsgroups\\):"
\f
;;;; *** Rmail Mode ***
+;; This variable is dynamically bound. The defvar is here to placate
+;; the byte compiler.
+
(defvar rmail-enable-multibyte nil)
+
+(defun rmail-require-mime-maybe ()
+ "Require `rmail-mime-feature' if that is non-nil.
+Signal an error and set `rmail-mime-feature' to nil if the feature
+isn't provided."
+ (when rmail-enable-mime
+ (condition-case err
+ (require rmail-mime-feature)
+ (error
+ (message "Feature `%s' not provided" rmail-mime-feature)
+ (sit-for 1)
+ (setq rmail-enable-mime nil)))))
+
+
;;;###autoload
(defun rmail (&optional file-name-arg)
"Read and edit incoming mail.
If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
(interactive (if current-prefix-arg
(list (read-file-name "Run rmail on RMAIL file: "))))
- (if rmail-enable-mime
- (condition-case err
- (require rmail-mime-feature)
- (error (message "Feature `%s' not provided" rmail-mime-feature)
- (sit-for 1)
- (setq rmail-enable-mime nil))))
+ (rmail-require-mime-maybe)
(let* ((file-name (expand-file-name (or file-name-arg rmail-file-name)))
- (existed (get-file-buffer file-name))
- ;; This binding is necessary because we much decide if we
+ ;; Use find-buffer-visiting, not get-file-buffer, for those users
+ ;; who have find-file-visit-truename set to t.
+ (existed (find-buffer-visiting file-name))
+ ;; This binding is necessary because we must decide if we
;; need code conversion while the buffer is unibyte
;; (i.e. enable-multibyte-characters is nil).
(rmail-enable-multibyte
- (if existed
+ (if existed
(with-current-buffer existed enable-multibyte-characters)
(default-value 'enable-multibyte-characters)))
;; Since the file may contain messages of different encodings
(eq major-mode 'rmail-mode))
(progn (rmail-forget-messages)
(rmail-set-message-counters))))
- (switch-to-buffer
+ (switch-to-buffer
(let ((enable-local-variables nil))
(find-file-noselect file-name))))
(if (eq major-mode 'rmail-edit-mode)
(setq to (point))
(unless (and coding-system
(coding-system-p coding-system))
- (setq coding-system (detect-coding-region from to t)))
+ (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))
(set-buffer-modified-p t) ; avoid locking when decoding
- (decode-coding-region from to coding-system)
+ (let ((buffer-undo-list t))
+ (decode-coding-region from to coding-system))
(setq coding-system last-coding-system-used))
(set-buffer-modified-p modifiedp)
(setq buffer-file-coding-system nil)
(define-key rmail-mode-map "\C-c\C-s\C-r" 'rmail-sort-by-recipient)
(define-key rmail-mode-map "\C-c\C-s\C-c" 'rmail-sort-by-correspondent)
(define-key rmail-mode-map "\C-c\C-s\C-l" 'rmail-sort-by-lines)
- (define-key rmail-mode-map "\C-c\C-s\C-k" 'rmail-sort-by-keywords)
+ (define-key rmail-mode-map "\C-c\C-s\C-k" 'rmail-sort-by-labels)
(define-key rmail-mode-map "\C-c\C-n" 'rmail-next-same-subject)
(define-key rmail-mode-map "\C-c\C-p" 'rmail-previous-same-subject)
)
\\[rmail-summary-by-topic] Summarize only messages with subject line regexp(s).
\\[rmail-toggle-header] Toggle display of complete header."
(interactive)
- (rmail-mode-2)
- (rmail-set-message-counters)
- (rmail-show-message rmail-total-messages)
- (run-hooks 'rmail-mode-hook))
+ (let ((finding-rmail-file (not (eq major-mode 'rmail-mode))))
+ (rmail-mode-2)
+ (when (and finding-rmail-file
+ (null coding-system-for-read)
+ default-enable-multibyte-characters)
+ (let ((rmail-enable-multibyte t))
+ (rmail-require-mime-maybe)
+ (rmail-convert-file)
+ (goto-char (point-max))
+ (set-buffer-multibyte t)))
+ (rmail-set-message-counters)
+ (rmail-show-message rmail-total-messages)
+ (when finding-rmail-file
+ (when rmail-display-summary
+ (rmail-summary))
+ (rmail-construct-io-menu))
+ (run-hooks 'rmail-mode-hook)))
(defun rmail-mode-2 ()
(kill-all-local-variables)
;; Handle M-x revert-buffer done in an rmail-mode buffer.
(defun rmail-revert (arg noconfirm)
+ (set-buffer rmail-buffer)
(let* ((revert-buffer-function (default-value 'revert-buffer-function))
(rmail-enable-multibyte enable-multibyte-characters)
;; See similar code in `rmail'.
;; If the user said "yes", and we changed something,
;; reparse the messages.
(progn
- (rmail-mode-2)
+ (set-buffer rmail-buffer)
+ (rmail-mode-2)
;; Convert all or part to Babyl file if possible.
(rmail-convert-file)
;; We have read the file as raw-text, so the buffer is set to
"Expunge and save RMAIL file."
(interactive)
(rmail-expunge)
+ (set-buffer rmail-buffer)
(save-buffer)
(if (rmail-summary-exists)
(rmail-select-summary (set-buffer-modified-p nil))))
(when rmail-summary-buffer
(replace-buffer-in-windows rmail-summary-buffer)
(bury-buffer rmail-summary-buffer))
- (let ((obuf (current-buffer)))
- (quit-window)
- (replace-buffer-in-windows obuf)))
+ (if rmail-enable-mime
+ (let ((obuf rmail-buffer)
+ (ovbuf rmail-view-buffer))
+ (set-buffer rmail-view-buffer)
+ (quit-window)
+ (replace-buffer-in-windows ovbuf)
+ (replace-buffer-in-windows obuf)
+ (bury-buffer obuf))
+ (let ((obuf (current-buffer)))
+ (quit-window)
+ (replace-buffer-in-windows obuf))))
(defun rmail-bury ()
"Bury current Rmail buffer and its summary buffer."
(if (consp item)
(progn
(setq command
- (rmail-list-to-menu (car item) (cdr item)
- action
+ (rmail-list-to-menu (car item) (cdr item)
+ action
(if full-name
(concat full-name "/"
(car item))
(setq name (car item)))
(progn
(setq name item)
- (setq command
+ (setq command
(list 'lambda () '(interactive)
(list action
- (expand-file-name
+ (expand-file-name
(if full-name
(concat full-name "/" item)
item)
(cons name command)))))
(reverse l))
menu))
-
+
;; This command is always "disabled" when it appears in a menu.
(put 'rmail-disable-menu 'menu-enable ''nil)
(if files
(progn
(define-key rmail-mode-map [menu-bar classify input-menu]
- (cons "Input Rmail File"
- (rmail-list-to-menu "Input Rmail File"
+ (cons "Input Rmail File"
+ (rmail-list-to-menu "Input Rmail File"
files
'rmail-input)))
(define-key rmail-mode-map [menu-bar classify output-menu]
- (cons "Output Rmail File"
- (rmail-list-to-menu "Output Rmail File"
+ (cons "Output Rmail File"
+ (rmail-list-to-menu "Output Rmail File"
files
'rmail-output-to-rmail-file))))
;; revert to it before we get new mail.
(or (verify-visited-file-modtime (current-buffer))
(find-file (buffer-file-name)))
+ (set-buffer rmail-buffer)
(rmail-maybe-set-message-counters)
(widen)
;; Get rid of all undo records for this buffer.
(save-excursion
(setq errors (generate-new-buffer " *rmail loss*"))
(buffer-disable-undo errors)
- (let ((args
- (append
+ (let ((args
+ (append
(list (or rmail-movemail-program
(expand-file-name "movemail"
exec-directory))
nil errors nil)
- (if rmail-preserve-inbox
+ (if rmail-preserve-inbox
(list "-p")
nil)
rmail-movemail-flags
""
(concat
"Date: \\2, \\4 \\3 \\9 \\5 "
-
+
;; The timezone could be matched by group 7 or group 10.
;; If neither of them matched, assume EST, since only
;; Easterners would be so sloppy.
(goto-char beg)
(forward-line 1)
(if (/= (following-char) ?0)
- (error "Bad format in RMAIL file."))
- (let ((buffer-read-only nil)
+ (error "Bad format in RMAIL file"))
+ (let ((inhibit-read-only t)
(delta (- (buffer-size) end)))
(delete-char 1)
(insert ?1)
(forward-line 1)
(= (following-char) ?1))))
+(defun rmail-msg-restore-non-pruned-header ()
+ (let ((old-point (point))
+ new-point
+ new-start
+ (inhibit-read-only t))
+ (save-excursion
+ (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+ (goto-char (point-min))
+ (forward-line 1)
+ ;; Change 1 to 0.
+ (delete-char 1)
+ (insert ?0)
+ ;; Insert new EOOH line at the proper place.
+ (forward-line 1)
+ (let ((case-fold-search t))
+ (while (looking-at "Summary-Line:\\|Mail-From:")
+ (forward-line 1)))
+ (insert "*** EOOH ***\n")
+ (setq new-start (point))
+ ;; Delete the old reformatted header.
+ (forward-char -1)
+ (search-forward "\n*** EOOH ***\n")
+ (forward-line -1)
+ (let ((start (point)))
+ (search-forward "\n\n")
+ (if (and (<= start old-point)
+ (<= old-point (point)))
+ (setq new-point new-start))
+ (delete-region start (point)))
+ ;; Narrow to after the new EOOH line.
+ (narrow-to-region new-start (point-max)))
+ (if new-point
+ (goto-char new-point))))
+
+(defun rmail-msg-prune-header ()
+ (let ((new-point
+ (= (point) (point-min))))
+ (save-excursion
+ (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+ (rmail-reformat-message (point-min) (point-max)))
+ (if new-point
+ (goto-char (point-min)))))
+
(defun rmail-toggle-header (&optional arg)
"Show original message header if pruned header currently shown, or vice versa.
With argument ARG, show the message header pruned if ARG is greater than zero;
otherwise, show it in full."
(interactive "P")
- (let* ((buffer-read-only nil)
- (pruned (rmail-msg-is-pruned))
+ (let* ((pruned (with-current-buffer rmail-buffer
+ (rmail-msg-is-pruned)))
(prune (if arg
(> (prefix-numeric-value arg) 0)
(not pruned))))
(if (eq pruned prune)
t
+ (set-buffer rmail-buffer)
(rmail-maybe-set-message-counters)
- (let* ((window (get-buffer-window (current-buffer)))
- (at-point-min (= (point) (point-min)))
- (all-headers-visible (= (window-start window) (point-min)))
- (on-header (save-excursion
- (and (not (search-backward "\n\n" nil t))
- (progn
- (end-of-line)
- (re-search-backward "^[-A-Za-z0-9]+:" nil t))
- (match-string 0))))
- (old-screen-line
- (rmail-count-screen-lines (window-start window) (point))))
- (save-excursion
- (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+ (if rmail-enable-mime
+ (let ((buffer-read-only nil))
+ (if pruned
+ (rmail-msg-restore-non-pruned-header)
+ (rmail-msg-prune-header))
+ (funcall rmail-show-mime-function))
+ (let* ((buffer-read-only nil)
+ (window (get-buffer-window (current-buffer)))
+ (at-point-min (= (point) (point-min)))
+ (all-headers-visible (= (window-start window) (point-min)))
+ (on-header
+ (save-excursion
+ (and (not (search-backward "\n\n" nil t))
+ (progn
+ (end-of-line)
+ (re-search-backward "^[-A-Za-z0-9]+:" nil t))
+ (match-string 0))))
+ (old-screen-line
+ (rmail-count-screen-lines (window-start window) (point))))
(if pruned
- (let (new-start)
- (goto-char (point-min))
- (forward-line 1)
- ;; Change 1 to 0.
- (delete-char 1)
- (insert ?0)
- ;; Insert new EOOH line at the proper place.
- (forward-line 1)
- (let ((case-fold-search t))
- (while (looking-at "Summary-Line:\\|Mail-From:")
- (forward-line 1)))
- (insert "*** EOOH ***\n")
- (setq new-start (point))
- ;; Delete the old reformatted header.
- (forward-char -1)
- (search-forward "\n*** EOOH ***\n")
- (forward-line -1)
- (let ((start (point)))
- (search-forward "\n\n")
- (delete-region start (point)))
- ;; Narrow to after the new EOOH line.
- (narrow-to-region new-start (point-max)))
- (rmail-reformat-message (point-min) (point-max))))
- (cond (at-point-min
- (goto-char (point-min)))
- (on-header
- (goto-char (point-min))
- (search-forward "\n\n")
- (or (re-search-backward (concat "^" (regexp-quote on-header)) nil t)
- (goto-char (point-min))))
- (t
- (save-selected-window
- (select-window window)
- (recenter old-screen-line)
- (if (and all-headers-visible
- (not (= (window-start) (point-min))))
- (let ((lines-offscreen (rmail-count-screen-lines
- (point-min)
- (window-start window))))
- (recenter (min (+ old-screen-line lines-offscreen)
- ;; last line of window
- (- (window-height) 2)))))))))
+ (rmail-msg-restore-non-pruned-header)
+ (rmail-msg-prune-header))
+ (cond (at-point-min
+ (goto-char (point-min)))
+ (on-header
+ (goto-char (point-min))
+ (search-forward "\n\n")
+ (or (re-search-backward
+ (concat "^" (regexp-quote on-header)) nil t)
+ (goto-char (point-min))))
+ (t
+ (save-selected-window
+ (select-window window)
+ (recenter old-screen-line)
+ (if (and all-headers-visible
+ (not (= (window-start) (point-min))))
+ (recenter (- (window-height) 2))))))))
(rmail-highlight-headers))))
+(defun rmail-narrow-to-non-pruned-header ()
+ "Narrow to the whole (original) header of the current message."
+ (let (start end)
+ (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+ (goto-char (point-min))
+ (forward-line 1)
+ (if (= (following-char) ?1)
+ (progn
+ (forward-line 1)
+ (setq start (point))
+ (search-forward "*** EOOH ***\n")
+ (setq end (match-beginning 0)))
+ (forward-line 2)
+ (setq start (point))
+ (search-forward "\n\n")
+ (setq end (1- (point))))
+ (narrow-to-region start end)
+ (goto-char start)))
+
;; Lifted from repos-count-screen-lines.
;; Return number of screen lines between START and END.
(defun rmail-count-screen-lines (start end)
(substring blurb (match-end 0)))))
(setq mode-line-process
(format " %d/%d%s"
- rmail-current-message rmail-total-messages blurb))))
+ rmail-current-message rmail-total-messages blurb))
+ ;; If rmail-enable-mime is non-nil, we may have to update
+ ;; `mode-line-process' of rmail-view-buffer too.
+ (if (and rmail-enable-mime
+ (not (eq (current-buffer) rmail-view-buffer))
+ (buffer-live-p rmail-view-buffer))
+ (let ((mlp mode-line-process))
+ (with-current-buffer rmail-view-buffer
+ (setq mode-line-process mlp))))))
;; Turn an attribute of a message on or off according to STATE.
;; ATTR is the name of the attribute, as a string.
;; MSGNUM is message number to change; nil means current message.
(defun rmail-set-attribute (attr state &optional msgnum)
+ (set-buffer rmail-buffer)
(let ((omax (point-max-marker))
(omin (point-min-marker))
(buffer-read-only nil))
(aset rmail-msgref-vector i (list i))
(setq i (1+ i))))
(message "Counting messages...done")))))
-
+
(defun rmail-set-message-counters-counter (&optional stop)
(let ((start (point))
next)
(goto-char (point-min))
(if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
(let ((coding-system (intern (match-string 1))))
- (check-coding-system coding-system)
- (setq buffer-file-coding-system coding-system))
+ (condition-case nil
+ (progn
+ (check-coding-system coding-system)
+ (setq buffer-file-coding-system coding-system))
+ (error
+ (setq buffer-file-coding-system nil))))
(setq buffer-file-coding-system nil)))))
;; Clear the "unseen" attribute when we show a message.
(rmail-set-attribute "unseen" nil)
(search-forward "\n*** EOOH ***\n" end t)
(narrow-to-region (point) end)))
(goto-char (point-min))
+ (walk-windows
+ (function (lambda (window)
+ (if (eq (window-buffer window) (current-buffer))
+ (set-window-point window (point)))))
+ nil t)
(rmail-display-labels)
(if (eq rmail-enable-mime t)
(funcall rmail-show-mime-function)
(let ((curr-msg rmail-current-message))
(rmail-select-summary
(rmail-summary-goto-msg curr-msg t t))))
- (rmail-auto-file)
+ (with-current-buffer rmail-buffer
+ (rmail-auto-file))
(if blurb
(message blurb))))))
(or (eq major-mode 'rmail-mode)
(switch-to-buffer rmail-buffer))
(save-excursion
- (unwind-protect
- (let ((msgbeg (rmail-msgbeg rmail-current-message))
- (msgend (rmail-msgend rmail-current-message))
- x-coding-header)
- (narrow-to-region msgbeg msgend)
- (goto-char (point-min))
- (when (search-forward "\n*** EOOH ***\n" (point-max) t)
- (narrow-to-region msgbeg (point)))
- (goto-char (point-min))
- (if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
- (let ((old-coding (intern (match-string 1)))
- (buffer-read-only nil))
- (check-coding-system old-coding)
- ;; Make sure the new coding system uses the same EOL
- ;; conversion, to prevent ^M characters from popping
- ;; up all over the place.
- (setq coding
- (coding-system-change-eol-conversion
- coding
- (coding-system-eol-type old-coding)))
- (setq x-coding-header (point-marker))
- (narrow-to-region msgbeg msgend)
- (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
- ;; to what we did.
- (goto-char x-coding-header)
- (delete-region (point)
- (save-excursion
- (beginning-of-line)
- (point)))
- (insert "X-Coding-System: "
- (symbol-name last-coding-system-used))
- (set-marker x-coding-header nil)
- (rmail-show-message))
- (error "No X-Coding-System header found")))))))
+ (let ((pruned (rmail-msg-is-pruned)))
+ (unwind-protect
+ (let ((msgbeg (rmail-msgbeg rmail-current-message))
+ (msgend (rmail-msgend rmail-current-message))
+ x-coding-header)
+ ;; We need the message headers pruned (we later restore
+ ;; the pruned stat to what it was, see the end of
+ ;; unwind-protect form).
+ (or pruned
+ (rmail-toggle-header 1))
+ (narrow-to-region msgbeg msgend)
+ (goto-char (point-min))
+ (when (search-forward "\n*** EOOH ***\n" (point-max) t)
+ (narrow-to-region msgbeg (point)))
+ (goto-char (point-min))
+ (if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
+ (let ((old-coding (intern (match-string 1)))
+ (buffer-read-only nil))
+ (check-coding-system old-coding)
+ ;; Make sure the new coding system uses the same EOL
+ ;; conversion, to prevent ^M characters from popping
+ ;; up all over the place.
+ (setq coding
+ (coding-system-change-eol-conversion
+ coding
+ (coding-system-eol-type old-coding)))
+ (setq x-coding-header (point-marker))
+ (narrow-to-region msgbeg msgend)
+ (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
+ ;; to what we did.
+ (goto-char x-coding-header)
+ (delete-region (point)
+ (save-excursion
+ (beginning-of-line)
+ (point)))
+ (insert "X-Coding-System: "
+ (symbol-name last-coding-system-used))
+ (set-marker x-coding-header nil)
+ (rmail-show-message))
+ (error "No X-Coding-System header found")))
+ (or pruned
+ (rmail-toggle-header 0)))))))
;; Find all occurrences of certain fields, and highlight them.
(defun rmail-highlight-headers ()
"Show following message whether deleted or not.
With prefix arg N, moves forward N messages, or backward if N is negative."
(interactive "p")
+ (set-buffer rmail-buffer)
(rmail-maybe-set-message-counters)
(rmail-show-message (+ rmail-current-message n)))
"Show previous message whether deleted or not.
With prefix arg N, moves backward N messages, or forward if N is negative."
(interactive "p")
- (rmail-next-message (- n)))
+ (rmail-next-message (- n)))
(defun rmail-next-undeleted-message (n)
"Show following non-deleted message.
Returns t if a new message is being shown, nil otherwise."
(interactive "p")
+ (set-buffer rmail-buffer)
(rmail-maybe-set-message-counters)
(let ((lastwin rmail-current-message)
(current rmail-current-message))
(forward-line 2)
;; If there's a Summary-line in the (otherwise empty)
;; header, we didn't yet get past the EOOH line.
- (if (looking-at "^\\*\\*\\* EOOH \\*\\*\\*\n")
- (forward-line 1))
+ (when (looking-at "^\\*\\*\\* EOOH \\*\\*\\*\n")
+ (forward-line 1))
+ (setq beg (point))
(narrow-to-region (point) end))
- (rfc822-goto-eoh)
- (search-forward "\n*** EOOH ***\n" end t))
- (narrow-to-region beg (point))
- (goto-char (point-min))
- (re-search-forward regexp end t)))))
+ (progn
+ (rfc822-goto-eoh)
+ (setq end (point)))
+ (setq beg (point))
+ (search-forward "\n*** EOOH ***\n" end t)
+ (setq end (1+ (match-beginning 0)))))
+ (goto-char beg)
+ (if rmail-enable-mime
+ (funcall rmail-search-mime-header-function n regexp end)
+ (re-search-forward regexp end t)))))
+
+(defun rmail-search-message (msg regexp)
+ "Return non-nil, if for message number MSG, regexp REGEXP matches."
+ (goto-char (rmail-msgbeg msg))
+ (if rmail-enable-mime
+ (funcall rmail-search-mime-message-function msg regexp)
+ (re-search-forward regexp (rmail-msgend msg) t)))
(defvar rmail-search-last-regexp nil)
(defun rmail-search (regexp &optional n)
(message "%sRmail search for %s..."
(if (< n 0) "Reverse " "")
regexp)
+ (set-buffer rmail-buffer)
(rmail-maybe-set-message-counters)
(let ((omin (point-min))
(omax (point-max))
;; but searching forward through each message.
(if reversep
(while (and (null win) (> msg 1))
- (goto-char (rmail-msgbeg (setq msg (1- msg))))
- (setq win (re-search-forward
- regexp (rmail-msgend msg) t)))
+ (setq msg (1- msg)
+ win (rmail-search-message msg regexp)))
(while (and (null win) (< msg rmail-total-messages))
- (goto-char (rmail-msgbeg (setq msg (1+ msg))))
- (setq win (re-search-forward regexp (rmail-msgend msg) t))))
+ (setq msg (1+ msg)
+ win (rmail-search-message msg regexp))))
(setq n (+ n (if reversep 1 -1)))))
(if win
(progn
- ;; If this is a reverse search and we found a message,
- ;; search backward thru this message to position point.
+ (rmail-show-message msg)
+ ;; Search forward (if this is a normal search) or backward
+ ;; (if this is a reverse search) through this message to
+ ;; position point. This search may fail because REGEXP
+ ;; was found in the hidden portion of this message. In
+ ;; that case, move point to the beginning of visible
+ ;; portion.
(if reversep
(progn
- (goto-char (rmail-msgend msg))
- (re-search-backward
- regexp (rmail-msgbeg msg) t)))
- (setq win (point-marker))
- (rmail-show-message msg)
+ (goto-char (point-max))
+ (re-search-backward regexp nil 'move))
+ (goto-char (point-min))
+ (re-search-forward regexp nil t))
(message "%sRmail search for %s...done"
(if reversep "Reverse " "")
- regexp)
- (goto-char win))
+ regexp))
(goto-char opoint)
(narrow-to-region omin omax)
(ding)
(defun rmail-undelete-previous-message ()
"Back up to deleted message, select it, and undelete it."
(interactive)
+ (set-buffer rmail-buffer)
(let ((msg rmail-current-message))
(while (and (> msg 0)
(not (rmail-message-deleted-p msg)))
(defun rmail-expunge-confirmed ()
"Return t if deleted message should be expunged. If necessary, ask the user.
See also user-option `rmail-confirm-expunge'."
+ (set-buffer rmail-buffer)
(or (not (stringp rmail-deleted-vector))
(not (string-match "D" rmail-deleted-vector))
(null rmail-confirm-expunge)
(defun rmail-only-expunge ()
"Actually erase all deleted messages in the file."
(interactive)
+ (set-buffer rmail-buffer)
(message "Expunging deleted messages...")
;; Discard all undo records for this buffer.
(or (eq buffer-undo-list t)
(opoint (if (and (> rmail-current-message 0)
(rmail-message-deleted-p rmail-current-message))
0
- (- (point) (point-min))))
+ (if rmail-enable-mime
+ (with-current-buffer rmail-view-buffer
+ (- (point)(point-min)))
+ (- (point) (point-min)))))
(messages-head (cons (aref rmail-message-vector 0) nil))
(messages-tail messages-head)
;; Don't make any undo records for the expunging.
(narrow-to-region (- (buffer-size) omin) (- (buffer-size) omax)))
(rmail-show-message
(if (zerop rmail-current-message) 1 nil))
- (goto-char (+ (point) opoint)))))
+ (if rmail-enable-mime
+ (goto-char (+ (point-min) opoint))
+ (goto-char (+ (point) opoint))))))
(defun rmail-expunge ()
"Erase deleted messages from Rmail file and summary buffer."
(msgnum rmail-current-message))
(save-excursion
(save-restriction
- (widen)
- (goto-char (rmail-msgbeg rmail-current-message))
- (forward-line 1)
- (if (= (following-char) ?0)
+ (if rmail-enable-mime
(narrow-to-region
- (progn (forward-line 2)
- (point))
- (progn (search-forward "\n\n" (rmail-msgend rmail-current-message)
- 'move)
- (point)))
- (narrow-to-region (point)
- (progn (search-forward "\n*** EOOH ***\n")
- (beginning-of-line) (point))))
+ (goto-char (point-min))
+ (if (search-forward "\n\n" nil 'move)
+ (1+ (match-beginning 0))
+ (point)))
+ (widen)
+ (goto-char (rmail-msgbeg rmail-current-message))
+ (forward-line 1)
+ (if (= (following-char) ?0)
+ (narrow-to-region
+ (progn (forward-line 2)
+ (point))
+ (progn (search-forward "\n\n" (rmail-msgend rmail-current-message)
+ 'move)
+ (point)))
+ (narrow-to-region (point)
+ (progn (search-forward "\n*** EOOH ***\n")
+ (beginning-of-line) (point)))))
(setq from (mail-fetch-field "from")
reply-to (or (mail-fetch-field "reply-to" nil t)
from)
(if (string= cc-list "") nil cc-list)))
rmail-view-buffer
(list (list 'rmail-mark-message
- rmail-view-buffer
- (aref rmail-msgref-vector msgnum)
+ rmail-buffer
+ (with-current-buffer rmail-buffer
+ (aref rmail-msgref-vector msgnum))
"answered"))
nil
(list (cons "References" (concat (mapconcat 'identity references " ")
(interactive "P")
(if resend
(call-interactively 'rmail-resend)
- (let ((forward-buffer (current-buffer))
+ (let ((forward-buffer rmail-buffer)
(msgnum rmail-current-message)
(subject (concat "["
(let ((from (or (mail-fetch-field "From")
nil nil subject nil nil nil
(list (list 'rmail-mark-message
forward-buffer
- (aref rmail-msgref-vector msgnum)
+ (with-current-buffer rmail-buffer
+ (aref rmail-msgref-vector msgnum))
"forwarded"))
;; If only one window, use it for the mail buffer.
;; Otherwise, use another window for the mail buffer
(save-excursion
;; Insert after header separator--before signature if any.
(goto-char (mail-text-start))
- (insert "------- Start of forwarded message -------\n")
- ;; Quote lines with `- ' if they start with `-'.
- (let ((beg (point)) end)
- (setq end (point-marker))
- (set-marker-insertion-type end t)
- (insert-buffer-substring forward-buffer)
- (goto-char beg)
- (while (re-search-forward "^-" end t)
- (beginning-of-line)
- (insert "- ")
- (forward-line 1))
- (goto-char end)
- (skip-chars-backward "\n")
- (if (< (point) end)
- (forward-char 1))
- (delete-region (point) end)
- (set-marker end nil))
- (insert "------- End of forwarded message -------\n")
+ (if (or rmail-enable-mime rmail-enable-mime-composing)
+ (funcall rmail-insert-mime-forwarded-message-function
+ forward-buffer)
+ (insert "------- Start of forwarded message -------\n")
+ ;; Quote lines with `- ' if they start with `-'.
+ (let ((beg (point)) end)
+ (setq end (point-marker))
+ (set-marker-insertion-type end t)
+ (insert-buffer-substring forward-buffer)
+ (goto-char beg)
+ (while (re-search-forward "^-" end t)
+ (beginning-of-line)
+ (insert "- ")
+ (forward-line 1))
+ (goto-char end)
+ (skip-chars-backward "\n")
+ (if (< (point) end)
+ (forward-char 1))
+ (delete-region (point) end)
+ (set-marker end nil))
+ (insert "------- End of forwarded message -------\n"))
(push-mark))))))
\f
(defun rmail-resend (address &optional from comment mail-alias-file)
(interactive "sResend to: ")
(require 'sendmail)
(require 'mailalias)
- (unless (eq rmail-buffer (current-buffer))
+ (unless (or (eq rmail-view-buffer (current-buffer))
+ (eq rmail-buffer (current-buffer)))
(error "Not an Rmail buffer"))
(if (not from) (setq from user-mail-address))
(let ((tembuf (generate-new-buffer " sendmail temp"))
(unwind-protect
(with-current-buffer tembuf
;;>> Copy message into temp buffer
- (insert-buffer-substring mailbuf)
+ (if rmail-enable-mime
+ (funcall rmail-insert-mime-resent-message-function mailbuf)
+ (insert-buffer-substring mailbuf))
(goto-char (point-min))
;; Delete any Sender field, since that's not specifiable.
; Only delete Sender fields in the actual header.
(if (and (not (vectorp mail-abbrevs))
(file-exists-p mail-personal-alias-file))
(build-mail-abbrevs))
+ (unless mail-abbrev-syntax-table
+ (mail-abbrev-make-syntax-table))
(set-syntax-table mail-abbrev-syntax-table)
(goto-char before)
(while (and (< (point) end)
(require 'mail-utils)
(let ((rmail-this-buffer (current-buffer))
(msgnum rmail-current-message)
- (pruned (rmail-msg-is-pruned))
- bounce-start bounce-end bounce-indent resending)
- (unwind-protect
- (progn
- (save-excursion
- ;; Un-prune the header; we need to search the whole thing.
- (if pruned
- (rmail-toggle-header 0))
- (goto-char (rmail-msgbeg msgnum))
- (let* ((case-fold-search t)
- (top (point))
- (content-type
- (save-restriction
- ;; Fetch any content-type header in current message
- (search-forward "\n\n") (narrow-to-region top (point))
- (mail-fetch-field "Content-Type") )) )
- ;; Handle MIME multipart bounce messages
- (if (and content-type
- (string-match
- ";[\n\t ]*boundary=\"?\\([-0-9a-z'()+_,./:=? ]+\\)\"?"
- content-type))
- (let ((codestring
- (concat "\n--"
- (substring content-type (match-beginning 1)
- (match-end 1)))))
- (unless (re-search-forward mail-mime-unsent-header nil t)
- (error "Cannot find beginning of header in failed message"))
- (unless (search-forward "\n\n" nil t)
- (error "Cannot find start of Mime data in failed message"))
- (setq bounce-start (point))
- (if (search-forward codestring nil t)
- (setq bounce-end (match-beginning 0))
- (setq bounce-end (point-max)))
- )
- ;; non-MIME bounce
- (or (re-search-forward mail-unsent-separator nil t)
- (error "Cannot parse this as a failure message"))
- (skip-chars-forward "\n")
- ;; Support a style of failure message in which the original
- ;; message is indented, and included within lines saying
- ;; `Start of returned message' and `End of returned message'.
- (if (looking-at " +Received:")
- (progn
- (setq bounce-start (point))
- (skip-chars-forward " ")
- (setq bounce-indent (- (current-column)))
- (goto-char (point-max))
- (re-search-backward "^End of returned message$" nil t)
- (setq bounce-end (point)))
- ;; One message contained a few random lines before
- ;; the old message header. The first line of the
- ;; message started with two hyphens. A blank line
- ;; followed these random lines. The same line
- ;; beginning with two hyphens was possibly marking
- ;; the end of the message.
- (if (looking-at "^--")
- (let ((boundary (buffer-substring-no-properties
- (point)
- (progn (end-of-line) (point)))))
- (search-forward "\n\n")
- (skip-chars-forward "\n")
- (setq bounce-start (point))
- (goto-char (point-max))
- (search-backward (concat "\n\n" boundary) bounce-start t)
- (setq bounce-end (point)))
- (setq bounce-start (point)
- bounce-end (point-max)))
- (unless (search-forward "\n\n" nil t)
- (error "Cannot find end of header in failed message"))
- ))))
- ;; Start sending new message; default header fields from original.
- ;; Turn off the usual actions for initializing the message body
- ;; because we want to get only the text from the failure message.
- (let (mail-signature mail-setup-hook)
- (if (rmail-start-mail nil nil nil nil nil rmail-this-buffer
- (list (list 'rmail-mark-message
- rmail-this-buffer
- (aref rmail-msgref-vector msgnum)
- "retried")))
- ;; Insert original text as initial text of new draft message.
- ;; Bind inhibit-read-only since the header delimiter
- ;; of the previous message was probably read-only.
- (let ((inhibit-read-only t)
- rmail-displayed-headers
- rmail-ignored-headers)
- (erase-buffer)
- (insert-buffer-substring rmail-this-buffer
- bounce-start bounce-end)
- (goto-char (point-min))
- (if bounce-indent
- (indent-rigidly (point-min) (point-max) bounce-indent))
- (rmail-clear-headers rmail-retry-ignored-headers)
- (rmail-clear-headers "^sender:\\|^return-path:\\|^received:")
- (mail-sendmail-delimit-header)
- (save-restriction
- (narrow-to-region (point-min) (mail-header-end))
- (setq resending (mail-fetch-field "resent-to"))
- (if mail-self-blind
- (if resending
- (insert "Resent-Bcc: " (user-login-name) "\n")
- (insert "BCC: " (user-login-name) "\n"))))
- (goto-char (point-min))
- (mail-position-on-field (if resending "Resent-To" "To") t)))))
- (with-current-buffer rmail-this-buffer
- (if pruned
- (rmail-toggle-header 1))))))
+ bounce-start bounce-end bounce-indent resending
+ ;; Fetch any content-type header in current message
+ ;; Must search thru the whole unpruned header.
+ (content-type
+ (save-excursion
+ (save-restriction
+ (rmail-narrow-to-non-pruned-header)
+ (mail-fetch-field "Content-Type") ))))
+ (save-excursion
+ (goto-char (point-min))
+ (let ((case-fold-search t))
+ (if (and content-type
+ (string-match
+ ";[\n\t ]*boundary=\"?\\([-0-9a-z'()+_,./:=? ]+\\)\"?"
+ content-type))
+ ;; Handle a MIME multipart bounce message.
+ (let ((codestring
+ (concat "\n--"
+ (substring content-type (match-beginning 1)
+ (match-end 1)))))
+ (unless (re-search-forward mail-mime-unsent-header nil t)
+ (error "Cannot find beginning of header in failed message"))
+ (unless (search-forward "\n\n" nil t)
+ (error "Cannot find start of Mime data in failed message"))
+ (setq bounce-start (point))
+ (if (search-forward codestring nil t)
+ (setq bounce-end (match-beginning 0))
+ (setq bounce-end (point-max))))
+ ;; Non-MIME bounce.
+ (or (re-search-forward mail-unsent-separator nil t)
+ (error "Cannot parse this as a failure message"))
+ (skip-chars-forward "\n")
+ ;; Support a style of failure message in which the original
+ ;; message is indented, and included within lines saying
+ ;; `Start of returned message' and `End of returned message'.
+ (if (looking-at " +Received:")
+ (progn
+ (setq bounce-start (point))
+ (skip-chars-forward " ")
+ (setq bounce-indent (- (current-column)))
+ (goto-char (point-max))
+ (re-search-backward "^End of returned message$" nil t)
+ (setq bounce-end (point)))
+ ;; One message contained a few random lines before
+ ;; the old message header. The first line of the
+ ;; message started with two hyphens. A blank line
+ ;; followed these random lines. The same line
+ ;; beginning with two hyphens was possibly marking
+ ;; the end of the message.
+ (if (looking-at "^--")
+ (let ((boundary (buffer-substring-no-properties
+ (point)
+ (progn (end-of-line) (point)))))
+ (search-forward "\n\n")
+ (skip-chars-forward "\n")
+ (setq bounce-start (point))
+ (goto-char (point-max))
+ (search-backward (concat "\n\n" boundary) bounce-start t)
+ (setq bounce-end (point)))
+ (setq bounce-start (point)
+ bounce-end (point-max)))
+ (unless (search-forward "\n\n" nil t)
+ (error "Cannot find end of header in failed message"))))))
+ ;; We have found the message that bounced, within the current message.
+ ;; Now start sending new message; default header fields from original.
+ ;; Turn off the usual actions for initializing the message body
+ ;; because we want to get only the text from the failure message.
+ (let (mail-signature mail-setup-hook)
+ (if (rmail-start-mail nil nil nil nil nil rmail-this-buffer
+ (list (list 'rmail-mark-message
+ rmail-this-buffer
+ (aref rmail-msgref-vector msgnum)
+ "retried")))
+ ;; Insert original text as initial text of new draft message.
+ ;; Bind inhibit-read-only since the header delimiter
+ ;; of the previous message was probably read-only.
+ (let ((inhibit-read-only t)
+ rmail-displayed-headers
+ rmail-ignored-headers)
+ (erase-buffer)
+ (insert-buffer-substring rmail-this-buffer
+ bounce-start bounce-end)
+ (goto-char (point-min))
+ (if bounce-indent
+ (indent-rigidly (point-min) (point-max) bounce-indent))
+ (rmail-clear-headers rmail-retry-ignored-headers)
+ (rmail-clear-headers "^sender:\\|^return-path:\\|^received:")
+ (mail-sendmail-delimit-header)
+ (save-restriction
+ (narrow-to-region (point-min) (mail-header-end))
+ (setq resending (mail-fetch-field "resent-to"))
+ (if mail-self-blind
+ (if resending
+ (insert "Resent-Bcc: " (user-login-name) "\n")
+ (insert "BCC: " (user-login-name) "\n"))))
+ (goto-char (point-min))
+ (mail-position-on-field (if resending "Resent-To" "To") t))))))
\f
(defun rmail-summary-exists ()
"Non-nil iff in an RMAIL buffer and an associated summary buffer exists.
(setq window (get-buffer-window rmail-summary-buffer))
;; Don't try to change the size if just one window in frame.
(not (eq window (frame-root-window (window-frame window))))
- (unwind-protect
+ (unwind-protect
(progn
(select-window window)
(enlarge-window (- rmail-summary-window-size (window-height))))
(defun rmail-fontify-buffer-function ()
;; This function's symbol is bound to font-lock-fontify-buffer-function.
- (make-local-hook 'rmail-show-message-hook)
(add-hook 'rmail-show-message-hook 'rmail-fontify-message nil t)
;; If we're already showing a message, fontify it now.
(if rmail-current-message (rmail-fontify-message))
"Set PASSWORD to be used for retrieving mail from a POP server."
(interactive "sPassword: ")
(if password
- (setq rmail-encoded-pop-password
+ (setq rmail-encoded-pop-password
(rmail-encode-string password (emacs-pid)))
(setq rmail-pop-password nil)
(setq rmail-encoded-pop-password nil)))
Returns the encoded string. Calling the function again with an
encoded string (and the same mask) will decode the string."
(setq mask (abs mask)) ; doesn't work if negative
- (let* ((string-vector (string-to-vector string)) (i 0)
+ (let* ((string-vector (string-to-vector string)) (i 0)
(len (length string-vector)) (curmask mask) charmask)
(while (< i len)
(if (= curmask 0)