Revision: miles@gnu.org--gnu-2005/emacs--unicode--0--patch-13
[bpt/emacs.git] / lisp / mail / rmail.el
index 4a13031..1b04c7a 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,98,2000, 2001
+;; 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/"
@@ -88,6 +91,9 @@
   :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."
   :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.
@@ -113,13 +150,14 @@ or `-k' to enable Kerberos authentication."
   :group 'rmail-retrieve
   :version "20.3")
 
-(defvar rmail-pop-password-error "invalid usercode or password\\|
-unknown user name or bad 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,
@@ -127,20 +165,81 @@ rather than deleted, after it is retrieved."
   :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 by executing it with --version
+command line option 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 flavor of `movemail' program in use. If
+rmail-movemail-program is set, use it. Otherwise, look for `movemail'
+in the path constructed by appending rmail-movemail-search-path,
+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
@@ -149,7 +248,7 @@ It is useful to set this variable in the site customization file.")
          "\\|^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:"
+         "\\|^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:"
@@ -158,7 +257,9 @@ It is useful to set this variable in the site customization file.")
          "\\|^list-id:\\|^list-unsubscribe:\\|^list-archive:"
          "\\|^content-type:\\|^content-length:"
          "\\|^x-attribution:\\|^x-disclaimer:\\|^x-trace:"
-         "\\|^x-complaints-to:\\|^nntp-posting-date:\\|^user-agent:")
+         "\\|^x-complaints-to:\\|^nntp-posting-date:\\|^user-agent"
+         "\\|^importance:\\|^envelope-to:\\|^delivery-date"
+         "\\|^x.*-priority:\\|^x-mimeole:")
   "*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,
@@ -207,7 +308,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
@@ -222,7 +323,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)
 
@@ -258,7 +361,7 @@ and the value of the environment variable MAIL overrides it)."
 (defcustom rmail-show-message-hook nil
   "List of functions to call when Rmail displays a message."
   :type 'hook
-  :options '(goto-addr)
+  :options '(goto-address)
   :group 'rmail)
 
 ;;;###autoload
@@ -276,9 +379,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.
@@ -312,7 +415,7 @@ examples:
   :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.")
 
@@ -417,6 +520,9 @@ 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.
@@ -426,7 +532,8 @@ 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' is non-nil.
+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.")
@@ -450,7 +557,7 @@ MSG is the message number, REGEXP is the regular expression.")
 (defvar rmail-search-mime-header-function nil
   "Function to check if a regexp matches a header of MIME message.
 This function is called if `rmail-enable-mime' is non-nil.
-It is called with four arguments MSG, REGEXP, and LIMIT, where
+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.")
@@ -473,7 +580,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.")
 
@@ -535,11 +642,13 @@ 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)
            '("^\\(To\\|Apparently-To\\|Cc\\|Newsgroups\\):"
@@ -553,7 +662,7 @@ The first parenthesized expression should match the MIME-charset name.")
               (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\\):.*$"
+           '("^\\(X-[a-z0-9-]+\\|In-reply-to\\|Date\\):.*\\(\n[ \t]+.*\\)*$"
              . font-lock-string-face))))
   "Additional expressions to highlight in Rmail mode.")
 
