]> code.delx.au - gnu-emacs/blobdiff - lisp/mail/rmail.el
Replace `string-to-int' by `string-to-number'.
[gnu-emacs] / lisp / mail / rmail.el
index 2094ae4312eca8ad8dc6429ff6786b161d9c8d38..c68bb80d80f690c0c0465345776c9391e02221bd 100644 (file)
@@ -1,6 +1,6 @@
-;;; rmail.el --- main code of "RMAIL" mail reader for Emacs.
+;;; rmail.el --- main code of "RMAIL" mail reader for Emacs
 
-;; Copyright (C) 1985,86,87,88,93,94,95,96,97,1998
+;; Copyright (C) 1985,86,87,88,93,94,95,96,97,98,2000,01,2004,2005
 ;;             Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
@@ -23,6 +23,8 @@
 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 ;; Boston, MA 02111-1307, USA.
 
+;;; Commentary:
+
 ;;; Code:
 
 ;; Souped up by shane@mit-ajax based on ideas of rlk@athena.mit.edu
@@ -38,6 +40,7 @@
 ;;
 
 (require 'mail-utils)
+(eval-when-compile (require 'mule-util)) ; for detect-coding-with-priority
 
 ; These variables now declared in paths.el.
 ;(defvar rmail-spool-directory "/usr/spool/mail/"
   :prefix "rmail-"
   :group 'rmail)
 
+(defgroup rmail-edit nil
+  "Rmail editing."
+  :prefix "rmail-edit-"
+  :group 'rmail)
+
+(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 '(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.
@@ -108,37 +153,118 @@ 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-pop-password nil)
+(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 login name as an address
+*A regexp specifying addresses to prune from a reply message.
+A value of nil means exclude your own email address as an address
 plus whatever is specified by `rmail-default-dont-reply-to-names'."
   :type '(choice regexp (const :tag "Your Name" nil))
   :group 'rmail-reply)
 
 ;;;###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:\\|^x-coding-system:\\|^return-path:\\|^errors-to:\\|^return-receipt-to:\\|^x-attribution:\\|^x-disclaimer:"
+(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:\\|^mime-version:"
+         "\\|^content-transfer-encoding:\\|^x-coding-system:"
+         "\\|^return-path:\\|^errors-to:\\|^return-receipt-to:"
+         "\\|^x-sign:\\|^x-beenthere:\\|^x-mailman-version:\\|^x-mailman-copy:"
+         "\\|^precedence:\\|^list-help:\\|^list-post:\\|^list-subscribe:"
+         "\\|^list-id:\\|^list-unsubscribe:\\|^list-archive:"
+         "\\|^content-type:\\|^content-length:"
+         "\\|^x-attribution:\\|^x-disclaimer:\\|^x-trace:"
+         "\\|^x-complaints-to:\\|^nntp-posting-date:\\|^user-agent"
+         "\\|^importance:\\|^envelope-to:\\|^delivery-date"
+         "\\|^x.*-priority:\\|^x-mimeole:\\|^x-archive:"
+         "\\|^resent-face:\\|^resent-x.*:\\|^resent-organization\\|^resent-openpgp"
+         "\\|^openpgp:\\|^x-request-pgp:\\|^x-original.*:"
+         "\\|^x-virus-scanned:\\|^x-spam-[^s].*:")
   "*Regexp to match header fields that Rmail should normally hide.
 This variable is used for reformatting the message header,
 which normally happens once for each message,
@@ -158,7 +284,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)
@@ -187,7 +313,7 @@ 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
@@ -202,7 +328,9 @@ and the value of the environment variable MAIL overrides it)."
 
 ;;;###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)
 
@@ -217,6 +345,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.")
@@ -226,8 +363,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
@@ -240,9 +384,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.
@@ -251,6 +395,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.")
 
@@ -355,9 +525,47 @@ until a user explicitly requires it."
                 (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
@@ -377,7 +585,7 @@ the variable `rmail-mime-feature'.")
 
 ;;;###autoload
 (defvar rmail-mime-charset-pattern
-  "^content-type:[ ]*text/plain;[ \t\n]*charset=\"?\\([^ \t\n\"]+\\)\"?"
+  "^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.")
 
@@ -439,55 +647,75 @@ The first parenthesized expression should match the MIME-charset name.")
   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.
@@ -503,19 +731,16 @@ 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
-          (if existed 
+          (if existed
              (with-current-buffer existed enable-multibyte-characters)
             (default-value 'enable-multibyte-characters)))
         ;; Since the file may contain messages of different encodings
@@ -540,7 +765,7 @@ 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))))
-      (switch-to-buffer 
+      (switch-to-buffer
        (let ((enable-local-variables nil))
         (find-file-noselect file-name))))
     (if (eq major-mode 'rmail-edit-mode)
@@ -646,7 +871,7 @@ 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 ()
@@ -662,11 +887,21 @@ Note:    it means the file has no messages in it.\n\^_")))
     (setq to (point))
     (unless (and coding-system
                 (coding-system-p coding-system))
-      (setq coding-system (detect-coding-region from to t)))
+      (setq coding-system
+           ;; Emacs 21.1 and later writes RMAIL files in emacs-mule, but
+           ;; earlier versions did that with the current buffer's encoding.
+           ;; So we want to favor detection of emacs-mule (whose normal
+           ;; priority is quite low), but still allow detection of other
+           ;; encodings if emacs-mule won't fit.  The call to
+           ;; detect-coding-with-priority below achieves that.
+           (car (detect-coding-with-priority
+                 from to
+                 '((coding-category-emacs-mule . emacs-mule))))))
     (unless (memq coding-system
                  '(undecided undecided-unix))
       (set-buffer-modified-p t)                ; avoid locking when decoding
-      (decode-coding-region from to coding-system)
+      (let ((buffer-undo-list t))
+       (decode-coding-region from to coding-system))
       (setq coding-system last-coding-system-used))
     (set-buffer-modified-p modifiedp)
     (setq buffer-file-coding-system nil)
@@ -728,7 +963,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)
   )
@@ -905,10 +1140,23 @@ Instead, these commands are available:
 \\[rmail-summary-by-topic]   Summarize only messages with subject line regexp(s).
 \\[rmail-toggle-header]        Toggle display of complete header."
   (interactive)
-  (rmail-mode-2)
-  (rmail-set-message-counters)
-  (rmail-show-message rmail-total-messages)
-  (run-hooks 'rmail-mode-hook))
+  (let ((finding-rmail-file (not (eq major-mode 'rmail-mode))))
+    (rmail-mode-2)
+    (when (and finding-rmail-file
+              (null coding-system-for-read)
+              default-enable-multibyte-characters)
+      (let ((rmail-enable-multibyte t))
+       (rmail-require-mime-maybe)
+       (rmail-convert-file)
+       (goto-char (point-max))
+       (set-buffer-multibyte t)))
+    (rmail-set-message-counters)
+    (rmail-show-message rmail-total-messages)
+    (when finding-rmail-file
+      (when rmail-display-summary
+       (rmail-summary))
+      (rmail-construct-io-menu))
+    (run-hooks 'rmail-mode-hook)))
 
 (defun rmail-mode-2 ()
   (kill-all-local-variables)
@@ -981,7 +1229,7 @@ Instead, these commands are available:
   (make-local-variable 'font-lock-defaults)
   (setq font-lock-defaults
        '(rmail-font-lock-keywords
-         t nil nil nil
+         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)
@@ -993,10 +1241,13 @@ 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)
+  (set-buffer rmail-buffer)
   (let* ((revert-buffer-function (default-value 'revert-buffer-function))
         (rmail-enable-multibyte enable-multibyte-characters)
         ;; See similar code in `rmail'.
@@ -1006,7 +1257,8 @@ Instead, these commands are available:
        ;; If the user said "yes", and we changed something,
        ;; reparse the messages.
        (progn
-         (rmail-mode-2)
+         (set-buffer rmail-buffer)
+         (rmail-mode-2)
          ;; Convert all or part to Babyl file if possible.
          (rmail-convert-file)
          ;; We have read the file as raw-text, so the buffer is set to
@@ -1041,6 +1293,7 @@ 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))))
@@ -1056,9 +1309,17 @@ Hook `rmail-quit-hook' is run after expunging."
   (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."
@@ -1118,8 +1379,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))
@@ -1127,10 +1388,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)
@@ -1139,7 +1400,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)
 
@@ -1148,13 +1409,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))))
 
@@ -1194,6 +1455,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.
@@ -1208,6 +1470,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.
@@ -1290,11 +1553,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))
@@ -1304,6 +1618,56 @@ 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.)
@@ -1312,18 +1676,28 @@ It returns t if it got any new messages."
             (file-name-nondirectory buffer-file-name)))
   (let (file tofile delete-files movemail popmail got-password password)
     (while files
-      ;; Handle POP mailbox names specially; don't expand as filenames
+      ;; Handle remote mailbox names specially; don't expand as filenames
       ;; in case the userid contains a directory separator.
       (setq file (car files))
-      (setq popmail (string-match "^po:" file))
+      (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
-                   (expand-file-name (substitute-in-file-name file)))))
+                   (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
@@ -1332,15 +1706,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)))))
-      (if (and movemail (not popmail))
+      (if (not popmail)
          (progn
            ;; On some systems, /usr/spool/mail/foo is a directory
            ;; and the actual inbox is /usr/spool/mail/foo/foo.
@@ -1348,18 +1714,7 @@ It returns t if it got any new messages."
                (setq file (expand-file-name (user-login-name)
                                             file)))))
       (cond (popmail
-            (if rmail-pop-password-required
-                (progn (setq got-password (not (rmail-have-password)))
-                       (setq password (rmail-get-pop-password))))
-            (if (eq system-type 'windows-nt)
-                ;; cannot have "po:" in file name
-                (setq tofile
-                      (expand-file-name
-                       (concat ".newmail-pop-"
-                               (file-name-nondirectory (substring file 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))
@@ -1373,68 +1728,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 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)
-                      (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))
-                          (rmail-set-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).
@@ -1458,7 +1805,15 @@ It returns t if it got any new messages."
 (defun rmail-decode-region (from to coding)
   (if (or (not coding) (not (coding-system-p coding)))
       (setq coding 'undecided))
-  (decode-coding-region from to coding))
+  ;; 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 ()
@@ -1496,12 +1851,70 @@ It returns t if it got any new messages."
                              (save-excursion
                                (skip-chars-forward " \t\n")
                                (point)))
-              (setq last-coding-system-used nil)
-              (or rmail-enable-mime
-                  (not rmail-enable-multibyte)
-                  (decode-coding-region start (point)
-                                        (or rmail-file-coding-system
-                                            'undecided)))
+              (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)
@@ -1517,7 +1930,9 @@ It returns t if it got any new messages."
                     (insert "X-Coding-System: "
                             (symbol-name last-coding-system-used)
                             "\n")))
-              (narrow-to-region (point) (point-max)))
+              (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))
@@ -1542,7 +1957,9 @@ It returns t if it got any new messages."
                         (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")
@@ -1558,6 +1975,11 @@ It returns t if it got any new messages."
                         (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
@@ -1568,7 +1990,7 @@ It returns t if it got any new messages."
                                              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))
@@ -1588,24 +2010,46 @@ It returns t if it got any new messages."
                          (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
@@ -1613,6 +2057,10 @@ It returns t if it got any new messages."
                   (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
@@ -1634,7 +2082,9 @@ It returns t if it got any new messages."
                 (insert "X-Coding-System: "
                         (symbol-name last-coding-system-used)
                         "\n"))
-              (narrow-to-region (point) (point-max)))
+              (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
@@ -1644,46 +2094,6 @@ It returns t if it got any new messages."
              (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
@@ -1718,7 +2128,7 @@ It returns t if it got any new messages."
                      ""
                    (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.
@@ -1743,8 +2153,8 @@ It returns t if it got any new messages."
   (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)
@@ -1795,9 +2205,10 @@ Otherwise, delete all header fields whose names match `rmail-ignored-headers'."
        (or ignored-headers (setq ignored-headers rmail-ignored-headers))
        (save-restriction
          (narrow-to-region (point-min) (point))
-         (while (progn
-                  (goto-char (point-min))
-                  (re-search-forward ignored-headers nil t))
+         (while (and ignored-headers
+                     (progn
+                       (goto-char (point-min))
+                       (re-search-forward ignored-headers nil t)))
            (beginning-of-line)
            (delete-region (point)
                           (if (re-search-forward "\n[^ \t]" nil t)
@@ -1813,72 +2224,121 @@ Otherwise, delete all header fields whose names match `rmail-ignored-headers'."
       (forward-line 1)
       (= (following-char) ?1))))
 
+(defun rmail-msg-restore-non-pruned-header ()
+  (let ((old-point (point))
+       new-point
+       new-start
+       (inhibit-read-only t))
+    (save-excursion
+      (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+      (goto-char (point-min))
+      (forward-line 1)
+      ;; Change 1 to 0.
+      (delete-char 1)
+      (insert ?0)
+      ;; Insert new EOOH line at the proper place.
+      (forward-line 1)
+      (let ((case-fold-search t))
+       (while (looking-at "Summary-Line:\\|Mail-From:")
+         (forward-line 1)))
+      (insert "*** EOOH ***\n")
+      (setq new-start (point))
+      ;; Delete the old reformatted header.
+      (forward-char -1)
+      (search-forward "\n*** EOOH ***\n")
+      (forward-line -1)
+      (let ((start (point)))
+       (search-forward "\n\n")
+       (if (and (<= start old-point)
+                (<= old-point (point)))
+           (setq new-point new-start))
+       (delete-region start (point)))
+      ;; Narrow to after the new EOOH line.
+      (narrow-to-region new-start (point-max)))
+    (if new-point
+       (goto-char new-point))))
+
+(defun rmail-msg-prune-header ()
+  (let ((new-point
+        (= (point) (point-min))))
+    (save-excursion
+      (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+      (rmail-reformat-message (point-min) (point-max)))
+    (if new-point
+       (goto-char (point-min)))))
+
 (defun rmail-toggle-header (&optional arg)
   "Show original message header if pruned header currently shown, or vice versa.
 With argument ARG, show the message header pruned if ARG is greater than zero;
 otherwise, show it in full."
   (interactive "P")
-  (let* ((buffer-read-only nil)
-        (pruned (rmail-msg-is-pruned))
+  (let* ((pruned (with-current-buffer rmail-buffer
+                  (rmail-msg-is-pruned)))
         (prune (if arg
                    (> (prefix-numeric-value arg) 0)
                  (not pruned))))
     (if (eq pruned prune)
        t
+      (set-buffer rmail-buffer)
       (rmail-maybe-set-message-counters)
-      (let ((at-point-min (= (point) (point-min)))
-            (all-headers-visible (= (window-start) (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) (point))))
-        (save-excursion
-         (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+      (if rmail-enable-mime
+         (let ((buffer-read-only nil))
+           (if pruned
+               (rmail-msg-restore-non-pruned-header)
+             (rmail-msg-prune-header))
+           (funcall rmail-show-mime-function))
+       (let* ((buffer-read-only nil)
+              (window (get-buffer-window (current-buffer)))
+              (at-point-min (= (point) (point-min)))
+              (all-headers-visible (= (window-start window) (point-min)))
+              (on-header
+               (save-excursion
+                 (and (not (search-backward "\n\n" nil t))
+                      (progn
+                        (end-of-line)
+                        (re-search-backward "^[-A-Za-z0-9]+:" nil t))
+                      (match-string 0))))
+              (old-screen-line
+               (rmail-count-screen-lines (window-start window) (point))))
          (if pruned
-             (let (new-start)
-               (goto-char (point-min))
-               (forward-line 1)
-               ;; Change 1 to 0.
-               (delete-char 1)
-               (insert ?0)
-               ;; Insert new EOOH line at the proper place.
-               (forward-line 1)
-               (let ((case-fold-search t))
-                 (while (looking-at "Summary-Line:\\|Mail-From:")
-                   (forward-line 1)))
-               (insert "*** EOOH ***\n")
-               (setq new-start (point))
-               ;; Delete the old reformatted header.
-               (forward-char -1)
-               (search-forward "\n*** EOOH ***\n")
-               (forward-line -1)
-               (let ((start (point)))
-                 (search-forward "\n\n")
-                 (delete-region start (point)))
-               ;; Narrow to after the new EOOH line.
-               (narrow-to-region new-start (point-max)))
-           (rmail-reformat-message (point-min) (point-max))))
-       (cond (at-point-min
-              (goto-char (point-min)))
-             (on-header
-              (goto-char (point-min))
-              (search-forward "\n\n")
-              (or (re-search-backward (concat "^" (regexp-quote on-header)) nil t)
-                  (goto-char (point-min))))
-             (t
-              (recenter old-screen-line)
-              (if (and all-headers-visible
-                       (not (= (window-start) (point-min))))
-                  (let ((lines-offscreen (rmail-count-screen-lines
-                                          (point-min) (window-start))))
-                    (recenter (min (+ old-screen-line lines-offscreen)
-                                   ;; last line of window
-                                   (- (window-height) 2))))))))
+             (rmail-msg-restore-non-pruned-header)
+           (rmail-msg-prune-header))
+         (cond (at-point-min
+                (goto-char (point-min)))
+               (on-header
+                (goto-char (point-min))
+                (search-forward "\n\n")
+                (or (re-search-backward
+                     (concat "^" (regexp-quote on-header)) nil t)
+                    (goto-char (point-min))))
+               (t
+                (save-selected-window
+                  (select-window window)
+                  (recenter old-screen-line)
+                  (if (and all-headers-visible
+                           (not (= (window-start) (point-min))))
+                      (recenter (- (window-height) 2))))))))
       (rmail-highlight-headers))))
 
+(defun rmail-narrow-to-non-pruned-header ()
+  "Narrow to the whole (original) header of the current message."
+  (let (start end)
+    (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+    (goto-char (point-min))
+    (forward-line 1)
+    (if (= (following-char) ?1)
+       (progn
+         (forward-line 1)
+         (setq start (point))
+         (search-forward "*** EOOH ***\n")
+         (setq end (match-beginning 0)))
+      (forward-line 2)
+      (setq start (point))
+      (search-forward "\n\n")
+      (setq end (1- (point))))
+    (narrow-to-region start end)
+    (goto-char start)))
+
 ;; Lifted from repos-count-screen-lines.
 ;; Return number of screen lines between START and END.
 (defun rmail-count-screen-lines (start end)
@@ -1936,12 +2396,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))
@@ -1999,19 +2468,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
@@ -2096,7 +2563,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))
@@ -2104,7 +2571,7 @@ 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)
   (let ((start (point))
        next)
@@ -2139,6 +2606,39 @@ change the invisible header text."
   (interactive)
   (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.
 If summary buffer is currently displayed, update current message there also."
@@ -2183,8 +2683,12 @@ If summary buffer is currently displayed, update current message there also."
              (goto-char (point-min))
              (if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
                  (let ((coding-system (intern (match-string 1))))
-                   (check-coding-system coding-system)
-                   (setq buffer-file-coding-system coding-system))
+                   (condition-case nil
+                       (progn
+                         (check-coding-system coding-system)
+                         (setq buffer-file-coding-system coding-system))
+                     (error
+                      (setq buffer-file-coding-system nil))))
                (setq buffer-file-coding-system nil)))))
        ;; Clear the "unseen" attribute when we show a message.
        (rmail-set-attribute "unseen" nil)
@@ -2195,11 +2699,17 @@ If summary buffer is currently displayed, update current message there also."
            (search-forward "\n*** EOOH ***\n" end t)
            (narrow-to-region (point) end)))
        (goto-char (point-min))
+       (walk-windows
+        (function (lambda (window)
+                    (if (eq (window-buffer window) (current-buffer))
+                        (set-window-point window (point)))))
+        nil t)
        (rmail-display-labels)
        (if (eq rmail-enable-mime t)
            (funcall rmail-show-mime-function)
-         (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)
@@ -2212,9 +2722,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.
@@ -2258,10 +2837,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)))
 
@@ -2269,7 +2885,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.
@@ -2278,6 +2894,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))
@@ -2339,22 +2956,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)
@@ -2383,6 +3017,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))
@@ -2398,28 +3033,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-marker))
-           (rmail-show-message msg)
+                 (goto-char (point-max))
+                 (re-search-backward regexp nil 'move))
+             (goto-char (point-min))
+             (re-search-forward regexp nil t))
            (message "%sRmail search for %s...done"
                     (if reversep "Reverse " "")
-                    regexp)
-           (goto-char win))
+                    regexp))
        (goto-char opoint)
        (narrow-to-region omin omax)
        (ding)
