*** empty log message ***
[bpt/emacs.git] / lisp / mail / rmail.el
index 16f63ae..b84ea1f 100644 (file)
@@ -1,6 +1,7 @@
-;;; 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 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1987, 1988, 1993, 1994, 1995, 1996, 1997, 1998,
+;;   2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: mail
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
 
 ;;; Code:
 
 ;;
 
 (require 'mail-utils)
+(eval-when-compile (require 'mule-util)) ; for detect-coding-with-priority
+
+(defvar deleted-head)
+(defvar font-lock-fontified)
+(defvar mail-abbrev-syntax-table)
+(defvar mail-abbrevs)
+(defvar messages-head)
+(defvar rmail-use-spam-filter)
+(defvar rsf-beep)
+(defvar rsf-sleep-after-message)
+(defvar total-messages)
 
 ; 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)
+
+(defgroup rmail-obsolete nil
+  "Rmail obsolete customization variables."
+  :group 'rmail)
 
 (defcustom rmail-movemail-program nil
-  "If non-nil, name of program for fetching new mail."
+  "If non-nil, the file name of the `movemail' program."
   :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."
+  "*Password to use when reading mail from POP server.
+Please use `rmail-remote-password' instead."
   :type '(choice (string :tag "Password")
                 (const :tag "Not Required" nil))
-  :group 'rmail-retrieve)
+  :group 'rmail-obsolete)
 
 (defcustom rmail-pop-password-required nil
-  "*Non-nil if a password is required when reading mail using POP."
+  "*Non-nil if a password is required when reading mail from a POP server.
+Please use rmail-remote-password-required instead."
   :type 'boolean
-  :group 'rmail-retrieve)
+  :group 'rmail-obsolete)
+
+(defcustom rmail-remote-password nil
+  "*Password to use when reading mail from a remote server.
+This setting is ignored for mailboxes whose URL already contains a password."
+  :type '(choice (string :tag "Password")
+                (const :tag "Not Required" nil))
+  :set-after '(rmail-pop-password)
+  :set #'(lambda (symbol value)
+          (set-default symbol
+                       (if (and (not value)
+                                 (boundp 'rmail-pop-password)
+                                rmail-pop-password)
+                           rmail-pop-password
+                         value))
+          (setq rmail-pop-password nil))
+  :group 'rmail-retrieve
+  :version "22.1")
+
+(defcustom rmail-remote-password-required nil
+  "*Non-nil if a password is required when reading mail from a remote server."
+  :type 'boolean
+  :set-after '(rmail-pop-password-required)
+  :set #'(lambda (symbol value)
+          (set-default symbol
+                       (if (and (not value)
+                                 (boundp 'rmail-pop-password-required)
+                                rmail-pop-password-required)
+                           rmail-pop-password-required
+                         value))
+          (setq rmail-pop-password-required nil))
+  :group 'rmail-retrieve
+  :version "22.1")
 
 (defcustom rmail-movemail-flags nil
   "*List of flags to pass to movemail.
@@ -107,35 +163,137 @@ or `-k' to enable Kerberos authentication."
   :group 'rmail-retrieve
   :version "20.3")
 
-(defvar rmail-pop-password-error "invalid usercode or password"
-  "Regular expression matching incorrect-password POP server error messages.
+(defvar rmail-remote-password-error "invalid usercode or password\\|
+unknown user name or bad password\\|Authentication failed\\|MU_ERR_AUTH_FAILURE"
+  "Regular expression matching incorrect-password POP or IMAP 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-remote-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."
+  "*Non-nil means leave incoming mail in the user's inbox--don't delete it."
   :type 'boolean
   :group 'rmail-retrieve)
 
+(defcustom rmail-movemail-search-path nil
+    "*List of directories to search for movemail (in addition to `exec-path')."
+    :group 'rmail-retrieve
+    :type '(repeat (directory)))
+
+(defun rmail-probe (prog)
+  "Determine what flavor of movemail PROG is.
+We do this by executing it with `--version' and analyzing its output."
+  (with-temp-buffer
+    (let ((tbuf (current-buffer)))
+      (buffer-disable-undo tbuf)
+      (call-process prog nil tbuf nil "--version")
+      (if (not (buffer-modified-p tbuf))
+         ;; Should not happen...
+         nil
+       (goto-char (point-min))
+       (cond
+        ((looking-at ".*movemail: invalid option")
+         'emacs)    ;; Possibly...
+        ((looking-at "movemail (GNU Mailutils .*)")
+         'mailutils)
+        (t
+         ;; FIXME:
+         'emacs))))))
+
+(defun rmail-autodetect ()
+  "Determine and return the file name of the `movemail' program.
+If `rmail-movemail-program' is non-nil, use it.
+Otherwise, look for `movemail' in the directories in
+`rmail-movemail-search-path', those in `exec-path', and `exec-directory'."
+  (if rmail-movemail-program
+      (rmail-probe rmail-movemail-program)
+    (catch 'scan
+      (dolist (dir (append rmail-movemail-search-path exec-path
+                          (list exec-directory)))
+       (when (and dir (file-accessible-directory-p dir))
+         (let ((progname (expand-file-name "movemail" dir)))
+           (when (and (not (file-directory-p progname))
+                      (file-executable-p progname))
+             (let ((x (rmail-probe progname)))
+               (when x
+                 (setq rmail-movemail-program progname)
+                 (throw 'scan x))))))))))
+
+(defvar rmail-movemail-variant-in-use nil
+  "The movemail variant currently in use. Known variants are:
+
+  `emacs'     Means any implementation, compatible with the native Emacs one.
+              This is the default;
+  `mailutils' Means GNU mailutils implementation, capable of handling full
+mail URLs as the source mailbox;")
+
+;;;###autoload
+(defun rmail-movemail-variant-p (&rest variants)
+  "Return t if the current movemail variant is any of VARIANTS.
+Currently known variants are 'emacs and 'mailutils."
+  (when (not rmail-movemail-variant-in-use)
+    ;; Autodetect
+    (setq rmail-movemail-variant-in-use (rmail-autodetect)))
+  (not (null (member rmail-movemail-variant-in-use variants))))
+
 ;;;###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)
 
 ;;;###autoload
-(defvar rmail-default-dont-reply-to-names "info-" "\
-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
+(defvar rmail-default-dont-reply-to-names "\\`info-" "\
+A regular expression specifying part 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:"
-  "*Regexp to match header fields that Rmail should normally hide."
+(defcustom rmail-ignored-headers
+  (concat "^via:\\|^mail-from:\\|^origin:\\|^references:\\|^sender:"
+         "\\|^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:\\|^face:"
+         "\\|^x-mailer:\\|^delivered-to:\\|^lines:"
+         "\\|^content-transfer-encoding:\\|^x-coding-system:"
+         "\\|^return-path:\\|^errors-to:\\|^return-receipt-to:"
+         "\\|^precedence:\\|^list-help:\\|^list-post:\\|^list-subscribe:"
+         "\\|^list-id:\\|^list-unsubscribe:\\|^list-archive:"
+         "\\|^content-length:\\|^nntp-posting-date:\\|^user-agent"
+         "\\|^importance:\\|^envelope-to:\\|^delivery-date\\|^openpgp:"
+         "\\|^mbox-line:\\|^cancel-lock:"
+         "\\|^resent-face:\\|^resent-x.*:\\|^resent-organization:\\|^resent-openpgp:"
+
+         "\\|^x-.*:")
+  "*Regexp to match header fields that Rmail should normally hide.
+\(See also `rmail-nonignored-headers', which overrides this regexp.)
+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)
+
+(defcustom rmail-nonignored-headers "^x-spam-status:"
+  "*Regexp to match X header fields that Rmail should show.
+This regexp overrides `rmail-ignored-headers'; if both this regexp
+and that one match a certain header field, Rmail shows the field.
+
+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)
 
@@ -148,7 +306,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)
@@ -177,22 +335,24 @@ See also `rmail-highlight-face'."
 ;;;###autoload
 (defcustom rmail-primary-inbox-list nil "\
 *List of files which are inboxes for user's primary mail file `~/RMAIL'.
-`nil' means the default, which is (\"/usr/spool/mail/$USER\")
+nil means the default, which is (\"/usr/spool/mail/$USER\")
 \(the name varies depending on the operating system,
 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)
 
 ;;;###autoload
 (defcustom rmail-mail-new-frame nil
-  "*Non-nil means Rmail makes a new frame for composing outgoing mail."
+  "*Non-nil means Rmail makes a new frame for composing outgoing mail.
+This is handy if you want to preserve the window configuration of
+the frame where you have the RMAIL buffer displayed."
   :type 'boolean
   :group 'rmail-reply)
 
@@ -207,6 +367,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.")
@@ -216,8 +385,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-address)
+  :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
@@ -230,9 +406,9 @@ still the current message in the Rmail buffer.")
 ;;  files).
 
 (defvar rmail-mmdf-delim1 "^\001\001\001\001\n"
-  "Regexp marking the start of an mmdf message")
+  "Regexp marking the start of an mmdf message.")
 (defvar rmail-mmdf-delim2 "^\001\001\001\001\n"
-  "Regexp marking the end of an mmdf message")
+  "Regexp marking the end of an mmdf message.")
 
 (defcustom rmail-message-filter nil
   "If non-nil, a filter function for new messages in RMAIL.
@@ -241,6 +417,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.")
 
@@ -320,6 +522,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.
 
@@ -337,18 +544,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 three 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
@@ -408,55 +669,75 @@ is non-nil.")
   nil)
 
 (defvar rmail-font-lock-keywords
+  ;; These are all matched case-insensitively.
   (eval-when-compile
     (let* ((cite-chars "[>|}]")
-          (cite-prefix "A-Za-z")
+          (cite-prefix "a-z")
           (cite-suffix (concat cite-prefix "0-9_.@-`'\"")))
-      (list '("^\\(From\\|Sender\\):" . font-lock-function-name-face)
+      (list '("^\\(From\\|Sender\\|Resent-From\\):"
+             . font-lock-function-name-face)
            '("^Reply-To:.*$" . font-lock-function-name-face)
            '("^Subject:" . font-lock-comment-face)
+           '("^X-Spam-Status:" . font-lock-keyword-face)
            '("^\\(To\\|Apparently-To\\|Cc\\|Newsgroups\\):"
              . font-lock-keyword-face)
            ;; Use MATCH-ANCHORED to effectively anchor the regexp left side.
            `(,cite-chars
              (,(concat "\\=[ \t]*"
-                       "\\(\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
-                       "\\(" cite-chars "[ \t]*\\)\\)+"
+                       "\\(\\(\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
+                       "\\(" cite-chars "[ \t]*\\)\\)+\\)"
                        "\\(.*\\)")
               (beginning-of-line) (end-of-line)
-              (2 font-lock-constant-face nil t)
-              (4 font-lock-comment-face nil t)))
-           '("^\\(X-[A-Za-z0-9-]+\\|In-reply-to\\|Date\\):.*$"
+              (1 font-lock-comment-delimiter-face nil t)
+              (5 font-lock-comment-face nil t)))
+           '("^\\(X-[a-z0-9-]+\\|In-reply-to\\|Date\\):.*\\(\n[ \t]+.*\\)*$"
              . font-lock-string-face))))
   "Additional expressions to highlight in Rmail mode.")
 
 ;; 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.
@@ -472,18 +753,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
@@ -506,7 +787,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))
@@ -518,21 +801,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)
@@ -556,6 +836,8 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
 ; I have checked that adding "-*- rmail -*-" to the BABYL OPTIONS line
 ; will not cause emacs 18.55 problems.
 
+;; This calls rmail-decode-babyl-format if the file is already Babyl.
+
 (defun rmail-convert-file ()
   (let (convert)
     (widen)
@@ -600,11 +882,10 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
          ;; We still have to decode BABYL part.
          (rmail-decode-babyl-format)))))
 
-;;; I have checked that adding "-*- rmail -*-" to the BABYL OPTIONS line
-;;; will not cause emacs 18.55 problems.
-
 (defun rmail-insert-rmail-file-header ()
   (let ((buffer-read-only nil))
+    ;; -*-rmail-*- is here so that visiting the file normally
+    ;; recognizes it as an Rmail file.
     (insert "BABYL OPTIONS: -*- rmail -*-
 Version: 5
 Labels:
@@ -612,35 +893,42 @@ Note:   This is the header of an rmail file.
 Note:   If you are seeing it in rmail,
 Note:    it means the file has no messages in it.\n\^_")))
 
-;; Decode Babyl formated part at the head of current buffer by
+;; Decode Babyl formatted part at the head of current buffer by
 ;; rmail-file-coding-system, or if it is nil, do auto conversion.
 
 (defun rmail-decode-babyl-format ()
   (let ((modifiedp (buffer-modified-p))
        (buffer-read-only nil)
+       (coding-system rmail-file-coding-system)
        from to)
     (goto-char (point-min))
-    (search-forward "\n\^_" nil t)     ; Skip BYBYL header.
+    (search-forward "\n\^_" nil t)     ; Skip BABYL header.
     (setq from (point))
     (goto-char (point-max))
     (search-backward "\n\^_" from 'mv)
     (setq to (point))
-    (if (not (and rmail-file-coding-system
-                 (coding-system-p rmail-file-coding-system)))
-       (setq rmail-file-coding-system (detect-coding-region from to t)))
-    (if (not (eq rmail-file-coding-system 'undecided))
-       (let ((count 1))
-         (goto-char from)
-         (while (search-forward "\n\^_" nil t)
-           (decode-coding-region from (1- (point)) rmail-file-coding-system)
-           (goto-char (point))
-           (setq from (point))
-           (if (= (% count 10) 0)
-               (message "Decoding messages...%d" count))
-           (setq count (1+ count)))
-         (message "Decoding messages...done")
-         (set-buffer-file-coding-system rmail-file-coding-system)
-         (set-buffer-modified-p modifiedp)))))
+    (unless (and coding-system
+                (coding-system-p coding-system))
+      (setq coding-system
+           ;; Emacs 21.1 and later writes RMAIL files in emacs-mule, but
+           ;; earlier versions did that with the current buffer's encoding.
+           ;; So we want to favor detection of emacs-mule (whose normal
+           ;; priority is quite low), but still allow detection of other
+           ;; encodings if emacs-mule won't fit.  The call to
+           ;; detect-coding-with-priority below achieves that.
+           (car (detect-coding-with-priority
+                 from to
+                 '((coding-category-emacs-mule . emacs-mule))))))
+    (unless (memq coding-system
+                 '(undecided undecided-unix))
+      (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)
+    (setq save-buffer-coding-system
+         (or coding-system 'undecided))))
 
 (defvar rmail-mode-map nil)
 (if rmail-mode-map
@@ -686,6 +974,7 @@ Note:    it means the file has no messages in it.\n\^_")))
   (define-key rmail-mode-map "w"      'rmail-output-body-to-file)
   (define-key rmail-mode-map "x"      'rmail-expunge)
   (define-key rmail-mode-map "."      'rmail-beginning-of-message)
+  (define-key rmail-mode-map "/"      'rmail-end-of-message)
   (define-key rmail-mode-map "<"      'rmail-first-message)
   (define-key rmail-mode-map ">"      'rmail-last-message)
   (define-key rmail-mode-map " "      'scroll-up)
@@ -697,7 +986,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)
   )
@@ -830,7 +1119,8 @@ Note:    it means the file has no messages in it.\n\^_")))
 All normal editing commands are turned off.
 Instead, these commands are available:
 
-\\[rmail-beginning-of-message] Move point to front of this message (same as \\[beginning-of-buffer]).
+\\[rmail-beginning-of-message] Move point to front of this message.
+\\[rmail-end-of-message]       Move point to bottom of this message.
 \\[scroll-up]  Scroll to next screen of this message.
 \\[scroll-down]        Scroll to previous screen of this message.
 \\[rmail-next-undeleted-message]       Move to Next non-deleted message.
@@ -874,10 +1164,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-mode-hooks 'rmail-mode-hook)))
 
 (defun rmail-mode-2 ()
   (kill-all-local-variables)
@@ -935,18 +1238,26 @@ Instead, these commands are available:
 
 ;; Set up the non-permanent locals associated with Rmail mode.
 (defun rmail-variables ()
+  (make-local-variable 'save-buffer-coding-system)
+  ;; If we don't already have a value for save-buffer-coding-system,
+  ;; get it from buffer-file-coding-system, and clear that
+  ;; because it should be determined in rmail-show-message.
+  (unless save-buffer-coding-system
+    (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)
   (setq font-lock-defaults
-   '(rmail-font-lock-keywords t nil nil nil
-     (font-lock-maximum-size . nil)
-     (font-lock-fontify-buffer-function . rmail-fontify-buffer-function)
-     (font-lock-unfontify-buffer-function . rmail-unfontify-buffer-function)
-     (font-lock-inhibit-thing-lock . (lazy-lock-mode fast-lock-mode))))
+       '(rmail-font-lock-keywords
+         t t nil nil
+         (font-lock-maximum-size . nil)
+         (font-lock-fontify-buffer-function . rmail-fontify-buffer-function)
+         (font-lock-unfontify-buffer-function . rmail-unfontify-buffer-function)
+         (font-lock-inhibit-thing-lock . (lazy-lock-mode fast-lock-mode))))
   (make-local-variable 'require-final-newline)
   (setq require-final-newline nil)
   (make-local-variable 'version-control)
@@ -954,19 +1265,35 @@ Instead, these commands are available:
   (make-local-variable 'kill-buffer-hook)
   (add-hook 'kill-buffer-hook 'rmail-mode-kill-summary)
   (make-local-variable 'file-precious-flag)
-  (setq file-precious-flag t))
+  (setq file-precious-flag t)
+  (make-local-variable 'desktop-save-buffer)
+  (setq desktop-save-buffer t))
 
 ;; 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.
@@ -990,21 +1317,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."
@@ -1064,8 +1403,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))
@@ -1073,10 +1412,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)
@@ -1085,7 +1424,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)
 
@@ -1094,13 +1433,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))))
 
@@ -1140,6 +1479,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.
@@ -1154,6 +1494,7 @@ It returns t if it got any new messages."
          (while all-files
            (let ((opoint (point))
                  (new-messages 0)
+                 (rsf-number-of-spam 0)
                  (delete-files ())
                  ;; If buffer has not changed yet, and has not been saved yet,
                  ;; don't replace the old backup file now.
@@ -1198,14 +1539,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)))
@@ -1236,11 +1577,62 @@ It returns t if it got any new messages."
                  (progn (goto-char opoint)
                         (if (or file-name rmail-inbox-list)
                             (message "(No new mail has arrived)")))
-               (if (rmail-summary-exists)
+               ;; check new messages to see if any of them is spam:
+               (if (and (featurep 'rmail-spam-filter)
+                        rmail-use-spam-filter)
+                   (let*
+                       ((old-messages (- rmail-total-messages new-messages))
+                         (rsf-scanned-message-number (1+ old-messages))
+                         ;; save deletion flags of old messages: vector starts
+                         ;; at zero (is one longer that no of messages),
+                         ;; therefore take 1+ old-messages
+                         (save-deleted
+                          (substring rmail-deleted-vector 0 (1+
+                          old-messages))))
+                      ;; set all messages to undeleted
+                      (setq rmail-deleted-vector
+                            (make-string (1+ rmail-total-messages) ?\ ))
+                     (while (<= rsf-scanned-message-number
+                     rmail-total-messages)
+                       (progn
+                         (if (not (rmail-spam-filter rsf-scanned-message-number))
+                             (progn (setq rsf-number-of-spam (1+ rsf-number-of-spam)))
+                           )
+                         (setq rsf-scanned-message-number (1+ rsf-scanned-message-number))
+                         ))
+                     (if (> rsf-number-of-spam 0)
+                         (progn
+                           (when (rmail-expunge-confirmed)
+                              (rmail-only-expunge t))
+                            ))
+                      (setq rmail-deleted-vector
+                            (concat
+                             save-deleted
+                             (make-string (- rmail-total-messages old-messages)
+                                          ?\ )))
+                     ))
+               (if (rmail-summary-exists)
                    (rmail-select-summary
                     (rmail-update-summary)))
-               (message "%d new message%s read"
-                        new-messages (if (= 1 new-messages) "" "s"))
+               (message "%d new message%s read%s"
+                        new-messages (if (= 1 new-messages) "" "s")
+                        ;; print out a message on number of spam messages found:
+                        (if (and (featurep 'rmail-spam-filter)
+                                 rmail-use-spam-filter
+                                 (> rsf-number-of-spam 0))
+                            (if (= 1 new-messages)
+                                ", and found to be a spam message"
+                              (if (> rsf-number-of-spam 1)
+                                  (format ", %d of which found to be spam messages"
+                                          rsf-number-of-spam)
+                                ", one of which found to be a spam message"))
+                          ""))
+               (if (and (featurep 'rmail-spam-filter)
+                        rmail-use-spam-filter
+                        (> rsf-number-of-spam 0))
+                   (progn (if rsf-beep (beep t))
+                          (sleep-for rsf-sleep-after-message)))
+
                ;; Move to the first new message
                ;; unless we have other unseen messages before it.
                (rmail-show-message (rmail-first-unseen-message))
@@ -1250,20 +1642,86 @@ It returns t if it got any new messages."
       ;; Don't leave the buffer screwed up if we get a disk-full error.
       (or found (rmail-show-message)))))
 
+(defun rmail-parse-url (file)
+  "Parse the supplied URL. Return (list MAILBOX-NAME REMOTE PASSWORD GOT-PASSWORD)
+WHERE MAILBOX-NAME is the name of the mailbox suitable as argument to the
+actual version of `movemail', REMOTE is non-nil if MAILBOX-NAME refers to
+a remote mailbox, PASSWORD is the password if it should be
+supplied as a separate argument to `movemail' or nil otherwise, GOT-PASSWORD
+is non-nil if the user has supplied the password interactively.
+"
+  (cond
+   ((string-match "^\\([^:]+\\)://\\(\\([^:@]+\\)\\(:\\([^@]+\\)\\)?@\\)?.*" file)
+      (let (got-password supplied-password
+           (proto (match-string 1 file))
+           (user  (match-string 3 file))
+           (pass  (match-string 5 file))
+           (host  (substring file (or (match-end 2)
+                                      (+ 3 (match-end 1))))))
+
+       (if (not pass)
+           (when rmail-remote-password-required
+             (setq got-password (not (rmail-have-password)))
+             (setq supplied-password (rmail-get-remote-password
+                                      (string-equal proto "imap")))))
+
+       (if (rmail-movemail-variant-p 'emacs)
+           (if (string-equal proto "pop")
+               (list (concat "po:" user ":" host)
+                     t
+                     (or pass supplied-password)
+                     got-password)
+             (error "Emacs movemail does not support %s protocol" proto))
+         (list file
+               (or (string-equal proto "pop") (string-equal proto "imap"))
+               supplied-password
+               got-password))))
+
+   ((string-match "^po:\\([^:]+\\)\\(:\\(.*\\)\\)?" file)
+    (let (got-password supplied-password
+          (proto "pop")
+         (user  (match-string 1 file))
+         (host  (match-string 3 file)))
+
+      (when rmail-remote-password-required
+       (setq got-password (not (rmail-have-password)))
+       (setq supplied-password (rmail-get-remote-password nil)))
+
+      (list file "pop" supplied-password got-password)))
+
+   (t
+    (list file nil nil nil))))
+
 (defun rmail-insert-inbox-text (files renamep)
   ;; Detect a locked file now, so that we avoid moving mail
   ;; out of the real inbox file.  (That could scare people.)
   (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 remote mailbox names specially; don't expand as filenames
+      ;; in case the userid contains a directory separator.
+      (setq file (car files))
+      (let ((url-data (rmail-parse-url file)))
+       (setq file (nth 0 url-data))
+       (setq popmail (nth 1 url-data))
+       (setq password (nth 2 url-data))
+       (setq got-password (nth 3 url-data)))
+
+      (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))
+                   (concat ".newmail-"
+                           (file-name-nondirectory
+                            (if (memq system-type '(windows-nt cygwin))
+                                ;; cannot have "po:" in file name
+                                (substring file 3)
+                              file)))
                    ;; Use the directory of this rmail file
                    ;; because it's a nuisance to use the homedir
                    ;; if that is on a full disk and this rmail
@@ -1272,18 +1730,7 @@ It returns t if it got any new messages."
                     (expand-file-name buffer-file-name))))
       ;; Always use movemail to rename the file,
       ;; since there can be mailboxes in various directories.
-      (setq movemail t)
-;;;      ;; If getting from mail spool directory,
-;;;      ;; use movemail to move rather than just renaming,
-;;;      ;; so as to interlock with the mailer.
-;;;      (setq movemail (string= file
-;;;                          (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 (not popmail)
          (progn
            ;; On some systems, /usr/spool/mail/foo is a directory
            ;; and the actual inbox is /usr/spool/mail/foo/foo.
@@ -1291,20 +1738,7 @@ 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 (eq system-type 'windows-nt)
-                ;; cannot have "po:" in file name
-                (setq tofile
-                      (expand-file-name
-                       (concat ".newmail-pop-" (substring file (+ popmail 3)))
-                       (file-name-directory
-                        (expand-file-name buffer-file-name)))))
-            (message "Getting mail from post office ..."))
+            (message "Getting mail from the remote server ..."))
            ((and (file-exists-p tofile)
                  (/= 0 (nth 7 (file-attributes tofile))))
             (message "Getting mail from %s..." tofile))
@@ -1318,70 +1752,60 @@ It returns t if it got any new messages."
            ((or (file-exists-p tofile) (and (not popmail)
                                             (not (file-exists-p file))))
             nil)
-           ((and (not movemail) (not popmail))
-            ;; Try copying.  If that fails (perhaps no space) and
-            ;; we're allowed to blow away the inbox, rename instead.
-            (if rmail-preserve-inbox
-                (copy-file file tofile nil)
-              (condition-case nil
-                  (copy-file file tofile nil)
-                (error
-                 ;; Third arg is t so we can replace existing file TOFILE.
-                 (rename-file file tofile t))))
-            ;; Make the real inbox file empty.
-            ;; Leaving it deleted could cause lossage
-            ;; because mailers often won't create the file.
-            (if (not rmail-preserve-inbox)
-                (condition-case ()
-                    (write-region (point) (point) file)
-                  (file-error nil))))
            (t
-            (let ((errors nil))
-              (unwind-protect
-                  (save-excursion
-                    (setq errors (generate-new-buffer " *rmail loss*"))
-                    (buffer-disable-undo errors)
-                    (let ((args 
-                           (append 
-                            (list (or rmail-movemail-program
-                                      (expand-file-name "movemail"
-                                                        exec-directory))
-                                  nil errors nil)
-                            (if rmail-preserve-inbox 
-                                (list "-p")
-                              nil)
-                            rmail-movemail-flags
-                            (list file tofile)
-                            (if rmail-pop-password 
-                                (list rmail-pop-password)
-                              nil))))
-                      (apply 'call-process args))
-                    (if (not (buffer-modified-p errors))
-                        ;; No output => movemail won
-                        nil
-                      (set-buffer errors)
-                      (subst-char-in-region (point-min) (point-max)
-                                            ?\n ?\  )
-                      (goto-char (point-max))
-                      (skip-chars-backward " \t")
-                      (delete-region (point) (point-max))
-                      (goto-char (point-min))
-                      (if (looking-at "movemail: ")
-                          (delete-region (point-min) (match-end 0)))
-                      (beep t)
-                      (message "movemail: %s"
-                               (buffer-substring (point-min)
-                                                 (point-max)))
-                      ;; If we just read the password, most likely it is
-                      ;; wrong.  Otherwise, see if there is a specific
-                      ;; reason to think that the problem is a wrong passwd.
-                      (if (or got-password
-                              (re-search-forward rmail-pop-password-error
-                                                 nil t))
-                          (setq rmail-pop-password nil))
-                      (sit-for 3)
-                      nil))
-                (if errors (kill-buffer errors))))))
+            (with-temp-buffer
+              (let ((errors (current-buffer)))
+                (buffer-disable-undo errors)
+                (let ((args
+                       (append
+                        (list (or rmail-movemail-program
+                                  (expand-file-name "movemail"
+                                                    exec-directory))
+                              nil errors nil)
+                        (if rmail-preserve-inbox
+                            (list "-p")
+                          nil)
+                        (if (rmail-movemail-variant-p 'mailutils)
+                            (append (list "--emacs") rmail-movemail-flags)
+                          rmail-movemail-flags)
+                        (list file tofile)
+                        (if password (list password) nil))))
+                  (apply 'call-process args))
+                (if (not (buffer-modified-p errors))
+                    ;; No output => movemail won
+                    nil
+                  (set-buffer errors)
+                  (subst-char-in-region (point-min) (point-max)
+                                        ?\n ?\  )
+                  (goto-char (point-max))
+                  (skip-chars-backward " \t")
+                  (delete-region (point) (point-max))
+                  (goto-char (point-min))
+                  (if (looking-at "movemail: ")
+                      (delete-region (point-min) (match-end 0)))
+                  (beep t)
+                  ;; If we just read the password, most likely it is
+                  ;; wrong.  Otherwise, see if there is a specific
+                  ;; reason to think that the problem is a wrong passwd.
+                  (if (or got-password
+                          (re-search-forward rmail-remote-password-error
+                                             nil t))
+                      (rmail-set-remote-password nil))
+
+                  ;; If using Mailutils, remove initial error code
+                  ;; abbreviation
+                  (when (rmail-movemail-variant-p 'mailutils)
+                    (goto-char (point-min))
+                    (when (looking-at "[A-Z][A-Z0-9_]*:")
+                      (delete-region (point-min) (match-end 0))))
+
+                  (message "movemail: %s"
+                           (buffer-substring (point-min)
+                                             (point-max)))
+
+                  (sit-for 3)
+                  nil)))))
+
       ;; At this point, TOFILE contains the name to read:
       ;; Either the alternate name (if we renamed)
       ;; or the actual inbox (if not renaming).
@@ -1400,28 +1824,20 @@ 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))
+  ;; Use -dos decoding, to remove ^M characters left from base64 or
+  ;; rogue qp-encoded text.
+  (decode-coding-region from to
+                       (coding-system-change-eol-conversion coding 1))
+  ;; Don't reveal the fact we used -dos decoding, as users generally
+  ;; will not expect the RMAIL buffer to use DOS EOL format.
+  (setq buffer-file-coding-system
+       (setq last-coding-system-used
+             (coding-system-change-eol-conversion coding 0))))
 
 ;; the  rmail-break-forwarded-messages  feature is not implemented
 (defun rmail-convert-to-babyl-format ()
@@ -1459,12 +1875,88 @@ Optional DEFAULT is password to start with."
                              (save-excursion
                                (skip-chars-forward " \t\n")
                                (point)))
-              (or rmail-enable-mime
-                  (not rmail-enable-multibyte)
-                  (decode-coding-region start (point)
-                                        (or rmail-file-coding-system
-                                            'undecided)))
-              (narrow-to-region (point) (point-max)))
+              (save-excursion
+                (let* ((header-end
+                        (progn
+                          (save-excursion
+                            (goto-char start)
+                            (forward-line 1)
+                            (if (looking-at "0")
+                                (forward-line 1)
+                              (forward-line 2))
+                            (save-restriction
+                              (narrow-to-region (point) (point-max))
+                              (rfc822-goto-eoh)
+                              (point)))))
+                       (case-fold-search t)
+                       (quoted-printable-header-field-end
+                        (save-excursion
+                          (goto-char start)
+                          (re-search-forward
+                           "^content-transfer-encoding:\\(\n?[\t ]\\)*quoted-printable\\(\n?[\t ]\\)*"
+                           header-end t)))
+                       (base64-header-field-end
+                        (save-excursion
+                          (goto-char start)
+                          (re-search-forward
+                           "^content-transfer-encoding:\\(\n?[\t ]\\)*base64\\(\n?[\t ]\\)*"
+                           header-end t))))
+                  (if quoted-printable-header-field-end
+                      (save-excursion
+                        (unless
+                            (mail-unquote-printable-region header-end (point) nil t t)
+                          (message "Malformed MIME quoted-printable message"))
+                        ;; Change "quoted-printable" to "8bit",
+                        ;; to reflect the decoding we just did.
+                        (goto-char quoted-printable-header-field-end)
+                        (delete-region (point) (search-backward ":"))
+                        (insert ": 8bit")))
+                  (if base64-header-field-end
+                      (save-excursion
+                        (when
+                            (condition-case nil
+                                (progn
+                                  (base64-decode-region (1+ header-end)
+                                                        (- (point) 2))
+                                  t)
+                              (error nil))
+                          ;; Change "base64" to "8bit", to reflect the
+                          ;; decoding we just did.
+                          (goto-char base64-header-field-end)
+                          (delete-region (point) (search-backward ":"))
+                          (insert ": 8bit"))))
+                  (setq last-coding-system-used nil)
+                  (or rmail-enable-mime
+                      (not rmail-enable-multibyte)
+                      (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)))))
+              ;; Add an X-Coding-System: header if we don't have one.
+              (save-excursion
+                (goto-char start)
+                (forward-line 1)
+                (if (looking-at "0")
+                    (forward-line 1)
+                  (forward-line 2))
+                (or (save-restriction
+                      (narrow-to-region (point) (point-max))
+                      (rfc822-goto-eoh)
+                      (goto-char (point-min))
+                      (re-search-forward "^X-Coding-System:" nil t))
+                    (insert "X-Coding-System: "
+                            (symbol-name last-coding-system-used)
+                            "\n")))
+              (narrow-to-region (point) (point-max))
+              (and (= 0 (% count 10))
+                   (message "Converting to Babyl format...%d" count)))
              ;;*** MMDF format
              ((let ((case-fold-search t))
                 (looking-at rmail-mmdf-delim1))
@@ -1478,11 +1970,20 @@ Optional DEFAULT is password to start with."
                   (goto-char (point-min))
                   (while (search-forward "\n\^_" nil t); single char "\^_"
                     (replace-match "\n^_")))); 2 chars: "^" and "_"
+              (setq last-coding-system-used nil)
               (or rmail-enable-mime
                   (not rmail-enable-multibyte)
                   (decode-coding-region start (point) 'undecided))
+              (save-excursion
+                (goto-char start)
+                (forward-line 3)
+                (insert "X-Coding-System: "
+                        (symbol-name last-coding-system-used)
+                        "\n"))
               (narrow-to-region (point) (point-max))
-              (setq count (1+ count)))
+              (setq count (1+ count))
+              (and (= 0 (% count 10))
+                   (message "Converting to Babyl format...%d" count)))
              ;;*** Mail format
              ((looking-at "^From ")
               (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
@@ -1498,6 +1999,11 @@ Optional DEFAULT is password to start with."
                         (re-search-forward
                          "^content-transfer-encoding:\\(\n?[\t ]\\)*quoted-printable\\(\n?[\t ]\\)*"
                          header-end t)))
+                     (base64-header-field-end
+                      (save-excursion
+                        (re-search-forward
+                         "^content-transfer-encoding:\\(\n?[\t ]\\)*base64\\(\n?[\t ]\\)*"
+                         header-end t)))
                      (size
                       ;; Get the numeric value from the Content-Length field.
                       (save-excursion
@@ -1508,7 +2014,7 @@ Optional DEFAULT is password to start with."
                                              header-end t)
                              (let ((beg (point))
                                    (eol (progn (end-of-line) (point))))
-                               (string-to-int (buffer-substring beg eol)))))))
+                               (string-to-number (buffer-substring beg eol)))))))
                 (and size
                      (if (and (natnump size)
                               (<= (+ header-end size) (point-max))
@@ -1528,24 +2034,46 @@ Optional DEFAULT is password to start with."
                          (goto-char (+ header-end size))
                        (message "Ignoring invalid Content-Length field")
                        (sit-for 1 0 t)))
-                (if (re-search-forward
-                     (concat "^[\^_]?\\("
-                             rmail-unix-mail-delimiter
-                             "\\|"
-                             rmail-mmdf-delim1 "\\|"
-                             "^BABYL OPTIONS:\\|"
-                             "\^L\n[01],\\)") nil t)
+                (if (let ((case-fold-search nil))
+                      (re-search-forward
+                       (concat "^[\^_]?\\("
+                               rmail-unix-mail-delimiter
+                               "\\|"
+                               rmail-mmdf-delim1 "\\|"
+                               "^BABYL OPTIONS:\\|"
+                               "\^L\n[01],\\)") nil t))
                     (goto-char (match-beginning 1))
                   (goto-char (point-max)))
                 (setq count (1+ count))
                 (if quoted-printable-header-field-end
                     (save-excursion
-                      (rmail-decode-quoted-printable header-end (point))
+                      (unless
+                          (mail-unquote-printable-region header-end (point) nil t t)
+                        (message "Malformed MIME quoted-printable message"))
                       ;; Change "quoted-printable" to "8bit",
                       ;; to reflect the decoding we just did.
                       (goto-char quoted-printable-header-field-end)
                       (delete-region (point) (search-backward ":"))
-                      (insert ": 8bit"))))
+                      (insert ": 8bit")))
+                (if base64-header-field-end
+                    (save-excursion
+                      (when
+                          (condition-case nil
+                              (progn
+                                (base64-decode-region
+                                 (1+ header-end)
+                                 (save-excursion
+                                   ;; Prevent base64-decode-region
+                                   ;; from removing newline characters.
+                                   (skip-chars-backward "\n\t ")
+                                   (point)))
+                                t)
+                            (error nil))
+                        ;; Change "base64" to "8bit", to reflect the
+                        ;; decoding we just did.
+                        (goto-char base64-header-field-end)
+                        (delete-region (point) (search-backward ":"))
+                        (insert ": 8bit")))))
 
               (save-excursion
                 (save-restriction
@@ -1553,11 +2081,34 @@ Optional DEFAULT is password to start with."
                   (goto-char (point-min))
                   (while (search-forward "\n\^_" nil t); single char
                     (replace-match "\n^_")))); 2 chars: "^" and "_"
+              ;; This is for malformed messages that don't end in newline.
+              ;; There shouldn't be any, but some users say occasionally
+              ;; there are some.
+              (or (bolp) (newline))
               (insert ?\^_)
+              (setq last-coding-system-used nil)
               (or rmail-enable-mime
                   (not rmail-enable-multibyte)
-                  (decode-coding-region start (point) 'undecided))
-              (narrow-to-region (point) (point-max)))
+                  (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)
+                (insert "X-Coding-System: "
+                        (symbol-name last-coding-system-used)
+                        "\n"))
+              (narrow-to-region (point) (point-max))
+              (and (= 0 (% count 10))
+                   (message "Converting to Babyl format...%d" count)))
              ;;
              ;; This kludge is because some versions of sendmail.el
              ;; insert an extra newline at the beginning that shouldn't
@@ -1567,46 +2118,6 @@ Optional DEFAULT is password to start with."
              (t (error "Cannot convert to babyl format")))))
     count))
 
-(defun rmail-hex-char-to-integer (character)
-  "Return CHARACTER's value interpreted as a hex digit."
-  (if (and (>= character ?0) (<= character ?9))
-      (- character ?0)
-    (let ((ch (logior character 32)))
-      (if (and (>= ch ?a) (<= ch ?f))
-         (- ch (- ?a 10))
-       (error "Invalid hex digit `%c'" ch)))))
-
-(defun rmail-hex-string-to-integer (hex-string)
-  "Return decimal integer for HEX-STRING."
-  (let ((hex-num 0)
-       (index 0))
-    (while (< index (length hex-string))
-      (setq hex-num (+ (* hex-num 16)
-                      (rmail-hex-char-to-integer (aref hex-string index))))
-      (setq index (1+ index)))
-    hex-num))
-
-(defun rmail-decode-quoted-printable (from to)
-  "Decode Quoted-Printable in the region between FROM and TO."
-  (interactive "r")
-  (goto-char from)
-  (or (markerp to)
-      (setq to (copy-marker to)))
-  (while (search-forward "=" to t)
-    (cond ((eq (following-char) ?\n)
-          (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))
-         ((looking-at "=")
-          (delete-char 1))
-         (t
-          (message "Malformed MIME quoted-printable message")))))
-
 ;; Delete the "From ..." line, creating various other headers with
 ;; information from it if they don't already exist.  Now puts the
 ;; original line into a mail-from: header line for debugging and for
@@ -1641,7 +2152,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.
@@ -1666,8 +2177,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)
@@ -1694,7 +2205,8 @@ If the optional argument IGNORED-HEADERS is non-nil,
 delete all header fields whose names match that regexp.
 Otherwise, if `rmail-displayed-headers' is non-nil,
 delete all header fields *except* those whose names match that regexp.
-Otherwise, delete all header fields whose names match `rmail-ignored-headers'."
+Otherwise, delete all header fields whose names match `rmail-ignored-headers'
+unless they also match `rmail-nonignored-headers'."
   (when (search-forward "\n\n" nil t)
     (forward-char -1)
     (let ((case-fold-search t)
@@ -1718,59 +2230,150 @@ 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))
+         (goto-char (point-min))
+         (while (and ignored-headers
+                     (re-search-forward ignored-headers nil t))
            (beginning-of-line)
-           (delete-region (point)
-                          (if (re-search-forward "\n[^ \t]" nil t)
-                              (1- (point))
-                            (point-max)))))))))
+           (if (looking-at rmail-nonignored-headers)
+               (forward-line 1)
+             (delete-region (point)
+                            (save-excursion
+                              (if (re-search-forward "\n[^ \t]" nil t)
+                                  (1- (point))
+                                (point-max)))))))))))
 
 (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 ***
 
@@ -1820,12 +2423,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))
@@ -1883,19 +2495,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
@@ -1980,7 +2590,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))
@@ -1988,26 +2598,85 @@ 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."
   (interactive)
-  (rmail-show-message rmail-current-message))
+  (let ((rmail-show-message-hook
+        (list (function (lambda ()
+                          (goto-char (point-min)))))))
+    (rmail-show-message rmail-current-message)))
+
+(defun rmail-end-of-message ()
+  "Show bottom of current message."
+  (interactive)
+  (let ((rmail-show-message-hook
+        (list (function (lambda ()
+                          (goto-char (point-max))
+                          (recenter (1- (window-height))))))))
+    (rmail-show-message rmail-current-message)))
+
+(defun rmail-unknown-mail-followup-to ()
+  "Handle a \"Mail-Followup-To\" header field with an unknown mailing list.
+Ask the user whether to add that list name to `mail-mailing-lists'."
+   (save-restriction
+     (rmail-narrow-to-non-pruned-header)
+     (let ((mail-followup-to (mail-fetch-field "mail-followup-to" nil t)))
+       (when mail-followup-to
+        (let ((addresses
+               (split-string
+                (mail-strip-quoted-names mail-followup-to)
+                ",[[:space:]]+" t)))
+          (dolist (addr addresses)
+            (when (and (not (member addr mail-mailing-lists))
+                       (not
+                        ;; taken from rmailsum.el
+                        (string-match
+                         (or rmail-user-mail-address-regexp
+                             (concat "^\\("
+                                     (regexp-quote (user-login-name))
+                                     "\\($\\|@\\)\\|"
+                                     (regexp-quote
+                                      (or user-mail-address
+                                          (concat (user-login-name) "@"
+                                                  (or mail-host-address
+                                                      (system-name)))))
+                                     "\\>\\)"))
+                         addr))
+                       (y-or-n-p
+                        (format "Add `%s' to `mail-mailing-lists'? "
+                                addr)))
+              (customize-save-variable 'mail-mailing-lists
+                                       (cons addr mail-mailing-lists)))))))))
 
 (defun rmail-show-message (&optional n no-summary)
   "Show message number N (prefix argument), counting from start of file.
@@ -2021,7 +2690,7 @@ If summary buffer is currently displayed, update current message there also."
       (progn (narrow-to-region (point-min) (1- (point-max)))
             (goto-char (point-min))
             (setq mode-line-process nil))
-    (let (blurb)
+    (let (blurb coding-system)
       (if (not n)
          (setq n rmail-current-message)
        (cond ((<= n 0)
@@ -2037,20 +2706,49 @@ If summary buffer is currently displayed, update current message there also."
       (let ((beg (rmail-msgbeg n)))
        (goto-char beg)
        (forward-line 1)
+       (save-excursion
+         (let ((end (rmail-msgend n)))
+           (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))
+             (narrow-to-region beg (point))
+             (goto-char (point-min))
+             (if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
+                 (let ((coding-system (intern (match-string 1))))
+                   (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)
-       ;; Reformat the header, or else find the reformatted header.
        (let ((end (rmail-msgend n)))
+         ;; Reformat the header, or else find the reformatted header.
          (if (= (following-char) ?0)
              (rmail-reformat-message beg end)
            (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)
-         (setq rmail-view-buffer rmail-buffer)
-         )
+         (setq rmail-view-buffer rmail-buffer))
+       (when mail-mailing-lists
+         (rmail-unknown-mail-followup-to))
        (rmail-highlight-headers)
        (if transient-mark-mode (deactivate-mark))
        (run-hooks 'rmail-show-message-hook)
@@ -2063,9 +2761,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.
@@ -2109,10 +2876,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)))
 
@@ -2120,7 +2924,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.
@@ -2129,6 +2933,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))
@@ -2190,22 +2995,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)
@@ -2234,6 +3056,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))
@@ -2249,28 +3072,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)
@@ -2332,9 +3157,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)
@@ -2386,6 +3211,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)))
@@ -2438,9 +3264,20 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
        (setq i (1+ i)))
       newnum)))
 