@@ -618,12 +727,14 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
                   (list (read-file-name "Run rmail on RMAIL file: "))))
   (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
@@ -648,7 +759,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)
@@ -754,7 +865,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 ()
@@ -770,11 +881,21 @@ Note:    it means the file has no messages in it.\n\^_")))
     (setq to (point))
     (unless (and coding-system
                 (coding-system-p coding-system))
-      (setq coding-system (detect-coding-region from to t)))
-    (unless (memq coding-system
-                 '(undecided undecided-unix))
+      (setq coding-system
+           ;; If rmail-file-coding-system is nil, Emacs 21 writes
+           ;; RMAIL files in emacs-mule, Emacs 22 in utf-8, but
+           ;; earlier versions did that with the current buffer's
+           ;; encoding.  So we want to favor detection of emacs-mule
+           ;; (whose normal priority is quite low) and utf-8, but
+           ;; still allow detection of other encodings if they won't
+           ;; fit.  The call to with-coding-priority below achieves
+           ;; that.
+           (with-coding-priority '(emacs-mule utf-8)
+             (detect-coding-region from to 'highest))))
+    (unless (eq (coding-system-type coding-system) 'undecided)
       (set-buffer-modified-p t)                ; avoid locking when decoding
-      (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)
@@ -1102,7 +1223,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)
@@ -1114,7 +1235,9 @@ 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)
@@ -1250,8 +1373,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))
@@ -1259,10 +1382,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)
@@ -1271,7 +1394,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)
 
@@ -1280,13 +1403,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))))
 
@@ -1341,6 +1464,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.
@@ -1423,11 +1547,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))
@@ -1437,6 +1612,40 @@ 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.
+"
+  (if (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)))
+    (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.)
@@ -1445,10 +1654,15 @@ 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
@@ -1456,7 +1670,12 @@ It returns t if it got any new messages."
       (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
@@ -1481,18 +1700,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))
@@ -1524,50 +1732,59 @@ It returns t if it got any new messages."
                     (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).