@@ -2535,6 +3172,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)))
@@ -2587,9 +3225,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)
@@ -2600,7 +3249,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.
@@ -2662,17 +3314,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 ***
 
@@ -2687,7 +3342,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
@@ -2723,23 +3378,34 @@ 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 (if just-sender
+                              (mail-fetch-field "mail-reply-to" nil t)
+                            (mail-fetch-field "mail-followup-to" nil t))
+                          (mail-fetch-field "reply-to" nil t)
                           from)
              cc (and (not just-sender)
+                     ;; mail-followup-to, if given, overrides cc.
+                     (not (mail-fetch-field "mail-followup-to" nil t))
                      (mail-fetch-field "cc" nil t))
              subject (mail-fetch-field "subject")
              date (mail-fetch-field "date")
@@ -2777,7 +3443,8 @@ 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)
+     reply-to
      subject
      (rmail-make-in-reply-to-field from date message-id)
      (if just-sender
@@ -2790,8 +3457,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 " ")
@@ -2873,7 +3541,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")
@@ -2887,7 +3555,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
@@ -2898,24 +3567,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)
@@ -2931,14 +3603,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)
+       (mail-personal-alias-file
+        (or mail-alias-file mail-personal-alias-file))
        (mailbuf rmail-buffer))
     (unwind-protect
        (with-current-buffer tembuf
          ;;>> Copy message into temp buffer
-         (insert-buffer-substring mailbuf)
+         (if rmail-enable-mime
+             (funcall rmail-insert-mime-resent-message-function mailbuf)
+           (insert-buffer-substring mailbuf))
          (goto-char (point-min))
          ;; Delete any Sender field, since that's not specifiable.
          ; Only delete Sender fields in the actual header.
@@ -2975,6 +3654,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)
@@ -3013,7 +3694,7 @@ typically for purposes of moderating a list."
          "^ *---+ +Original message follows +---+ *$\\|"
          "^ *---+ +Your message follows +---+ *$\\|"
          "^|? *---+ +Message text follows: +---+ *|?$\\|"
-         "^ *---+ +This is a copy of the message, including all the headers.*---+ *$")
+         "^ *---+ +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 *$"
@@ -3033,39 +3714,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)
-       (pruned (rmail-msg-is-pruned))
-       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
-      (if pruned
-         (rmail-toggle-header 0))
-      (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")
@@ -3080,11 +3757,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)
@@ -3097,10 +3775,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)
@@ -3112,14 +3790,17 @@ specifying headers which should not be copied into the new message."
          ;; Insert original text as initial text of new draft message.
          ;; Bind inhibit-read-only since the header delimiter
          ;; of the previous message was probably read-only.