-(defun rmail-only-expunge ()
+(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 (&optional dont-show)
   "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)
@@ -2451,7 +3288,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.
@@ -2513,17 +3353,20 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
       (message "Expunging deleted messages...done")
       (if (not win)
          (narrow-to-region (- (buffer-size) omin) (- (buffer-size) omax)))
-      (rmail-show-message
-       (if (zerop rmail-current-message) 1 nil))
-      (goto-char (+ (point) opoint)))))
+      (if (not dont-show)
+         (rmail-show-message
+          (if (zerop rmail-current-message) 1 nil)))
+      (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 ***
 
@@ -2538,7 +3381,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 rmail-mail-new-frame
          (prog1
              (compose-mail to subject others
                            noerase 'switch-to-buffer-other-frame
@@ -2574,27 +3417,31 @@ 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)
+             reply-to (or (mail-fetch-field "mail-reply-to" nil t)
+                          (mail-fetch-field "reply-to" nil t)
                           from)
-             cc (and (not just-sender)
-                     (mail-fetch-field "cc" nil t))
              subject (mail-fetch-field "subject")
              date (mail-fetch-field "date")
-             to (or (mail-fetch-field "to" nil t) "")
              message-id (mail-fetch-field "message-id")
              references (mail-fetch-field "references" nil nil t)
              resent-reply-to (mail-fetch-field "resent-reply-to" nil t)
@@ -2604,7 +3451,16 @@ use \\[mail-yank-original] to yank the original message into it."
 ;;;          resent-subject (mail-fetch-field "resent-subject")
 ;;;          resent-date (mail-fetch-field "resent-date")
 ;;;          resent-message-id (mail-fetch-field "resent-message-id")
-             )))
+             )
+       (unless just-sender
+         (if (mail-fetch-field "mail-followup-to" nil t)
+             ;; If this header field is present, use it instead of the To and CC fields.
+             (setq to (mail-fetch-field "mail-followup-to" nil t))
+           (setq cc (or (mail-fetch-field "cc" nil t) "")
+                 to (or (mail-fetch-field "to" nil t) ""))))
+
+       ))
+
     ;; Merge the resent-to and resent-cc into the to and cc.
     (if (and resent-to (not (equal resent-to "")))
        (if (not (equal to ""))
@@ -2628,7 +3484,12 @@ use \\[mail-yank-original] to yank the original message into it."
      ;; since they can handle the names unstripped.
      ;; I don't know whether there are other mailers that still
      ;; need the names to be stripped.
-     (mail-strip-quoted-names reply-to)
+;;;     (mail-strip-quoted-names reply-to)
+     ;; Remove unwanted names from reply-to, since Mail-Followup-To
+     ;; header causes all the names in it to wind up in reply-to, not
+     ;; in cc.  But if what's left is an empty list, use the original.
+     (let* ((reply-to-list (rmail-dont-reply-to reply-to)))
+       (if (string= reply-to-list "") reply-to reply-to-list))
      subject
      (rmail-make-in-reply-to-field from date message-id)
      (if just-sender
@@ -2641,8 +3502,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 " ")
@@ -2724,7 +3586,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")
@@ -2738,7 +3600,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
@@ -2749,24 +3612,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)
@@ -2782,15 +3648,21 @@ 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)))
+       (mail-personal-alias-file
+        (or mail-alias-file mail-personal-alias-file))
+       (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.
@@ -2827,6 +3699,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)
@@ -2851,17 +3725,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 *$"
@@ -2881,36 +3759,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")
@@ -2925,11 +3802,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)
@@ -2942,10 +3820,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)
@@ -2957,15 +3835,18 @@ 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:")
-           (mail-send-delimit-header)
+           (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"))
@@ -2974,9 +3855,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.
@@ -3019,7 +3898,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))))
@@ -3029,7 +3908,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))
@@ -3061,20 +3939,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 is 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.
@@ -3106,7 +4009,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))
@@ -3127,7 +4030,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
@@ -3144,7 +4047,76 @@ 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-remote-password (password)
+  "Set PASSWORD to be used for retrieving mail from a POP or IMAP server."
+  (interactive "sPassword: ")
+  (if password
+      (setq rmail-encoded-remote-password
+           (rmail-encode-string password (emacs-pid)))
+    (setq rmail-remote-password nil)
+    (setq rmail-encoded-remote-password nil)))
+
+(defun rmail-get-remote-password (imap)
+  "Get the password for retrieving mail from a POP or IMAP server.  If none
+has been set, then prompt the user for one."
+  (when (not rmail-encoded-remote-password)
+    (if (not rmail-remote-password)
+       (setq rmail-remote-password
+             (read-passwd (if imap
+                              "IMAP password: "
+                            "POP password: "))))
+    (rmail-set-remote-password rmail-remote-password)
+    (setq rmail-remote-password nil))
+  (rmail-encode-string rmail-encoded-remote-password (emacs-pid)))
+
+(defun rmail-have-password ()
+  (or rmail-remote-password rmail-encoded-remote-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)))
+
+;;;;  Desktop support
+
+(defun rmail-restore-desktop-buffer (desktop-buffer-file-name
+                                     desktop-buffer-name
+                                     desktop-buffer-misc)
+  "Restore an rmail buffer specified in a desktop file."
+  (condition-case error
+      (progn
+        (rmail-input desktop-buffer-file-name)
+        (if (eq major-mode 'rmail-mode)
+            (current-buffer)
+          rmail-buffer))
+    (file-locked
+      (kill-buffer (current-buffer))
+      nil)))
+
+(add-to-list 'desktop-buffer-mode-handlers
+            '(rmail-mode . rmail-restore-desktop-buffer))
 
 (provide 'rmail)
 
+;;; arch-tag: cff0a950-57fe-4f73-a86e-91ff75afd06c
 ;;; rmail.el ends here