@@ -1591,7 +1808,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 ()
@@ -1629,12 +1854,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)
@@ -1650,7 +1933,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))
@@ -1675,7 +1960,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")
@@ -1691,6 +1978,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
@@ -1721,24 +2013,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
@@ -1746,6 +2060,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
@@ -1767,7 +2085,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
@@ -1777,45 +2097,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]")
-          (let ((byte (rmail-hex-string-to-integer
-                       (buffer-substring (point) (+ 2 (point))))))
-            (delete-region (1- (point)) (+ 2 (point)))
-            (insert byte)))
-         ((looking-at "=")
-          (delete-char 1))
-         (t
-          (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
@@ -1850,7 +2131,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.
@@ -1875,8 +2156,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)
@@ -1946,80 +2227,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")
-  (switch-to-buffer rmail-buffer)
-  (let* ((buffer-read-only nil)
-        (pruned (rmail-msg-is-pruned))
+  (let* ((pruned (with-current-buffer rmail-buffer
+                  (rmail-msg-is-pruned)))
         (prune (if arg
                    (> (prefix-numeric-value arg) 0)
                  (not pruned))))
     (if (eq pruned prune)
        t
+      (set-buffer rmail-buffer)
       (rmail-maybe-set-message-counters)
-      (let* ((window (get-buffer-window (current-buffer)))
-            (at-point-min (= (point) (point-min)))
-            (all-headers-visible (= (window-start window) (point-min)))
-            (on-header (save-excursion
-                         (and (not (search-backward "\n\n" nil t))
-                              (progn
-                                (end-of-line)
-                                (re-search-backward "^[-A-Za-z0-9]+:" nil t))
-                              (match-string 0))))
-            (old-screen-line
-             (rmail-count-screen-lines (window-start window) (point))))
-        (save-excursion
-         (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+      (if rmail-enable-mime
+         (let ((buffer-read-only nil))
+           (if pruned
+               (rmail-msg-restore-non-pruned-header)
+             (rmail-msg-prune-header))
+           (funcall rmail-show-mime-function))
+       (let* ((buffer-read-only nil)
+              (window (get-buffer-window (current-buffer)))
+              (at-point-min (= (point) (point-min)))
+              (all-headers-visible (= (window-start window) (point-min)))
+              (on-header
+               (save-excursion
+                 (and (not (search-backward "\n\n" nil t))
+                      (progn
+                        (end-of-line)
+                        (re-search-backward "^[-A-Za-z0-9]+:" nil t))
+                      (match-string 0))))
+              (old-screen-line
+               (rmail-count-screen-lines (window-start window) (point))))
          (if pruned
-             (let (new-start)
-               (goto-char (point-min))
-               (forward-line 1)
-               ;; Change 1 to 0.
-               (delete-char 1)
-               (insert ?0)
-               ;; Insert new EOOH line at the proper place.
-               (forward-line 1)
-               (let ((case-fold-search t))
-                 (while (looking-at "Summary-Line:\\|Mail-From:")
-                   (forward-line 1)))
-               (insert "*** EOOH ***\n")
-               (setq new-start (point))
-               ;; Delete the old reformatted header.
-               (forward-char -1)
-               (search-forward "\n*** EOOH ***\n")
-               (forward-line -1)
-               (let ((start (point)))
-                 (search-forward "\n\n")
-                 (delete-region start (point)))
-               ;; Narrow to after the new EOOH line.
-               (narrow-to-region new-start (point-max)))
-           (rmail-reformat-message (point-min) (point-max))))
-       (cond (rmail-enable-mime
-              (funcall rmail-show-mime-function))
-             (at-point-min
-              (goto-char (point-min)))
-             (on-header
-              (goto-char (point-min))
-              (search-forward "\n\n")
-              (or (re-search-backward (concat "^" (regexp-quote on-header)) nil t)
-                  (goto-char (point-min))))
-             (t
-              (save-selected-window
-                (select-window window)
-                (recenter old-screen-line)
-                (if (and all-headers-visible
-                         (not (= (window-start) (point-min))))
-                    (let ((lines-offscreen (rmail-count-screen-lines
-                                            (point-min)
-                                            (window-start window))))
-                      (recenter (min (+ old-screen-line lines-offscreen)
-                                     ;; last line of window
-                                     (- (window-height) 2)))))))))
+             (rmail-msg-restore-non-pruned-header)
+           (rmail-msg-prune-header))
+         (cond (at-point-min
+                (goto-char (point-min)))
+               (on-header
+                (goto-char (point-min))
+                (search-forward "\n\n")
+                (or (re-search-backward
+                     (concat "^" (regexp-quote on-header)) nil t)
+                    (goto-char (point-min))))
+               (t
+                (save-selected-window
+                  (select-window window)
+                  (recenter old-screen-line)
+                  (if (and all-headers-visible
+                           (not (= (window-start) (point-min))))
+                      (recenter (- (window-height) 2))))))))
       (rmail-highlight-headers))))
 
+(defun rmail-narrow-to-non-pruned-header ()
+  "Narrow to the whole (original) header of the current message."
+  (let (start end)
+    (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+    (goto-char (point-min))
+    (forward-line 1)
+    (if (= (following-char) ?1)
+       (progn
+         (forward-line 1)
+         (setq start (point))
+         (search-forward "*** EOOH ***\n")
+         (setq end (match-beginning 0)))
+      (forward-line 2)
+      (setq start (point))
+      (search-forward "\n\n")
+      (setq end (1- (point))))
+    (narrow-to-region start end)
+    (goto-char start)))
+
 ;; Lifted from repos-count-screen-lines.
 ;; Return number of screen lines between START and END.
 (defun rmail-count-screen-lines (start end)
@@ -2252,7 +2574,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)
@@ -2331,8 +2653,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)
@@ -2343,6 +2669,11 @@ If summary buffer is currently displayed, update current message there also."
            (search-forward "\n*** EOOH ***\n" end t)
            (narrow-to-region (point) end)))
        (goto-char (point-min))