-         (let ((inhibit-read-only t))
+         (let ((inhibit-read-only t)
+               rmail-displayed-headers
+               rmail-ignored-headers)
            (erase-buffer)
-           (insert-buffer-substring rmail-this-buffer bounce-start bounce-end)
+           (insert-buffer-substring rmail-this-buffer
+                                    bounce-start bounce-end)
            (goto-char (point-min))
            (if bounce-indent
                (indent-rigidly (point-min) (point-max) bounce-indent))
            (rmail-clear-headers rmail-retry-ignored-headers)
-           (rmail-clear-headers "^sender:\\|^from:\\|^return-path:")
+           (rmail-clear-headers "^sender:\\|^return-path:\\|^received:")
            (mail-sendmail-delimit-header)
            (save-restriction
              (narrow-to-region (point-min) (mail-header-end))
@@ -3129,11 +3810,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))))
-    (if pruned
-       (rmail-toggle-header))))
+           (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.
@@ -3176,7 +3853,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))))
@@ -3186,7 +3863,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))
@@ -3223,7 +3899,7 @@ This has an effect only if a summary buffer exists."
 (eval-when-compile (require 'speedbar))
 
 (defvar rmail-speedbar-match-folder-regexp "^[A-Z0-9]+\\(\\.[A-Z0-9]+\\)?$"
-  "*This regex us used to match folder names to be displayed in speedbar.
+  "*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.")
 
@@ -3332,27 +4008,30 @@ TEXT and INDENT are not used."
 ; nor is it meant to be.
 
 ;;;###autoload
-(defun rmail-set-pop-password (password)
-  "Set PASSWORD to be used for retrieving mail from a POP server."
+(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-pop-password 
+      (setq rmail-encoded-remote-password
            (rmail-encode-string password (emacs-pid)))
-    (setq rmail-pop-password nil)
-    (setq rmail-encoded-pop-password nil)))
+    (setq rmail-remote-password nil)
+    (setq rmail-encoded-remote-password nil)))
 
-(defun rmail-get-pop-password ()
-  "Get the password for retrieving mail from a POP server.  If none
+(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."
-  (if (not rmail-encoded-pop-password)
-      (progn (if (not rmail-pop-password)
-                (setq rmail-pop-password (read-passwd "POP password: ")))
-            (rmail-set-pop-password rmail-pop-password)
-            (setq rmail-pop-password nil)))
-  (rmail-encode-string rmail-encoded-pop-password (emacs-pid)))
+  (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-pop-password rmail-encoded-pop-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
@@ -3362,7 +4041,7 @@ 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) 
+ (let* ((string-vector (string-to-vector string)) (i 0)
        (len (length string-vector)) (curmask mask) charmask)
    (while (< i len)
      (if (= curmask 0)
@@ -3373,6 +4052,24 @@ encoded string (and the same mask) will decode the string."
      (setq i (1+ i)))
    (concat string-vector)))
 
+;;;;  Desktop support
+
+;;;###autoload
+(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)))
+
 (provide 'rmail)
 
+;;; arch-tag: cff0a950-57fe-4f73-a86e-91ff75afd06c
 ;;; rmail.el ends here