]> code.delx.au - gnu-emacs/blobdiff - lisp/mail/rmail.el
(report-emacs-bug-text-prompt): New variable.
[gnu-emacs] / lisp / mail / rmail.el
index 32d787e737006a4c733056474a455b7128127b10..e6dd92671e170ff993370f9823d90dafec629eac 100644 (file)
@@ -1,6 +1,6 @@
-;;; 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,1998
+;; Copyright (C) 1985,86,87,88,93,94,95,96,97,98,2000, 2001
 ;;             Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
@@ -23,6 +23,8 @@
 ;; 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
@@ -38,6 +40,7 @@
 ;;
 
 (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."
   :group 'rmail-retrieve
-  :type 'string)
+  :type '(choice (const nil) string))
 
 (defcustom rmail-pop-password nil
   "*Password to use when reading mail from a POP server, if required."
@@ -108,11 +116,14 @@ or `-k' to enable Kerberos authentication."
   :group 'rmail-retrieve
   :version "20.3")
 
-(defvar rmail-pop-password-error "invalid usercode or password"
+(defvar rmail-pop-password-error "invalid usercode or password\\|
+unknown user name or bad password"
   "Regular expression matching incorrect-password POP server error messages.
 If you get an incorrect-password error that this expression does not match,
 please report it with \\[report-emacs-bug].")
 
+(defvar rmail-encoded-pop-password nil)
+
 (defcustom rmail-preserve-inbox nil
   "*Non-nil if incoming mail should be left in the user's inbox,
 rather than deleted, after it is retrieved."
@@ -121,8 +132,9 @@ rather than deleted, after it is retrieved."
 
 ;;;###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 name only."
+*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)
 
@@ -131,12 +143,32 @@ A value of nil means exclude your own name only."
 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
-(defcustom rmail-ignored-headers "^via:\\|^mail-from:\\|^origin:\\|^references:\\|^status:\\|^received:\\|^x400-originator:\\|^x400-recipients:\\|^x400-received:\\|^x400-mts-identifier:\\|^x400-content-type:\\|^\\(resent-\\|\\)message-id:\\|^summary-line:\\|^resent-date:\\|^nntp-posting-host:\\|^path:\\|^x-char.*:\\|^x-face:\\|^x-mailer:\\|^delivered-to:\\|^lines:\\|^mime-version:\\|^content-transfer-encoding:\\|^x-coding-system:\\|^return-path:\\|^errors-to:\\|^return-receipt-to:\\|^x-attribution:\\|^x-disclaimer:"
-  "*Regexp to match header fields that Rmail should normally hide."
+(defcustom rmail-ignored-headers
+  (concat "^via:\\|^mail-from:\\|^origin:\\|^references:"
+         "\\|^status:\\|^received:\\|^x400-originator:\\|^x400-recipients:"
+         "\\|^x400-received:\\|^x400-mts-identifier:\\|^x400-content-type:"
+         "\\|^\\(resent-\\|\\)message-id:\\|^summary-line:\\|^resent-date:"
+         "\\|^nntp-posting-host:\\|^path:\\|^x-char.*:\\|^x-face:"
+         "\\|^x-mailer:\\|^delivered-to:\\|^lines:\\|^mime-version:"
+         "\\|^content-transfer-encoding:\\|^x-coding-system:"
+         "\\|^return-path:\\|^errors-to:\\|^return-receipt-to:"
+         "\\|^x-sign:\\|^x-beenthere:\\|^x-mailman-version:"
+         "\\|^precedence:\\|^list-help:\\|^list-post:\\|^list-subscribe:"
+         "\\|^list-id:\\|^list-unsubscribe:\\|^list-archive:"
+         "\\|^content-type:\\|^content-length:"
+         "\\|^x-attribution:\\|^x-disclaimer:\\|^x-trace:"
+         "\\|^x-complaints-to:\\|^nntp-posting-date:\\|^user-agent:")
+  "*Regexp to match header fields that Rmail should normally hide.
+This variable is used for reformatting the message header,
+which normally happens once for each message,
+when you view the message for the first time in Rmail.
+To make a change in this variable take effect
+for a message that you have already viewed,
+go to that message and type \\[rmail-toggle-header] twice."
   :type 'regexp
   :group 'rmail-headers)
 
@@ -149,7 +181,7 @@ If nil, display all header fields except those matched by
   :group 'rmail-headers)
 
 ;;;###autoload
-(defcustom rmail-retry-ignored-headers nil "\
+(defcustom rmail-retry-ignored-headers "^x-authentication-warning:" "\
 *Headers that should be stripped when retrying a failed message."
   :type '(choice regexp (const nil :tag "None"))
   :group 'rmail-headers)
@@ -184,9 +216,9 @@ and the value of the environment variable MAIL overrides it)."
   ;; Don't use backquote here, because we don't want to need it
   ;; at load time.
   :type (list 'choice '(const :tag "Default" nil)
-             (list 'repeat ':value (or (getenv "MAIL")
-                                       (concat "/var/spool/mail/"
-                                               (getenv "USER")))
+             (list 'repeat ':value (list (or (getenv "MAIL")
+                                             (concat "/var/spool/mail/"
+                                                     (getenv "USER"))))
                    'file))
   :group 'rmail-retrieve
   :group 'rmail-files)
@@ -208,6 +240,15 @@ and the value of the environment variable MAIL overrides it)."
   :type 'regexp
   :group 'rmail-files)
 
+;;;###autoload
+(defcustom rmail-confirm-expunge 'y-or-n-p
+  "*Whether and how to ask for confirmation before expunging deleted messages."
+  :type '(choice (const :tag "No confirmation" nil)
+                (const :tag "Confirm with y-or-n-p" y-or-n-p)
+                (const :tag "Confirm with yes-or-no-p" yes-or-no-p))
+  :version "21.1"
+  :group 'rmail-files)
+
 ;;;###autoload
 (defvar rmail-mode-hook nil
   "List of functions to call when Rmail is invoked.")
@@ -217,8 +258,15 @@ and the value of the environment variable MAIL overrides it)."
   "List of functions to call when Rmail has retrieved new mail.")
 
 ;;;###autoload
-(defvar rmail-show-message-hook nil
-  "List of functions to call when Rmail displays a message.")
+(defcustom rmail-show-message-hook nil
+  "List of functions to call when Rmail displays a message."
+  :type 'hook
+  :options '(goto-addr)
+  :group 'rmail)
+
+;;;###autoload
+(defvar rmail-quit-hook nil
+  "List of functions to call when quitting out of Rmail.")
 
 ;;;###autoload
 (defvar rmail-delete-message-hook nil
@@ -242,6 +290,32 @@ before obeying `rmail-ignored-headers'."
   :group 'rmail-headers
   :type 'function)
 
+(defcustom rmail-automatic-folder-directives nil
+  "List of directives specifying where to put a message.
+Each element of the list is of the form:
+
+  (FOLDERNAME FIELD REGEXP [ FIELD REGEXP ] ... )
+
+Where FOLDERNAME is the name of a BABYL format folder to put the
+message.  If any of the field regexp's are nil, then it is ignored.
+
+If FOLDERNAME is \"/dev/null\", it is deleted.
+If FOLDERNAME is nil then it is deleted, and skipped.
+
+FIELD is the plain text name of a field in the message, such as
+\"subject\" or \"from\".  A FIELD of \"to\" will automatically include
+all text from the \"cc\" field as well.
+
+REGEXP is an expression to match in the preceeding specified FIELD.
+FIELD/REGEXP pairs continue in the list.
+
+examples:
+  (\"/dev/null\" \"from\" \"@spam.com\") ; delete all mail from spam.com
+  (\"RMS\" \"from\" \"rms@\") ; save all mail from RMS."
+  :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.")
 
@@ -321,6 +395,11 @@ by substituting the new message number into the existing list.")
   "*Default file name for \\[rmail-output-to-rmail-file]."
   :type 'file
   :group 'rmail-files)
+(defcustom rmail-default-body-file "~/mailout"
+  "*Default file name for \\[rmail-output-body-to-file]."
+  :type 'file
+  :group 'rmail-files
+  :version "20.3")
 
 ;; Mule and MIME related variables.
 
@@ -338,18 +417,72 @@ If the value is neither t nor nil, RMAIL does not show MIME decoded message
 until a user explicitly requires it."
   :type '(choice (const :tag "on" t)
                 (const :tag "off" nil)
-                (sexp :tag "when asked" :format "%t\n" ask))
+                (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
-  "Feature to provide for using MIME feature in RMAIL.
-When starting rmail, this feature is requrired if rmail-enable-mime
-is non-nil.")
+  "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'.")
+
+;;;###autoload
+(defvar rmail-decode-mime-charset t
+  "*Non-nil means a message is decoded by MIME's charset specification.
+If this variable is nil, or the message has not MIME specification,
+the message is decoded as normal way.
+
+If the variable `rmail-enable-mime' is non-nil, this variables is
+ignored, and all the decoding work is done by a feature specified by
+the variable `rmail-mime-feature'.")
+
+;;;###autoload
+(defvar rmail-mime-charset-pattern
+  "^content-type:[ ]*text/plain;[ \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.")
 
 \f
 ;;; Regexp matching the delimiter of messages in UNIX mail format
@@ -413,7 +546,7 @@ is non-nil.")
     (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\\):"
@@ -434,30 +567,47 @@ is non-nil.")
 ;; Perform BODY in the summary buffer
 ;; in such a way that its cursor is properly updated in its own window.
 (defmacro rmail-select-summary (&rest body)
-  (` (let ((total rmail-total-messages))
-       (if (rmail-summary-displayed)
-          (let ((window (selected-window)))
-            (save-excursion
-              (unwind-protect
-                  (progn
-                    (pop-to-buffer rmail-summary-buffer)
-                    ;; rmail-total-messages is a buffer-local var
-                    ;; in the rmail buffer.
-                    ;; This way we make it available for the body
-                    ;; even tho the rmail buffer is not current.
-                    (let ((rmail-total-messages total))
-                      (,@ body)))
-                (select-window window))))
-        (save-excursion
-          (set-buffer rmail-summary-buffer)
-          (let ((rmail-total-messages total))
-            (,@ body))))
-       (rmail-maybe-display-summary))))
+  `(let ((total rmail-total-messages))
+     (if (rmail-summary-displayed)
+        (let ((window (selected-window)))
+          (save-excursion
+            (unwind-protect
+                (progn
+                  (pop-to-buffer rmail-summary-buffer)
+                  ;; rmail-total-messages is a buffer-local var
+                  ;; in the rmail buffer.
+                  ;; This way we make it available for the body
+                  ;; even tho the rmail buffer is not current.
+                  (let ((rmail-total-messages total))
+                    ,@body))
+              (select-window window))))
+       (save-excursion
+        (set-buffer rmail-summary-buffer)
+        (let ((rmail-total-messages total))
+          ,@body)))
+     (rmail-maybe-display-summary)))
 \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.
@@ -473,18 +623,18 @@ have a chance to specify a file name with the minibuffer.
 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 (default-value 'enable-multibyte-characters))
+         (rmail-enable-multibyte
+          (if existed
+             (with-current-buffer existed enable-multibyte-characters)
+            (default-value 'enable-multibyte-characters)))
         ;; Since the file may contain messages of different encodings
         ;; at the tail (non-BYBYL part), we can't decode them at once
         ;; on reading.  So, at first, we read the file without text
@@ -507,7 +657,9 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
                   (eq major-mode 'rmail-mode))
              (progn (rmail-forget-messages)
                     (rmail-set-message-counters))))
-      (find-file file-name))
+      (switch-to-buffer
+       (let ((enable-local-variables nil))
+        (find-file-noselect file-name))))
     (if (eq major-mode 'rmail-edit-mode)
        (error "Exit Rmail Edit mode before getting new mail"))
     (if (and existed (> (buffer-size) 0))
@@ -519,21 +671,18 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
       (rmail-mode-2)
       ;; Convert all or part to Babyl file if possible.
       (rmail-convert-file)
-      ;; As we have read a file by raw-text, the buffer is set to
-      ;; unibyte.  We must make it multibyte if necessary.
-      (if (and rmail-enable-multibyte
-              (not enable-multibyte-characters))
-         (set-buffer-multibyte t))
-      (goto-char (point-max))
-      (if (null rmail-inbox-list)
-         (progn
-           (rmail-set-message-counters)
-           (rmail-show-message)
-           (setq msg-shown t))))
+      (goto-char (point-max)))
+    ;; As we have read a file by raw-text, the buffer is set to
+    ;; unibyte.  We must make it multibyte if necessary.
+    (if (and rmail-enable-multibyte
+            (not enable-multibyte-characters))
+       (set-buffer-multibyte t))
+    ;; If necessary, scan to find all the messages.
+    (rmail-maybe-set-message-counters)
     (unwind-protect
-       (or (and (null file-name-arg)
-                (rmail-get-new-mail))
-           (or msg-shown (rmail-show-message (rmail-first-unseen-message))))
+       (unless (and (not file-name-arg)
+                    (rmail-get-new-mail))
+         (rmail-show-message (rmail-first-unseen-message)))
       (progn
        (if rmail-display-summary (rmail-summary))
        (rmail-construct-io-menu)
@@ -630,9 +779,21 @@ Note:    it means the file has no messages in it.\n\^_")))
     (setq to (point))
     (unless (and coding-system
                 (coding-system-p coding-system))
-      (setq coding-system (detect-coding-region from to t)))
-    (unless (eq coding-system 'undecided)
-      (decode-coding-region from to 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))
+      (set-buffer-modified-p t)                ; avoid locking when decoding
+      (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)
@@ -694,7 +855,7 @@ Note:    it means the file has no messages in it.\n\^_")))
   (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)
   )
@@ -871,10 +1032,23 @@ Instead, these commands are available:
 \\[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)
@@ -940,8 +1114,8 @@ Instead, these commands are available:
     (setq save-buffer-coding-system (or buffer-file-coding-system 'undecided))
     (setq buffer-file-coding-system nil))
   ;; Don't let a local variables list in a message cause confusion.
-  (make-local-variable 'enable-local-variables)
-  (setq enable-local-variables nil)
+  (make-local-variable 'local-enable-local-variables)
+  (setq local-enable-local-variables nil)
   (make-local-variable 'revert-buffer-function)
   (setq revert-buffer-function 'rmail-revert)
   (make-local-variable 'font-lock-defaults)
@@ -963,15 +1137,29 @@ Instead, these commands are available:
 
 ;; Handle M-x revert-buffer done in an rmail-mode buffer.
 (defun rmail-revert (arg noconfirm)
-  (let ((revert-buffer-function (default-value 'revert-buffer-function)))
+  (set-buffer rmail-buffer)
+  (let* ((revert-buffer-function (default-value 'revert-buffer-function))
+        (rmail-enable-multibyte enable-multibyte-characters)
+        ;; See similar code in `rmail'.
+        (coding-system-for-read (and rmail-enable-multibyte 'raw-text)))
     ;; Call our caller again, but this time it does the default thing.
     (if (revert-buffer arg noconfirm)
        ;; If the user said "yes", and we changed something,
        ;; reparse the messages.
        (progn
+         (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
+         ;; unibyte.  Make it multibyte if necessary.
+         (if (and rmail-enable-multibyte
+                  (not enable-multibyte-characters))
+             (set-buffer-multibyte t))
          (goto-char (point-max))
-         (rmail-mode)))))
+         (rmail-set-message-counters)
+         (rmail-show-message rmail-total-messages)
+         (run-hooks 'rmail-mode-hook)))))
 
 ;; Return a list of files from this buffer's Mail: option.
 ;; Does not assume that messages have been parsed.
@@ -995,21 +1183,33 @@ Instead, these commands are available:
   "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))))
 
 (defun rmail-quit ()
-  "Quit out of RMAIL."
+  "Quit out of RMAIL.
+Hook `rmail-quit-hook' is run after expunging."
   (interactive)
   (rmail-expunge-and-save)
+  (when (boundp 'rmail-quit-hook)
+    (run-hooks 'rmail-quit-hook))
   ;; Don't switch to the summary buffer even if it was recently visible.
   (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."
@@ -1069,8 +1269,8 @@ original copy."
                   (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))
@@ -1078,10 +1278,10 @@ original copy."
                         (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)
@@ -1090,7 +1290,7 @@ original copy."
                     (cons name command)))))
      (reverse l))
     menu))
+
 ;; This command is always "disabled" when it appears in a menu.
 (put 'rmail-disable-menu 'menu-enable ''nil)
 
@@ -1099,13 +1299,13 @@ original copy."
     (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))))
 
@@ -1145,6 +1345,7 @@ It returns t if it got any new messages."
   ;; 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.
@@ -1203,14 +1404,14 @@ It returns t if it got any new messages."
                      (save-excursion
                        (setq new-messages (rmail-convert-to-babyl-format)
                              success t))
+                   ;; Try to delete the garbage just inserted.
+                   (or success (delete-region (point-min) (point-max)))
                    ;; If we could not convert the file's inboxes,
                    ;; rename the files we tried to read
                    ;; so we won't over and over again.
                    (if (and (not file-name) (not success))
                        (let ((delfiles delete-files)
                              (count 0))
-                         ;; Try to delete the garbage just inserted.
-                         (delete-region (point-min) (point-max))
                          (while delfiles
                            (while (file-exists-p (format "RMAILOSE.%d" count))
                              (setq count (1+ count)))
@@ -1261,11 +1462,17 @@ It returns t if it got any new messages."
   (or (memq (file-locked-p buffer-file-name) '(nil t))
       (error "RMAIL file %s is locked"
             (file-name-nondirectory buffer-file-name)))
-  (let (file tofile delete-files movemail popmail got-password)
+  (let (file tofile delete-files movemail popmail got-password password)
     (while files
-      (setq file (file-truename
-                 (expand-file-name (substitute-in-file-name (car files))))
-           tofile (expand-file-name
+      ;; Handle POP mailbox names specially; don't expand as filenames
+      ;; in case the userid contains a directory separator.
+      (setq file (car files))
+      (setq popmail (string-match "^po:" file))
+      (if popmail
+         (setq renamep t)
+       (setq file (file-truename
+                   (substitute-in-file-name (expand-file-name file)))))
+      (setq tofile (expand-file-name
                    ;; Generate name to move to from inbox name,
                    ;; in case of multiple inboxes that need moving.
                    (concat ".newmail-" (file-name-nondirectory file))
@@ -1285,10 +1492,7 @@ It returns t if it got any new messages."
 ;;;                          (file-truename
 ;;;                           (concat rmail-spool-directory
 ;;;                                   (file-name-nondirectory file)))))
-      (setq popmail (string-match "^po:" (file-name-nondirectory file)))
-      (if popmail (setq file (file-name-nondirectory file)
-                       renamep t))
-      (if movemail
+      (if (and movemail (not popmail))
          (progn
            ;; On some systems, /usr/spool/mail/foo is a directory
            ;; and the actual inbox is /usr/spool/mail/foo/foo.
@@ -1296,17 +1500,15 @@ It returns t if it got any new messages."
                (setq file (expand-file-name (user-login-name)
                                             file)))))
       (cond (popmail
-            (if (and rmail-pop-password-required (not rmail-pop-password))
-                (setq rmail-pop-password
-                      (rmail-read-passwd
-                       (format "Password for %s: "
-                               (substring file (+ popmail 3))))
-                      got-password t))
+            (if rmail-pop-password-required
+                (progn (setq got-password (not (rmail-have-password)))
+                       (setq password (rmail-get-pop-password))))
             (if (eq system-type 'windows-nt)
                 ;; cannot have "po:" in file name
                 (setq tofile
                       (expand-file-name
-                       (concat ".newmail-pop-" (substring file (+ popmail 3)))
+                       (concat ".newmail-pop-"
+                               (file-name-nondirectory (substring file 3)))
                        (file-name-directory
                         (expand-file-name buffer-file-name)))))
             (message "Getting mail from post office ..."))
@@ -1346,20 +1548,18 @@ It returns t if it got any new messages."
                   (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
                             (list file tofile)
-                            (if rmail-pop-password 
-                                (list rmail-pop-password)
-                              nil))))
+                            (if password (list password) nil))))
                       (apply 'call-process args))
                     (if (not (buffer-modified-p errors))
                         ;; No output => movemail won
@@ -1383,7 +1583,7 @@ It returns t if it got any new messages."
                       (if (or got-password
                               (re-search-forward rmail-pop-password-error
                                                  nil t))
-                          (setq rmail-pop-password nil))
+                          (rmail-set-pop-password nil))
                       (sit-for 3)
                       nil))
                 (if errors (kill-buffer errors))))))
@@ -1405,28 +1605,12 @@ It returns t if it got any new messages."
       (setq files (cdr files)))
     delete-files))
 
-(defun rmail-read-passwd (prompt &optional default)
-  "Read a password, echoing `.' for each character typed.
-End with RET, LFD, or ESC.  DEL or C-h rubs out.  C-u kills line.
-Optional DEFAULT is password to start with."
-  (let ((pass (if default default ""))
-       (c 0)
-       (echo-keystrokes 0)
-       (cursor-in-echo-area t))
-    (while (progn (message "%s%s"
-                          prompt
-                          (make-string (length pass) ?.))
-                 (setq c (read-char))
-                 (and (/= c ?\r) (/= c ?\n) (/= c ?\e)))
-      (if (= c ?\C-u)
-         (setq pass "")
-       (if (and (/= c ?\b) (/= c ?\177))
-           (setq pass (concat pass (char-to-string c)))
-         (if (> (length pass) 0)
-             (setq pass (substring pass 0 -1))))))
-    (message "")
-    (message nil)
-    pass))
+;; Decode the region specified by FROM and TO by CODING.
+;; If CODING is nil or an invalid coding system, decode by `undecided'.
+(defun rmail-decode-region (from to coding)
+  (if (or (not coding) (not (coding-system-p coding)))
+      (setq coding 'undecided))
+  (decode-coding-region from to coding))
 
 ;; the  rmail-break-forwarded-messages  feature is not implemented
 (defun rmail-convert-to-babyl-format ()
@@ -1585,7 +1769,17 @@ Optional DEFAULT is password to start with."
               (setq last-coding-system-used nil)
               (or rmail-enable-mime
                   (not rmail-enable-multibyte)
-                  (decode-coding-region start (point) 'undecided))
+                  (let ((mime-charset
+                         (if (and rmail-decode-mime-charset
+                                  (save-excursion
+                                    (goto-char start)
+                                    (search-forward "\n\n" nil t)
+                                    (let ((case-fold-search t))
+                                      (re-search-backward
+                                       rmail-mime-charset-pattern
+                                       start t))))
+                             (intern (downcase (match-string 1))))))
+                    (rmail-decode-region start (point) mime-charset)))
               (save-excursion
                 (goto-char start)
                 (forward-line 3)
@@ -1632,11 +1826,10 @@ Optional DEFAULT is password to start with."
           (delete-char -1)
           (delete-char 1))
          ((looking-at "[0-9A-F][0-9A-F]")
-          (subst-char-in-region
-           (1- (point)) (point) ?=
-           (rmail-hex-string-to-integer
-            (buffer-substring (point) (+ 2 (point)))))
-          (delete-char 2))
+          (let ((byte (rmail-hex-string-to-integer
+                       (buffer-substring (point) (+ 2 (point))))))
+            (delete-region (1- (point)) (+ 2 (point)))
+            (insert byte)))
          ((looking-at "=")
           (delete-char 1))
          (t
@@ -1676,7 +1869,7 @@ Optional DEFAULT is password to start with."
                      ""
                    (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.
@@ -1701,8 +1894,8 @@ Optional DEFAULT is password to start with."
   (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)
@@ -1753,9 +1946,10 @@ Otherwise, delete all header fields whose names match `rmail-ignored-headers'."
        (or ignored-headers (setq ignored-headers rmail-ignored-headers))
        (save-restriction
          (narrow-to-region (point-min) (point))
-         (while (progn
-                  (goto-char (point-min))
-                  (re-search-forward ignored-headers nil t))
+         (while (and ignored-headers
+                     (progn
+                       (goto-char (point-min))
+                       (re-search-forward ignored-headers nil t)))
            (beginning-of-line)
            (delete-region (point)
                           (if (re-search-forward "\n[^ \t]" nil t)
@@ -1765,47 +1959,135 @@ Otherwise, delete all header fields whose names match `rmail-ignored-headers'."
 (defun rmail-msg-is-pruned ()
   (rmail-maybe-set-message-counters)
   (save-restriction
+    (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
     (save-excursion
-  (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
-    (goto-char (point-min))
-    (forward-line 1)
+      (goto-char (point-min))
+      (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)
-      (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
-      (if pruned
-         (progn (goto-char (point-min))
-                (forward-line 1)
-                (delete-char 1)
-                (insert ?0)
-                (forward-line 1)
-                (let ((case-fold-search t))
-                  (while (looking-at "Summary-Line:\\|Mail-From:")
-                    (forward-line 1)))
-                (insert "*** EOOH ***\n")
-                (forward-char -1)
-                (search-forward "\n*** EOOH ***\n")
-                (forward-line -1)
-                (let ((temp (point)))
-                  (and (search-forward "\n\n" nil t)
-                       (delete-region temp (point))))
+      (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
+             (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*** EOOH ***\n")
-                (narrow-to-region (point) (point-max)))
-       (rmail-reformat-message (point-min) (point-max)))
+                (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)
+  (save-excursion
+    (save-restriction
+      (narrow-to-region start end)
+      (goto-char (point-min))
+      (vertical-motion (- (point-max) (point-min))))))
 \f
 ;;;; *** Rmail Attributes and Keywords ***
 
@@ -1855,12 +2137,21 @@ otherwise, show it in full."
                          (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))
@@ -1918,19 +2209,17 @@ again afterward.
 FUNCTION may not change the visible text of the message, but it may
 change the invisible header text."
   (save-excursion
-    (let ((obeg (- (point-max) (point-min))))
-      (unwind-protect
-         (progn
-           (narrow-to-region (rmail-msgbeg rmail-current-message)
-                             (point-max))
-           (goto-char (point-min))
-           (funcall function))
+    (unwind-protect
+       (progn
+         (narrow-to-region (rmail-msgbeg rmail-current-message)
+                           (point-max))
+         (goto-char (point-min))
+         (funcall function))
        ;; Note: we don't use save-restriction because that does not work right
        ;; if changes are made outside the saved restriction
        ;; before that restriction is restored.
-       ;; Here we assume that changes made by FUNCTION
-       ;; occur before the visible region of the message.
-       (narrow-to-region (- (point-max) obeg) (point-max))))))
+      (narrow-to-region (rmail-msgbeg rmail-current-message)
+                       (rmail-msgend rmail-current-message)))))
 
 (defun rmail-forget-messages ()
   (unwind-protect
@@ -2015,7 +2304,7 @@ change the invisible header text."
                   (max 1 (- total-messages messages-after-point))))
        (setq rmail-message-vector
              (apply 'vector (cons (point-min-marker) messages-head))
-             rmail-deleted-vector (concat "D" deleted-head)
+             rmail-deleted-vector (concat "0" deleted-head)
              rmail-summary-vector (make-vector rmail-total-messages nil)
              rmail-msgref-vector (make-vector (1+ rmail-total-messages) nil))
        (let ((i 0))
@@ -2023,21 +2312,35 @@ change the invisible header text."
            (aset rmail-msgref-vector i (list i))
            (setq i (1+ i))))
        (message "Counting messages...done")))))
-       
+
 (defun rmail-set-message-counters-counter (&optional stop)
-  (while (search-backward "\n\^_\^L\n" stop t)
-    (forward-char 1)
-    (setq messages-head (cons (point-marker) messages-head))
-    (save-excursion
-      (setq deleted-head
-           (cons (if (search-backward ", deleted,"
-                                      (prog1 (point)
-                                        (forward-line 2))
-                                      t)
-                     ?D ?\ )
-                 deleted-head)))
-    (if (zerop (% (setq total-messages (1+ total-messages)) 20))
-       (message "Counting messages...%d" total-messages))))
+  (let ((start (point))
+       next)
+    (while (search-backward "\n\^_\^L" stop t)
+      ;; Detect messages that have been added with DOS line endings and
+      ;; convert the line endings for such messages.
+      (setq next (point))
+      (if (looking-at "\n\^_\^L\r\n")
+         (let ((buffer-read-only nil)
+               (buffer-undo t))
+           (message "Counting messages...(converting line endings)")
+           (save-excursion
+             (goto-char start)
+             (while (search-backward "\r\n" next t)
+               (delete-char 1)))))
+      (setq start next)
+      (forward-char 1)
+      (setq messages-head (cons (point-marker) messages-head))
+      (save-excursion
+       (setq deleted-head
+             (cons (if (search-backward ", deleted,"
+                                        (prog1 (point)
+                                          (forward-line 2))
+                                        t)
+                       ?D ?\ )
+                   deleted-head)))
+      (if (zerop (% (setq total-messages (1+ total-messages)) 20))
+         (message "Counting messages...%d" total-messages)))))
 
 (defun rmail-beginning-of-message ()
   "Show current message starting from the beginning."
@@ -2077,6 +2380,10 @@ If summary buffer is currently displayed, update current message there also."
            (save-restriction
              (if (prog1 (= (following-char) ?0)
                    (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))
                    (narrow-to-region (point) end))
                  (rfc822-goto-eoh)
                (search-forward "\n*** EOOH ***\n" end t))
@@ -2084,8 +2391,12 @@ If summary buffer is currently displayed, update current message there also."
              (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)
@@ -2096,6 +2407,11 @@ If summary buffer is currently displayed, update current message there also."
            (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)
@@ -2113,9 +2429,78 @@ If summary buffer is currently displayed, update current message there also."
             (let ((curr-msg rmail-current-message))
               (rmail-select-summary
                (rmail-summary-goto-msg curr-msg t t))))
+       (with-current-buffer rmail-buffer
+         (rmail-auto-file))
        (if blurb
            (message blurb))))))
 
+(defun rmail-redecode-body (coding)
+  "Decode the body of the current message using coding system CODING.
+This is useful with mail messages that have malformed or missing
+charset= headers.
+
+This function assumes that the current message is already decoded
+and displayed in the RMAIL buffer, but the coding system used to
+decode it was incorrect.  It then encodes the message back to its
+original form, and decodes it again, using the coding system CODING.
+
+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
+locking-shift codes are impossible to recover.  This function is meant
+to be used to fix messages encoded with 8-bit encodings, such as
+iso-8859, koi8-r, etc."
+  (interactive "zCoding system for re-decoding this message: ")
+  (when (not rmail-enable-mime)
+    (or (eq major-mode 'rmail-mode)
+       (switch-to-buffer rmail-buffer))
+    (save-excursion
+      (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 ()
   ;; Do this only if the system supports faces.
@@ -2159,10 +2544,47 @@ If summary buffer is currently displayed, update current message there also."
                  (setq rmail-overlay-list
                        (cons overlay rmail-overlay-list))))))))))
 
+(defun rmail-auto-file ()
+  "Automatically move a message into a sub-folder based on criteria.
+Called when a new message is displayed."
+  (if (or (rmail-message-labels-p rmail-current-message "filed")
+         (not (string= (buffer-file-name)
+                       (expand-file-name rmail-file-name))))
+      ;; Do nothing if it's already been filed.
+      nil
+    ;; Find out some basics (common fields)
+    (let ((from (mail-fetch-field "from"))
+         (subj (mail-fetch-field "subject"))
+         (to   (concat (mail-fetch-field "to") "," (mail-fetch-field "cc")))
+         (d rmail-automatic-folder-directives)
+         (directive-loop nil)
+         (folder nil))
+      (while d
+       (setq folder (car (car d))
+             directive-loop (cdr (car d)))
+       (while (and (car directive-loop)
+                   (let ((f (cond
+                             ((string= (car directive-loop) "from") from)
+                             ((string= (car directive-loop) "to") to)
+                             ((string= (car directive-loop) "subject") subj)
+                             (t (mail-fetch-field (car directive-loop))))))
+                     (and f (string-match (car (cdr directive-loop)) f))))
+         (setq directive-loop (cdr (cdr directive-loop))))
+       ;; If there are no directives left, then it was a complete match.
+       (if (null directive-loop)
+           (if (null folder)
+               (rmail-delete-forward)
+             (if (string= "/dev/null" folder)
+                 (rmail-delete-message)
+               (rmail-output-to-rmail-file folder 1 t)
+               (setq d nil))))
+       (setq d (cdr d))))))
+
 (defun rmail-next-message (n)
   "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)))
 
@@ -2170,7 +2592,7 @@ With prefix arg N, moves forward N messages, or backward if N is negative."
   "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.
@@ -2179,6 +2601,7 @@ or backward if N is negative.
 
 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))
@@ -2240,22 +2663,39 @@ or forward if N is negative."
        (if (not primary-only)
            (string-match recipients (or (mail-fetch-field "Cc") ""))))))
 
-(defun rmail-message-regexp-p (msg regexp)
-  "Return t, if for message number MSG, regexp REGEXP matches in the header."
-  (save-excursion
-    (goto-char (rmail-msgbeg msg))
-    (let (beg end)
-      (save-excursion
-       (forward-line 2)
-       (setq beg (point)))
-      (save-excursion 
-       (search-forward "\n*** EOOH ***\n" (point-max))
-       (when (= beg (match-beginning 0))
+(defun rmail-message-regexp-p (n regexp)
+  "Return t, if for message number N, regexp REGEXP matches in the header."
+  (let ((beg (rmail-msgbeg n))
+       (end (rmail-msgend n)))
+    (goto-char beg)
+    (forward-line 1)
+    (save-excursion
+      (save-restriction
+       (if (prog1 (= (following-char) ?0)
+             (forward-line 2)
+             ;; If there's a Summary-line in the (otherwise empty)
+             ;; header, we didn't yet get past the EOOH line.
+             (when (looking-at "^\\*\\*\\* EOOH \\*\\*\\*\n")
+               (forward-line 1))
+             (setq beg (point))
+             (narrow-to-region (point) end))
+           (progn
+             (rfc822-goto-eoh)
+             (setq end (point)))
          (setq beg (point))
-         (search-forward "\n\n" (point-max)))
-       (setq end (point)))
-      (goto-char beg)
-      (re-search-forward regexp end t))))
+         (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)
@@ -2284,6 +2724,7 @@ Interactively, empty argument means use same regexp used last time."
   (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))
@@ -2299,28 +2740,30 @@ Interactively, empty argument means use same regexp used last time."
            ;; 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))
-           (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)
@@ -2382,9 +2825,9 @@ If N is negative, go backwards instead."
        (setq subject (substring subject (match-end 0))))
     (if (string-match "[ \t]+\\'" subject)
        (setq subject (substring subject 0 (match-beginning 0))))
-    (setq search-regexp (concat "^Subject: *\\(Re: *\\)?"
+    (setq search-regexp (concat "^Subject: *\\(Re:[ \t]*\\)?"
                                (regexp-quote subject)
-                               "\n"))
+                               "[ \t]*\n"))
     (save-excursion
       (save-restriction
        (widen)
@@ -2436,6 +2879,7 @@ If N is negative, go forwards instead."
 (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)))
@@ -2488,9 +2932,20 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
        (setq i (1+ i)))
       newnum)))
 
+(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)
+      (funcall rmail-confirm-expunge
+              "Erase deleted messages from Rmail file? ")))
+
 (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)
@@ -2501,7 +2956,10 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
         (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.
@@ -2565,15 +3023,17 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
          (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."
   (interactive)
-  (rmail-only-expunge)
-  (if (rmail-summary-exists)
-      (rmail-select-summary
-       (rmail-update-summary))))
+  (when (rmail-expunge-confirmed)
+    (rmail-only-expunge)
+    (if (rmail-summary-exists)
+       (rmail-select-summary (rmail-update-summary)))))
 \f
 ;;;; *** Rmail Mailing Commands ***
 
@@ -2588,7 +3048,7 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
        (compose-mail to subject others
                      noerase nil
                      yank-action sendactions)
-      (if (and window-system rmail-mail-new-frame)
+      (if (and (display-multi-frame-p) rmail-mail-new-frame)
          (prog1
              (compose-mail to subject others
                            noerase 'switch-to-buffer-other-frame
@@ -2624,19 +3084,25 @@ use \\[mail-yank-original] to yank the original message into it."
             (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)
@@ -2691,8 +3157,9 @@ use \\[mail-yank-original] to yank the original message into it."
         (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 " ")
@@ -2774,7 +3241,7 @@ see the documentation of `rmail-resend'."
   (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")
@@ -2788,7 +3255,8 @@ see the documentation of `rmail-resend'."
           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
@@ -2799,24 +3267,27 @@ see the documentation of `rmail-resend'."
          (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)
@@ -2832,15 +3303,19 @@ typically for purposes of moderating a list."
   (interactive "sResend to: ")
   (require 'sendmail)
   (require 'mailalias)
+  (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"))
        (case-fold-search nil)
-       (mailbuf (current-buffer)))
+       (mailbuf rmail-buffer))
     (unwind-protect
-       (save-excursion
+       (with-current-buffer tembuf
          ;;>> Copy message into temp buffer
-         (set-buffer tembuf)
-         (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.
@@ -2877,6 +3352,8 @@ typically for purposes of moderating a list."
                    (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)
@@ -2901,17 +3378,21 @@ typically for purposes of moderating a list."
          (let (mail-aliases)
            (funcall send-mail-function)))
       (kill-buffer tembuf))
-    (rmail-set-attribute "resent" t rmail-current-message)))
+    (with-current-buffer rmail-buffer
+      (rmail-set-attribute "resent" t rmail-current-message))))
 \f
 (defvar mail-unsent-separator
   (concat "^ *---+ +Unsent message follows +---+ *$\\|"
          "^ *---+ +Returned message +---+ *$\\|"
+         "^ *---+ *Returned mail follows *---+ *$\\|"
          "^Start of returned message$\\|"
+         "^---+ Below this line is a copy of the message.$\\|"
          "^ *---+ +Original message +---+ *$\\|"
          "^ *--+ +begin message +--+ *$\\|"
          "^ *---+ +Original message follows +---+ *$\\|"
          "^ *---+ +Your message follows +---+ *$\\|"
-         "^|? *---+ +Message text follows: +---+ *|?$")
+         "^|? *---+ +Message text follows: +---+ *|?$\\|"
+         "^ *---+ +This is a copy of \\w+ message, including all the headers.*---+ *$")
   "A regexp that matches the separator before the text of a failed message.")
 
 (defvar mail-mime-unsent-header "^Content-Type: message/rfc822 *$"
@@ -2931,36 +3412,35 @@ specifying headers which should not be copied into the new message."
   (require 'mail-utils)
   (let ((rmail-this-buffer (current-buffer))
        (msgnum rmail-current-message)
-       bounce-start bounce-end bounce-indent resending)
+       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
-      ;; Narrow down to just the quoted original message
-      (rmail-beginning-of-message)
-      (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'()+_,./:=?]+\\)\"?" 
+      (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)))))
-             (or (re-search-forward mail-mime-unsent-header nil t)
-                 (error "Cannot find beginning of header in failed message"))
-             (or (search-forward "\n\n" nil t)
-                 (error "Cannot find start of Mime data in failed message"))
+                          (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
+               (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")
@@ -2975,11 +3455,12 @@ specifying headers which should not be copied into the new message."
                (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.
+           ;; 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)
@@ -2992,10 +3473,10 @@ specifying headers which should not be copied into the new message."
                  (setq bounce-end (point)))
              (setq bounce-start (point)
                    bounce-end (point-max)))
-           (or (search-forward "\n\n" nil t)
-               (error "Cannot find end of header in failed message"))
-           ))))
-    ;; Start sending a new message; default header fields from the original.
+           (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)
@@ -3007,14 +3488,17 @@ specifying headers which should not be copied into the new message."
          ;; 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))
+         (let ((inhibit-read-only t)
+               rmail-displayed-headers
+               rmail-ignored-headers)
            (erase-buffer)
-           (insert-buffer-substring rmail-this-buffer bounce-start bounce-end)
+           (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:\\|^from:\\|^return-path:")
+           (rmail-clear-headers "^sender:\\|^return-path:\\|^received:")
            (mail-sendmail-delimit-header)
            (save-restriction
              (narrow-to-region (point-min) (mail-header-end))
@@ -3024,9 +3508,7 @@ specifying headers which should not be copied into the new message."
                      (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)
-           (set-buffer rmail-this-buffer)
-           (rmail-beginning-of-message))))))
+           (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.
@@ -3069,7 +3551,7 @@ This has an effect only if a 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))))
@@ -3079,7 +3561,6 @@ This has an effect only if a summary buffer exists."
 
 (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))
@@ -3111,20 +3592,45 @@ This has an effect only if a summary buffer exists."
            (add-text-properties (point-min) (point-max) '(rmail-fontified t))
            (font-lock-fontify-region (point-min) (point-max))
            (and (not modified) (buffer-modified-p) (set-buffer-modified-p nil)))))))
-
+\f
 ;;; Speedbar support for RMAIL files.
-(eval-when-compile (require 'speedbspec))
+(eval-when-compile (require 'speedbar))
+
+(defvar rmail-speedbar-match-folder-regexp "^[A-Z0-9]+\\(\\.[A-Z0-9]+\\)?$"
+  "*This regex us used to match folder names to be displayed in speedbar.
+Enabling this will permit speedbar to display your folders for easy
+browsing, and moving of messages.")
 
 (defvar rmail-speedbar-last-user nil
   "The last user to be displayed in the speedbar.")
 
+(defvar rmail-speedbar-key-map nil
+  "Keymap used when in rmail display mode.")
+
+(defun rmail-install-speedbar-variables ()
+  "Install those variables used by speedbar to enhance rmail."
+  (if rmail-speedbar-key-map
+      nil
+    (setq rmail-speedbar-key-map (speedbar-make-specialized-keymap))
+
+    (define-key rmail-speedbar-key-map "e" 'speedbar-edit-line)
+    (define-key rmail-speedbar-key-map "r" 'speedbar-edit-line)
+    (define-key rmail-speedbar-key-map "\C-m" 'speedbar-edit-line)
+    (define-key rmail-speedbar-key-map "M"
+      'rmail-speedbar-move-message-to-folder-on-line)))
+
 (defvar rmail-speedbar-menu-items
-  '(["Browse Item On Line" speedbar-edit-line t]
-    ["Move message to folder" rmail-move-message-to-folder-on-line
+  '(["Read Folder" speedbar-edit-line t]
+    ["Move message to folder" rmail-speedbar-move-message-to-folder-on-line
      (save-excursion (beginning-of-line)
                     (looking-at "<M> "))])
   "Additional menu-items to add to speedbar frame.")
 
+;; Make sure our special speedbar major mode is loaded
+(if (featurep 'speedbar)
+    (rmail-install-speedbar-variables)
+  (add-hook 'speedbar-load-hook 'rmail-install-speedbar-variables))
+
 (defun rmail-speedbar-buttons (buffer)
   "Create buttons for BUFFER containing rmail messages.
 Click on the address under Reply to: to reply to this person.
@@ -3156,7 +3662,7 @@ current message into that RMAIL folder."
       (let* ((case-fold-search nil)
             (df (directory-files (save-excursion (set-buffer buffer)
                                                  default-directory)
-                                 nil "^[A-Z0-9]+\\(\\.[A-Z0-9]+\\)?$")))
+                                 nil rmail-speedbar-match-folder-regexp)))
        (while df
          (speedbar-insert-button "<M>" 'speedbar-button-face 'highlight
                                  'rmail-speedbar-move-message (car df))
@@ -3177,7 +3683,7 @@ TOKEN and INDENT are not used."
    (message "Loading in RMAIL file %s..." text)
    (find-file text)))
 
-(defun rmail-move-message-to-folder-on-line ()
+(defun rmail-speedbar-move-message-to-folder-on-line ()
   "If the current line is a folder, move current message to it."
   (interactive)
   (save-excursion
@@ -3194,6 +3700,52 @@ TEXT and INDENT are not used."
    (message "Moving message to %s" token)
    (rmail-output-to-rmail-file token)))
 
+; Functions for setting, getting and encoding the POP password.
+; The password is encoded to prevent it from being easily accessible
+; to "prying eyes."  Obviously, this encoding isn't "real security,"
+; nor is it meant to be.
+
+;;;###autoload
+(defun rmail-set-pop-password (password)
+  "Set PASSWORD to be used for retrieving mail from a POP server."
+  (interactive "sPassword: ")
+  (if password
+      (setq rmail-encoded-pop-password
+           (rmail-encode-string password (emacs-pid)))
+    (setq rmail-pop-password nil)
+    (setq rmail-encoded-pop-password nil)))
+
+(defun rmail-get-pop-password ()
+  "Get the password for retrieving mail from a POP server.  If none
+has been set, then prompt the user for one."
+  (if (not rmail-encoded-pop-password)
+      (progn (if (not rmail-pop-password)
+                (setq rmail-pop-password (read-passwd "POP password: ")))
+            (rmail-set-pop-password rmail-pop-password)
+            (setq rmail-pop-password nil)))
+  (rmail-encode-string rmail-encoded-pop-password (emacs-pid)))
+
+(defun rmail-have-password ()
+  (or rmail-pop-password rmail-encoded-pop-password))
+
+(defun rmail-encode-string (string mask)
+ "Encode STRING with integer MASK, by taking the exclusive OR of the
+lowest byte in the mask with the first character of string, the
+second-lowest-byte with the second character of the string, etc.,
+restarting at the lowest byte of the mask whenever it runs out.
+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)
+       (len (length string-vector)) (curmask mask) charmask)
+   (while (< i len)
+     (if (= curmask 0)
+        (setq curmask mask))
+     (setq charmask (% curmask 256))
+     (setq curmask (lsh curmask -8))
+     (aset string-vector i (logxor charmask (aref string-vector i)))
+     (setq i (1+ i)))
+   (concat string-vector)))
 
 (provide 'rmail)