+       (walk-windows
+        (function (lambda (window)
+                    (if (eq (window-buffer window) (current-buffer))
+                        (set-window-point window (point)))))
+        nil t)
        (rmail-display-labels)
        (if (eq rmail-enable-mime t)
            (funcall rmail-show-mime-function)
@@ -2386,43 +2717,51 @@ iso-8859, koi8-r, etc."
     (or (eq major-mode 'rmail-mode)
        (switch-to-buffer rmail-buffer))
     (save-excursion
-      (unwind-protect
-         (let ((msgbeg (rmail-msgbeg rmail-current-message))
-               (msgend (rmail-msgend rmail-current-message))
-               x-coding-header)
-           (narrow-to-region msgbeg msgend)
-           (goto-char (point-min))
-           (when (search-forward "\n*** EOOH ***\n" (point-max) t)
-             (narrow-to-region msgbeg (point)))
-           (goto-char (point-min))
-           (if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
-               (let ((old-coding (intern (match-string 1)))
-                     (buffer-read-only nil))
-                 (check-coding-system old-coding)
-                 ;; Make sure the new coding system uses the same EOL
-                 ;; conversion, to prevent ^M characters from popping
-                 ;; up all over the place.
-                 (setq coding
-                       (coding-system-change-eol-conversion
-                        coding
-                        (coding-system-eol-type old-coding)))
-                 (setq x-coding-header (point-marker))
-                 (narrow-to-region msgbeg msgend)
-                 (encode-coding-region (point) msgend old-coding)
-                 (decode-coding-region (point) msgend coding)
-                 (setq last-coding-system-used coding)
-                 ;; Rewrite the coding-system header according
-                 ;; to what we did.
-                 (goto-char x-coding-header)
-                 (delete-region (point)
-                                (save-excursion
-                                  (beginning-of-line)
-                                  (point)))
-                 (insert "X-Coding-System: "
-                         (symbol-name last-coding-system-used))
-                 (set-marker x-coding-header nil)
-                 (rmail-show-message))
-             (error "No X-Coding-System header found")))))))
+      (let ((pruned (rmail-msg-is-pruned)))
+       (unwind-protect
+           (let ((msgbeg (rmail-msgbeg rmail-current-message))
+                 (msgend (rmail-msgend rmail-current-message))
+                 x-coding-header)
+             ;; We need the message headers pruned (we later restore
+             ;; the pruned stat to what it was, see the end of
+             ;; unwind-protect form).
+             (or pruned
+                 (rmail-toggle-header 1))
+             (narrow-to-region msgbeg msgend)
+             (goto-char (point-min))
+             (when (search-forward "\n*** EOOH ***\n" (point-max) t)
+               (narrow-to-region msgbeg (point)))
+             (goto-char (point-min))
+             (if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
+                 (let ((old-coding (intern (match-string 1)))
+                       (buffer-read-only nil))
+                   (check-coding-system old-coding)
+                   ;; Make sure the new coding system uses the same EOL
+                   ;; conversion, to prevent ^M characters from popping
+                   ;; up all over the place.
+                   (setq coding
+                         (coding-system-change-eol-conversion
+                          coding
+                          (coding-system-eol-type old-coding)))
+                   (setq x-coding-header (point-marker))
+                   (narrow-to-region msgbeg msgend)
+                   (encode-coding-region (point) msgend old-coding)
+                   (decode-coding-region (point) msgend coding)
+                   (setq last-coding-system-used coding)
+                   ;; Rewrite the coding-system header according
+                   ;; to what we did.
+                   (goto-char x-coding-header)
+                   (delete-region (point)
+                                  (save-excursion
+                                    (beginning-of-line)
+                                    (point)))
+                   (insert "X-Coding-System: "
+                           (symbol-name last-coding-system-used))
+                   (set-marker x-coding-header nil)
+                   (rmail-show-message))
+               (error "No X-Coding-System header found")))
+         (or pruned
+             (rmail-toggle-header 0)))))))
 
 ;; Find all occurrences of certain fields, and highlight them.
 (defun rmail-highlight-headers ()
@@ -2515,7 +2854,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.
@@ -2602,7 +2941,7 @@ or forward if N is negative."
                (forward-line 1))
              (setq beg (point))
              (narrow-to-region (point) end))
-           (progn 
+           (progn
              (rfc822-goto-eoh)
              (setq end (point)))
          (setq beg (point))
@@ -2865,7 +3204,7 @@ See also user-option `rmail-confirm-expunge'."
       (funcall rmail-confirm-expunge
               "Erase deleted messages from Rmail file? ")))
 
-(defun rmail-only-expunge ()
+(defun rmail-only-expunge (&optional dont-show)
   "Actually erase all deleted messages in the file."
   (interactive)
   (set-buffer rmail-buffer)
@@ -2944,11 +3283,12 @@ See also user-option `rmail-confirm-expunge'."
       (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))
-      (if rmail-enable-mime
-         (goto-char (+ (point-min) opoint))
-       (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."
@@ -2971,7 +3311,7 @@ See also user-option `rmail-confirm-expunge'."
        (compose-mail to subject others
                      noerase nil
                      yank-action sendactions)
-      (if (and (display-multi-frame-p) rmail-mail-new-frame)
+      (if rmail-mail-new-frame
          (prog1
              (compose-mail to subject others
                            noerase 'switch-to-buffer-other-frame
@@ -3007,11 +3347,12 @@ use \\[mail-yank-original] to yank the original message into it."
             (msgnum rmail-current-message))
     (save-excursion
       (save-restriction
-       ;; If rmail-enable-mime is non-nil, we are in a
-       ;; rmail-view-buffer which doesn't contain any lines specific
-       ;; to BABYL format (e.g. "*** EOOH ***").  Thus, there's no
-       ;; need of narrowing in such a case.
-       (unless rmail-enable-mime
+       (if rmail-enable-mime
+           (narrow-to-region
+            (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)
@@ -3066,7 +3407,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
@@ -3189,7 +3531,7 @@ see the documentation of `rmail-resend'."
          (save-excursion
            ;; Insert after header separator--before signature if any.
            (goto-char (mail-text-start))
-           (if rmail-enable-mime
+           (if (or rmail-enable-mime rmail-enable-mime-composing)
                (funcall rmail-insert-mime-forwarded-message-function
                         forward-buffer)
              (insert "------- Start of forwarded message -------\n")
@@ -3231,6 +3573,8 @@ typically for purposes of moderating a list."
   (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
@@ -3274,6 +3618,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)
@@ -3332,115 +3678,103 @@ 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)
-    (unwind-protect
-       (progn
-         (save-excursion
-           ;; Un-prune the header; we need to search the whole thing.
-           (if pruned
-               (rmail-toggle-header 0))
-           (goto-char (rmail-msgbeg msgnum))
-           (let* ((case-fold-search t)
-                  (top (point))
-                  (content-type
-                   (save-restriction
-                     ;; Fetch any content-type header in current message
-                     (search-forward "\n\n") (narrow-to-region top (point))
-                     (mail-fetch-field "Content-Type") )) )
-             ;; Handle MIME multipart bounce messages
-             (if (and content-type 
-                      (string-match 
-                       ";[\n\t ]*boundary=\"?\\([-0-9a-z'()+_,./:=? ]+\\)\"?" 
-                       content-type))
-                 (let ((codestring
-                        (concat "\n--"
-                                (substring content-type (match-beginning 1) 
-                                           (match-end 1)))))
-                   (unless (re-search-forward mail-mime-unsent-header nil t)
-                     (error "Cannot find beginning of header in failed message"))
-                   (unless (search-forward "\n\n" nil t)
-                     (error "Cannot find start of Mime data in failed message"))
-                   (setq bounce-start (point))
-                   (if (search-forward codestring nil t)
-                       (setq bounce-end (match-beginning 0))
-                     (setq bounce-end (point-max)))
-                   )
-               ;; non-MIME bounce
-               (or (re-search-forward mail-unsent-separator nil t)
-                   (error "Cannot parse this as a failure message"))
-               (skip-chars-forward "\n")
-               ;; Support a style of failure message in which the original
-               ;; message is indented, and included within lines saying
-               ;; `Start of returned message' and `End of returned message'.
-               (if (looking-at " +Received:")
-                   (progn
-                     (setq bounce-start (point))
-                     (skip-chars-forward " ")
-                     (setq bounce-indent (- (current-column)))
-                     (goto-char (point-max))
-                     (re-search-backward "^End of returned message$" nil t)
-                     (setq bounce-end (point)))
-                 ;; One message contained a few random lines before
-                 ;; the old message header.  The first line of the
-                 ;; message started with two hyphens.  A blank line
-                 ;; followed these random lines.  The same line
-                 ;; beginning with two hyphens was possibly marking
-                 ;; the end of the message.
-                 (if (looking-at "^--")
-                     (let ((boundary (buffer-substring-no-properties
-                                      (point)
-                                      (progn (end-of-line) (point)))))
-                       (search-forward "\n\n")
-                       (skip-chars-forward "\n")
-                       (setq bounce-start (point))
-                       (goto-char (point-max))
-                       (search-backward (concat "\n\n" boundary) bounce-start t)
-                       (setq bounce-end (point)))
-                   (setq bounce-start (point)
-                         bounce-end (point-max)))
-                 (unless (search-forward "\n\n" nil t)
-                   (error "Cannot find end of header in failed message"))
-                 ))))
-         ;; Start sending new message; default header fields from original.
-         ;; Turn off the usual actions for initializing the message body
-         ;; because we want to get only the text from the failure message.
-         (let (mail-signature mail-setup-hook)
-           (if (rmail-start-mail nil nil nil nil nil rmail-this-buffer
-                                 (list (list 'rmail-mark-message
-                                             rmail-this-buffer
-                                             (aref rmail-msgref-vector msgnum)
-                                             "retried")))
-               ;; Insert original text as initial text of new draft message.
-               ;; Bind inhibit-read-only since the header delimiter
-               ;; of the previous message was probably read-only.
-               (let ((inhibit-read-only t)
-                     rmail-displayed-headers
-                     rmail-ignored-headers)
-                 (erase-buffer)
-                 (insert-buffer-substring rmail-this-buffer
-                                          bounce-start bounce-end)
-                 (goto-char (point-min))
-                 (if bounce-indent
-                     (indent-rigidly (point-min) (point-max) bounce-indent))
-                 (rmail-clear-headers rmail-retry-ignored-headers)
-                 (rmail-clear-headers "^sender:\\|^return-path:\\|^received:")
-                 (mail-sendmail-delimit-header)
-                 (save-restriction
-                   (narrow-to-region (point-min) (mail-header-end))
-                   (setq resending (mail-fetch-field "resent-to"))
-                   (if mail-self-blind
-                       (if resending
-                           (insert "Resent-Bcc: " (user-login-name) "\n")
-                         (insert "BCC: " (user-login-name) "\n"))))
-                 (goto-char (point-min))
-                 (mail-position-on-field (if resending "Resent-To" "To") t)))))
-      ;; save-window-excursion is needed because of the switch-to-buffer
-      ;; in rmail-toggle-header.
-      (save-window-excursion
-       (with-current-buffer rmail-this-buffer
-         (if pruned
-             (rmail-toggle-header 1)))))))
+       bounce-start bounce-end bounce-indent resending
+       ;; Fetch any content-type header in current message
+       ;; Must search thru the whole unpruned header.
+       (content-type
+        (save-excursion
+          (save-restriction
+            (rmail-narrow-to-non-pruned-header)
+            (mail-fetch-field "Content-Type") ))))
+    (save-excursion
+      (goto-char (point-min))
+      (let ((case-fold-search t))
+       (if (and content-type
+                (string-match
+                 ";[\n\t ]*boundary=\"?\\([-0-9a-z'()+_,./:=? ]+\\)\"?"
+                 content-type))
+           ;; Handle a MIME multipart bounce message.
+           (let ((codestring
+                  (concat "\n--"
+                          (substring content-type (match-beginning 1)
+                                     (match-end 1)))))
+             (unless (re-search-forward mail-mime-unsent-header nil t)
+               (error "Cannot find beginning of header in failed message"))
+             (unless (search-forward "\n\n" nil t)
+               (error "Cannot find start of Mime data in failed message"))
+             (setq bounce-start (point))
+             (if (search-forward codestring nil t)
+                 (setq bounce-end (match-beginning 0))
+               (setq bounce-end (point-max))))
+         ;; Non-MIME bounce.
+         (or (re-search-forward mail-unsent-separator nil t)
+             (error "Cannot parse this as a failure message"))
+         (skip-chars-forward "\n")
+         ;; Support a style of failure message in which the original
+         ;; message is indented, and included within lines saying
+         ;; `Start of returned message' and `End of returned message'.
+         (if (looking-at " +Received:")
+             (progn
+               (setq bounce-start (point))
+               (skip-chars-forward " ")
+               (setq bounce-indent (- (current-column)))
+               (goto-char (point-max))
+               (re-search-backward "^End of returned message$" nil t)
+               (setq bounce-end (point)))
+           ;; One message contained a few random lines before
+           ;; the old message header.  The first line of the
+           ;; message started with two hyphens.  A blank line
+           ;; followed these random lines.  The same line
+           ;; beginning with two hyphens was possibly marking
+           ;; the end of the message.
+           (if (looking-at "^--")
+               (let ((boundary (buffer-substring-no-properties
+                                (point)
+                                (progn (end-of-line) (point)))))
+                 (search-forward "\n\n")
+                 (skip-chars-forward "\n")
+                 (setq bounce-start (point))
+                 (goto-char (point-max))
+                 (search-backward (concat "\n\n" boundary) bounce-start t)
+                 (setq bounce-end (point)))
+             (setq bounce-start (point)
+                   bounce-end (point-max)))
+           (unless (search-forward "\n\n" nil t)
+             (error "Cannot find end of header in failed message"))))))
+    ;; We have found the message that bounced, within the current message.
+    ;; Now start sending new message; default header fields from original.
+    ;; Turn off the usual actions for initializing the message body
+    ;; because we want to get only the text from the failure message.
+    (let (mail-signature mail-setup-hook)
+      (if (rmail-start-mail nil nil nil nil nil rmail-this-buffer
+                           (list (list 'rmail-mark-message
+                                       rmail-this-buffer
+                                       (aref rmail-msgref-vector msgnum)
+                                       "retried")))
+         ;; Insert original text as initial text of new draft message.
+         ;; Bind inhibit-read-only since the header delimiter
+         ;; of the previous message was probably read-only.
+         (let ((inhibit-read-only t)
+               rmail-displayed-headers
+               rmail-ignored-headers)
+           (erase-buffer)
+           (insert-buffer-substring rmail-this-buffer
+                                    bounce-start bounce-end)
+           (goto-char (point-min))
+           (if bounce-indent
+               (indent-rigidly (point-min) (point-max) bounce-indent))
+           (rmail-clear-headers rmail-retry-ignored-headers)
+           (rmail-clear-headers "^sender:\\|^return-path:\\|^received:")
+           (mail-sendmail-delimit-header)
+           (save-restriction
+             (narrow-to-region (point-min) (mail-header-end))
+             (setq resending (mail-fetch-field "resent-to"))
+             (if mail-self-blind
+                 (if resending
+                     (insert "Resent-Bcc: " (user-login-name) "\n")
+                   (insert "BCC: " (user-login-name) "\n"))))
+           (goto-char (point-min))
+           (mail-position-on-field (if resending "Resent-To" "To") t))))))
 \f
 (defun rmail-summary-exists ()
   "Non-nil iff in an RMAIL buffer and an associated summary buffer exists.
@@ -3483,7 +3817,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))))
@@ -3493,7 +3827,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))
@@ -3530,7 +3863,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.")
 
@@ -3639,27 +3972,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
@@ -3669,7 +4005,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)
@@ -3680,6 +4016,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