]> code.delx.au - gnu-emacs/blobdiff - lisp/mail/rmail.el
(mail-unsent-separator): Handle "returned message follows".
[gnu-emacs] / lisp / mail / rmail.el
index 3e2d5eae86b0758ed4e8d969cdd686e094cd8092..972f7d004777b39153824fde26aef0473d2ef581 100644 (file)
@@ -1,6 +1,7 @@
 ;;; rmail.el --- main code of "RMAIL" mail reader for Emacs.
 
-;; Copyright (C) 1985,86,87,88,93,94,95,96 Free Software Foundation, Inc.
+;; Copyright (C) 1985,86,87,88,93,94,95,96,97,1998
+;;             Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: mail
 
 (require 'mail-utils)
 
-;; For Emacs V18 compatibility
-(and (not (fboundp 'buffer-disable-undo))
-     (fboundp 'buffer-flush-undo)
-     (defalias 'buffer-disable-undo 'buffer-flush-undo))
-
 ; These variables now declared in paths.el.
 ;(defvar rmail-spool-directory "/usr/spool/mail/"
 ;  "This is the name of the directory used by the system mailer for\n\
 ;  (expand-file-name "~/RMAIL")
 ;  "")
 
-(defvar rmail-movemail-program nil
-  "If non-nil, name of program for fetching new mail.")
-
-(defvar rmail-pop-password nil
-  "*Password to use when reading mail from a POP server, if required.")
-
-(defvar rmail-pop-password-required nil
-  "*Non-nil if a password is required when reading mail using POP.")
-
-(defvar rmail-preserve-inbox nil
+(defgroup rmail nil
+  "Mail reader for Emacs."
+  :group 'mail)
+
+(defgroup rmail-retrieve nil
+  "Rmail retrieval options."
+  :prefix "rmail-"
+  :group 'rmail)
+
+(defgroup rmail-files nil
+  "Rmail files."
+  :prefix "rmail-"
+  :group 'rmail)
+
+(defgroup rmail-headers nil
+  "Rmail header options."
+  :prefix "rmail-"
+  :group 'rmail)
+
+(defgroup rmail-reply nil
+  "Rmail reply options."
+  :prefix "rmail-"
+  :group 'rmail)
+
+(defgroup rmail-summary nil
+  "Rmail summary options."
+  :prefix "rmail-"
+  :prefix "rmail-summary-"
+  :group 'rmail)
+
+(defgroup rmail-output nil
+  "Output message to a file."
+  :prefix "rmail-output-"
+  :prefix "rmail-"
+  :group 'rmail)
+
+
+(defcustom rmail-movemail-program nil
+  "If non-nil, name of program for fetching new mail."
+  :group 'rmail-retrieve
+  :type 'string)
+
+(defcustom rmail-pop-password nil
+  "*Password to use when reading mail from a POP server, if required."
+  :type '(choice (string :tag "Password")
+                (const :tag "Not Required" nil))
+  :group 'rmail-retrieve)
+
+(defcustom rmail-pop-password-required nil
+  "*Non-nil if a password is required when reading mail using POP."
+  :type 'boolean
+  :group 'rmail-retrieve)
+
+(defcustom rmail-movemail-flags nil
+  "*List of flags to pass to movemail.
+Most commonly used to specify `-g' to enable GSS-API authentication
+or `-k' to enable Kerberos authentication."
+  :type '(repeat string)
+  :group 'rmail-retrieve
+  :version "20.3")
+
+(defvar rmail-pop-password-error "invalid usercode or password"
+  "Regular expression matching incorrect-password POP server error messages.
+If you get an incorrect-password error that this expression does not match,
+please report it with \\[report-emacs-bug].")
+
+(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.")
+rather than deleted, after it is retrieved."
+  :type 'boolean
+  :group 'rmail-retrieve)
 
 ;;;###autoload
-(defvar rmail-dont-reply-to-names nil "\
+(defcustom rmail-dont-reply-to-names nil "\
 *A regexp specifying names to prune of reply to messages.
-A value of nil means exclude your own name only.")
+A value of nil means exclude your own name only."
+  :type '(choice regexp (const :tag "Your Name" nil))
+  :group 'rmail-reply)
 
 ;;;###autoload
 (defvar rmail-default-dont-reply-to-names "info-" "\
@@ -78,50 +135,78 @@ value is the user's name.)
 It is useful to set this variable in the site customization file.")
 
 ;;;###autoload
-(defvar rmail-ignored-headers "^via:\\|^mail-from:\\|^origin:\\|^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:"
-  "*Regexp to match header fields that Rmail should normally hide.")
+(defcustom rmail-ignored-headers "^via:\\|^mail-from:\\|^origin:\\|^references:\\|^status:\\|^received:\\|^x400-originator:\\|^x400-recipients:\\|^x400-received:\\|^x400-mts-identifier:\\|^x400-content-type:\\|^\\(resent-\\|\\)message-id:\\|^summary-line:\\|^resent-date:\\|^nntp-posting-host:\\|^path:\\|^x-char.*:\\|^x-face:\\|^x-mailer:\\|^delivered-to:\\|^lines:\\|^mime-version:\\|^content-transfer-encoding:\\|^x-coding-system:\\|^return-path:\\|^errors-to:\\|^return-receipt-to:\\|^x-attribution:\\|^x-disclaimer:"
+  "*Regexp to match header fields that Rmail should normally hide."
+  :type 'regexp
+  :group 'rmail-headers)
 
 ;;;###autoload
-(defvar rmail-displayed-headers nil
+(defcustom rmail-displayed-headers nil
   "*Regexp to match Header fields that Rmail should display.
 If nil, display all header fields except those matched by
-`rmail-ignored-headers'.")
+`rmail-ignored-headers'."
+  :type '(choice regexp (const :tag "All"))
+  :group 'rmail-headers)
 
 ;;;###autoload
-(defvar rmail-retry-ignored-headers nil "\
-*Headers that should be stripped when retrying a failed message.")
+(defcustom rmail-retry-ignored-headers nil "\
+*Headers that should be stripped when retrying a failed message."
+  :type '(choice regexp (const nil :tag "None"))
+  :group 'rmail-headers)
 
 ;;;###autoload
-(defvar rmail-highlighted-headers "^From:\\|^Subject:" "\
+(defcustom rmail-highlighted-headers "^From:\\|^Subject:" "\
 *Regexp to match Header fields that Rmail should normally highlight.
 A value of nil means don't highlight.
-See also `rmail-highlight-face'.")
+See also `rmail-highlight-face'."
+  :type 'regexp
+  :group 'rmail-headers)
 
 ;;;###autoload
-(defvar rmail-highlight-face nil "\
-*Face used by Rmail for highlighting headers.")
+(defcustom rmail-highlight-face nil "\
+*Face used by Rmail for highlighting headers."
+  :type '(choice (const :tag "Default" nil)
+                face)
+  :group 'rmail-headers)
 
 ;;;###autoload
-(defvar rmail-delete-after-output nil "\
-*Non-nil means automatically delete a message that is copied to a file.")
+(defcustom rmail-delete-after-output nil "\
+*Non-nil means automatically delete a message that is copied to a file."
+  :type 'boolean
+  :group 'rmail-files)
 
 ;;;###autoload
-(defvar rmail-primary-inbox-list nil "\
+(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\")
 \(the name varies depending on the operating system,
-and the value of the environment variable MAIL overrides it).")
+and the value of the environment variable MAIL overrides it)."
+  ;; Don't use backquote here, because we don't want to need it
+  ;; at load time.
+  :type (list 'choice '(const :tag "Default" nil)
+             (list 'repeat ':value (or (getenv "MAIL")
+                                       (concat "/var/spool/mail/"
+                                               (getenv "USER")))
+                   'file))
+  :group 'rmail-retrieve
+  :group 'rmail-files)
 
 ;;;###autoload
-(defvar rmail-mail-new-frame nil
-  "*Non-nil means Rmail makes a new frame for composing outgoing mail.")
+(defcustom rmail-mail-new-frame nil
+  "*Non-nil means Rmail makes a new frame for composing outgoing mail."
+  :type 'boolean
+  :group 'rmail-reply)
 
 ;;;###autoload
-(defvar rmail-secondary-file-directory "~/"
-  "*Directory for additional secondary Rmail files.")
+(defcustom rmail-secondary-file-directory "~/"
+  "*Directory for additional secondary Rmail files."
+  :type 'directory
+  :group 'rmail-files)
 ;;;###autoload
-(defvar rmail-secondary-file-regexp "\\.xmail$"
-  "*Regexp for which files are secondary Rmail files.")
+(defcustom rmail-secondary-file-regexp "\\.xmail$"
+  "*Regexp for which files are secondary Rmail files."
+  :type 'regexp
+  :group 'rmail-files)
 
 ;;;###autoload
 (defvar rmail-mode-hook nil
@@ -145,15 +230,17 @@ still the current message in the Rmail buffer.")
 ;;  delimiting used on a given host (delim1 and delim2 from the config
 ;;  files).
 
-(defvar mmdf-delim1 "^\001\001\001\001\n"
+(defvar rmail-mmdf-delim1 "^\001\001\001\001\n"
   "Regexp marking the start of an mmdf message")
-(defvar mmdf-delim2 "^\001\001\001\001\n"
+(defvar rmail-mmdf-delim2 "^\001\001\001\001\n"
   "Regexp marking the end of an mmdf message")
 
-(defvar rmail-message-filter nil
+(defcustom rmail-message-filter nil
   "If non-nil, a filter function for new messages in RMAIL.
 Called with region narrowed to the message, including headers,
-before obeying `rmail-ignored-headers'.")
+before obeying `rmail-ignored-headers'."
+  :group 'rmail-headers
+  :type 'function)
 
 (defvar rmail-reply-prefix "Re: "
   "String to prepend to Subject line when replying to a message.")
@@ -163,60 +250,107 @@ before obeying `rmail-ignored-headers'.")
 (defvar rmail-reply-regexp "\\`\\(Re\\(([0-9]+)\\|\\[[0-9]+\\]\\|\\^[0-9]+\\)?: *\\)*"
   "Regexp to delete from Subject line before inserting `rmail-reply-prefix'.")
 
-(defvar rmail-display-summary nil
-  "If non-nil, Rmail always displays the summary buffer.")
-
-(defvar rmail-mode-map nil)
-
+(defcustom rmail-display-summary nil
+  "*If non-nil, Rmail always displays the summary buffer."
+  :group 'rmail-summary
+  :type 'boolean)
+\f
 (defvar rmail-inbox-list nil)
+(put 'rmail-inbox-list 'permanent-local t)
+
 (defvar rmail-keywords nil)
+(put 'rmail-keywords 'permanent-local t)
+
+(defvar rmail-buffer nil
+  "The RMAIL buffer related to the current buffer.
+In an RMAIL buffer, this holds the RMAIL buffer itself.
+In a summary buffer, this holds the RMAIL buffer it is a summary for.")
+(put 'rmail-buffer 'permanent-local t)
 
 ;; Message counters and markers.  Deleted flags.
 
 (defvar rmail-current-message nil)
+(put 'rmail-current-message 'permanent-local t)
+
 (defvar rmail-total-messages nil)
+(put 'rmail-total-messages 'permanent-local t)
+
 (defvar rmail-message-vector nil)
+(put 'rmail-message-vector 'permanent-local t)
+
 (defvar rmail-deleted-vector nil)
+(put 'rmail-deleted-vector 'permanent-local t)
 
-(defvar rmail-overlay-list nil)
+(defvar rmail-msgref-vector nil
+  "In an Rmail buffer, a vector whose Nth element is a list (N).
+When expunging renumbers messages, these lists are modified
+by substituting the new message number into the existing list.")
+(put 'rmail-msgref-vector 'permanent-local t)
 
-(defvar rmail-font-lock-keywords
-  (eval-when-compile
-    (let* ((cite-chars "[>|}]")
-          (cite-prefix "A-Za-z")
-          (cite-suffix (concat cite-prefix "0-9_.@-`'\"")))
-      (list '("^\\(From\\|Sender\\):" . font-lock-function-name-face)
-           '("^Reply-To:.*$" . font-lock-function-name-face)
-           '("^Subject:" . font-lock-comment-face)
-           '("^\\(To\\|Apparently-To\\|Cc\\):" . font-lock-keyword-face)
-           ;; Use MATCH-ANCHORED to effectively anchor the regexp left side.
-           `(,cite-chars
-             (,(concat "\\=[ \t]*"
-                       "\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
-                       cite-chars ".*")
-              (beginning-of-line) (end-of-line)
-              (0 font-lock-reference-face)))       
-           '("^\\(X-[A-Za-z0-9-]+\\|In-reply-to\\|Date\\):.*$"
-             . font-lock-string-face))))
-  "Additional expressions to highlight in Rmail mode.")
+(defvar rmail-overlay-list nil)
+(put 'rmail-overlay-list 'permanent-local t)
 
 ;; These are used by autoloaded rmail-summary.
 
 (defvar rmail-summary-buffer nil)
+(put 'rmail-summary-buffer 'permanent-local t)
 (defvar rmail-summary-vector nil)
+(put 'rmail-summary-vector 'permanent-local t)
 
+(defvar rmail-view-buffer nil
+  "Buffer which holds RMAIL message for MIME displaying.")
+(put 'rmail-view-buffer 'permanent-local t)
+\f
 ;; `Sticky' default variables.
 
 ;; Last individual label specified to a or k.
 (defvar rmail-last-label nil)
+(put 'rmail-last-label 'permanent-local t)
+
 ;; Last set of values specified to C-M-n, C-M-p, C-M-s or C-M-l.
 (defvar rmail-last-multi-labels nil)
+
 (defvar rmail-last-regexp nil)
-(defvar rmail-default-file "~/xmail"
-  "*Default file name for \\[rmail-output].")
-(defvar rmail-default-rmail-file "~/XMAIL"
-  "*Default file name for \\[rmail-output-to-rmail-file].")
+(put 'rmail-last-regexp 'permanent-local t)
+
+(defcustom rmail-default-file "~/xmail"
+  "*Default file name for \\[rmail-output]."
+  :type 'file
+  :group 'rmail-files)
+(defcustom rmail-default-rmail-file "~/XMAIL"
+  "*Default file name for \\[rmail-output-to-rmail-file]."
+  :type 'file
+  :group 'rmail-files)
+
+;; Mule and MIME related variables.
+
+;;;###autoload
+(defvar rmail-file-coding-system nil
+  "Coding system used in RMAIL file.
 
+This is set to nil by default.")
+
+;;;###autoload
+(defcustom rmail-enable-mime nil
+  "*If non-nil, RMAIL uses MIME feature.
+If the value is t, RMAIL automatically shows MIME decoded message.
+If the value is neither t nor nil, RMAIL does not show MIME decoded message
+until a user explicitly requires it."
+  :type '(choice (const :tag "on" t)
+                (const :tag "off" nil)
+                (sexp :tag "when asked" :format "%t\n" ask))
+  :group 'rmail)
+
+;;;###autoload
+(defvar rmail-show-mime-function nil
+  "Function to show MIME decoded message of RMAIL file.")
+
+;;;###autoload
+(defvar rmail-mime-feature 'rmail-mime
+  "Feature to require to load MIME support in Rmail.
+When starting Rmail, if `rmail-enable-mime' is non-nil,
+this feature is required with `require'.")
+\f
 ;;; Regexp matching the delimiter of messages in UNIX mail format
 ;;; (UNIX From lines), minus the initial ^.  Note that if you change
 ;;; this expression, you must change the code in rmail-nuke-pinhead-header
@@ -273,6 +407,29 @@ before obeying `rmail-ignored-headers'.")
      "\n"))
   nil)
 
+(defvar rmail-font-lock-keywords
+  (eval-when-compile
+    (let* ((cite-chars "[>|}]")
+          (cite-prefix "A-Za-z")
+          (cite-suffix (concat cite-prefix "0-9_.@-`'\"")))
+      (list '("^\\(From\\|Sender\\):" . font-lock-function-name-face)
+           '("^Reply-To:.*$" . font-lock-function-name-face)
+           '("^Subject:" . font-lock-comment-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]*\\)\\)+"
+                       "\\(.*\\)")
+              (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\\):.*$"
+             . 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)
@@ -298,6 +455,8 @@ before obeying `rmail-ignored-headers'.")
 \f
 ;;;; *** Rmail Mode ***
 
+(defvar rmail-enable-multibyte nil)
+
 ;;;###autoload
 (defun rmail (&optional file-name-arg)
   "Read and edit incoming mail.
@@ -313,25 +472,43 @@ 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))))
   (let* ((file-name (expand-file-name (or file-name-arg rmail-file-name)))
         (existed (get-file-buffer file-name))
-        run-mail-hook)
+        ;; This binding is necessary because we much decide if we
+        ;; need code conversion while the buffer is unibyte
+        ;; (i.e. enable-multibyte-characters is nil).
+        (rmail-enable-multibyte (default-value 'enable-multibyte-characters))
+        ;; Since the file may contain messages of different encodings
+        ;; at the tail (non-BYBYL part), we can't decode them at once
+        ;; on reading.  So, at first, we read the file without text
+        ;; code conversion, then decode the messages one by one by
+        ;; rmail-decode-babyl-format or
+        ;; rmail-convert-to-babyl-format.
+        (coding-system-for-read (and rmail-enable-multibyte 'raw-text))
+        run-mail-hook msg-shown)
     ;; Like find-file, but in the case where a buffer existed
     ;; and the file was reverted, recompute the message-data.
+    ;; We used to bind enable-local-variables to nil here,
+    ;; but that should not be needed now that rmail-mode
+    ;; sets it locally to nil.
+    ;; (Binding a variable locally with let is not safe if it has
+    ;; buffer-local bindings.)
     (if (and existed (not (verify-visited-file-modtime existed)))
        (progn
-         ;; Don't be confused by apparent local-variables spec
-         ;; in the last message in the RMAIL file.
-         (let ((enable-local-variables nil))
-           (find-file file-name))
+         (find-file file-name)
          (if (and (verify-visited-file-modtime existed)
                   (eq major-mode 'rmail-mode))
              (progn (rmail-forget-messages)
                     (rmail-set-message-counters))))
-      (let ((enable-local-variables nil))
-       (find-file file-name)))
+      (find-file file-name))
     (if (eq major-mode 'rmail-edit-mode)
-       (error "Exit Rmail Edit mode before getting new mail."))
+       (error "Exit Rmail Edit mode before getting new mail"))
     (if (and existed (> (buffer-size) 0))
        ;; Buffer not new and not empty; ensure in proper mode, but that's all.
        (or (eq major-mode 'rmail-mode)
@@ -341,18 +518,26 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
       (rmail-mode-2)
       ;; Convert all or part to Babyl file if possible.
       (rmail-convert-file)
+      ;; As we have read a file by raw-text, the buffer is set to
+      ;; unibyte.  We must make it multibyte if necessary.
+      (if (and rmail-enable-multibyte
+              (not enable-multibyte-characters))
+         (set-buffer-multibyte t))
       (goto-char (point-max))
       (if (null rmail-inbox-list)
          (progn
            (rmail-set-message-counters)
-           (rmail-show-message))))
-    (or (and (null file-name-arg)
-            (rmail-get-new-mail))
-       (rmail-show-message (rmail-first-unseen-message)))
-    (if rmail-display-summary (rmail-summary))
-    (rmail-construct-io-menu)
-    (if run-mail-hook
-       (run-hooks 'rmail-mode-hook))))
+           (rmail-show-message)
+           (setq msg-shown t))))
+    (unwind-protect
+       (or (and (null file-name-arg)
+                (rmail-get-new-mail))
+           (or msg-shown (rmail-show-message (rmail-first-unseen-message))))
+      (progn
+       (if rmail-display-summary (rmail-summary))
+       (rmail-construct-io-menu)
+       (if run-mail-hook
+           (run-hooks 'rmail-mode-hook))))))
 
 ;; Given the value of MAILPATH, return a list of inbox file names.
 ;; This is turned off because it is not clear that the user wants
@@ -371,6 +556,8 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
 ; I have checked that adding "-*- rmail -*-" to the BABYL OPTIONS line
 ; will not cause emacs 18.55 problems.
 
+;; This calls rmail-decode-babyl-format if the file is already Babyl.
+
 (defun rmail-convert-file ()
   (let (convert)
     (widen)
@@ -409,13 +596,16 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
          (search-forward "\n\^_" nil t)
          (narrow-to-region (point) (point-max))
          (rmail-convert-to-babyl-format)
-         (message "Converting to Babyl format...done")))))
-
-;;; I have checked that adding "-*- rmail -*-" to the BABYL OPTIONS line
-;;; will not cause emacs 18.55 problems.
+         (message "Converting to Babyl format...done"))
+      (if (and (not rmail-enable-mime)
+              rmail-enable-multibyte)
+         ;; We still have to decode BABYL part.
+         (rmail-decode-babyl-format)))))
 
 (defun rmail-insert-rmail-file-header ()
   (let ((buffer-read-only nil))
+    ;; -*-rmail-*- is here so that visiting the file normally
+    ;; recognizes it as an Rmail file.
     (insert "BABYL OPTIONS: -*- rmail -*-
 Version: 5
 Labels:
@@ -423,6 +613,32 @@ 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
+;; rmail-file-coding-system, or if it is nil, do auto conversion.
+
+(defun rmail-decode-babyl-format ()
+  (let ((modifiedp (buffer-modified-p))
+       (buffer-read-only nil)
+       (coding-system rmail-file-coding-system)
+       from to)
+    (goto-char (point-min))
+    (search-forward "\n\^_" nil t)     ; Skip BABYL header.
+    (setq from (point))
+    (goto-char (point-max))
+    (search-backward "\n\^_" from 'mv)
+    (setq to (point))
+    (unless (and coding-system
+                (coding-system-p coding-system))
+      (setq coding-system (detect-coding-region from to t)))
+    (unless (eq coding-system 'undecided)
+      (decode-coding-region from to coding-system)
+      (setq coding-system last-coding-system-used))
+    (set-buffer-modified-p modifiedp)
+    (setq buffer-file-coding-system nil)
+    (setq save-buffer-coding-system
+         (or coding-system 'undecided))))
+
+(defvar rmail-mode-map nil)
 (if rmail-mode-map
     nil
   (setq rmail-mode-map (make-keymap))
@@ -463,7 +679,7 @@ Note:    it means the file has no messages in it.\n\^_")))
   (define-key rmail-mode-map "\es"    'rmail-search)
   (define-key rmail-mode-map "t"      'rmail-toggle-header)
   (define-key rmail-mode-map "u"      'rmail-undelete-previous-message)
-  (define-key rmail-mode-map "w"      'rmail-edit-current-message)
+  (define-key rmail-mode-map "w"      'rmail-output-body-to-file)
   (define-key rmail-mode-map "x"      'rmail-expunge)
   (define-key rmail-mode-map "."      'rmail-beginning-of-message)
   (define-key rmail-mode-map "<"      'rmail-first-message)
@@ -493,6 +709,9 @@ Note:    it means the file has no messages in it.\n\^_")))
 (define-key rmail-mode-map [menu-bar classify output-menu]
   nil)
 
+(define-key rmail-mode-map [menu-bar classify output-body]
+  '("Output body to file..." . rmail-output-body-to-file))
+
 (define-key rmail-mode-map [menu-bar classify output-inbox]
   '("Output (inbox)..." . rmail-output))
 
@@ -635,6 +854,7 @@ Instead, these commands are available:
 \\[rmail-forward]      Forward this message to another user.
 \\[rmail-output-to-rmail-file]       Output this message to an Rmail file (append it).
 \\[rmail-output]       Output this message to a Unix-format mail file (append it).
+\\[rmail-output-body-to-file]  Save message body to a file.  Default filename comes from Subject line.
 \\[rmail-input]        Input Rmail file.  Run Rmail on that file.
 \\[rmail-add-label]    Add label to message.  It will be displayed in the mode line.
 \\[rmail-kill-label]   Kill label.  Remove a label from current message.
@@ -658,6 +878,7 @@ Instead, these commands are available:
 (defun rmail-mode-2 ()
   (kill-all-local-variables)
   (rmail-mode-1)
+  (rmail-perm-variables)
   (rmail-variables))
 
 (defun rmail-mode-1 ()
@@ -670,42 +891,28 @@ Instead, these commands are available:
   ;; The one exception is when messages are copied into an Rmail mode buffer.
   ;; rmail-output-to-rmail-file enables auto save when you do that.
   (setq buffer-auto-save-file-name nil)
-  (if (boundp 'mode-line-modified)
-      (setq mode-line-modified "--- ")
-    (setq mode-line-format
-         (cons "--- " (cdr (default-value 'mode-line-format)))))
+  (setq mode-line-modified "--")
   (use-local-map rmail-mode-map)
   (set-syntax-table text-mode-syntax-table)
   (setq local-abbrev-table text-mode-abbrev-table))
 
-(defun rmail-variables ()
-  (make-local-variable 'revert-buffer-function)
-  (setq revert-buffer-function 'rmail-revert)
-  (make-local-variable 'font-lock-defaults)
-  (setq font-lock-defaults
-   '(rmail-font-lock-keywords t nil nil nil
-     (font-lock-maximum-size . nil)
-     (font-lock-fontify-buffer-function . rmail-fontify-buffer-function)
-     (font-lock-unfontify-buffer-function . rmail-unfontify-buffer-function)
-     (font-lock-inhibit-thing-lock . (lazy-lock-mode fast-lock-mode))))
+;; Set up the permanent locals associated with an Rmail file.
+(defun rmail-perm-variables ()
   (make-local-variable 'rmail-last-label)
   (make-local-variable 'rmail-last-regexp)
   (make-local-variable 'rmail-deleted-vector)
+  (make-local-variable 'rmail-buffer)
+  (setq rmail-buffer (current-buffer))
+  (make-local-variable 'rmail-view-buffer)
+  (setq rmail-view-buffer rmail-buffer)
   (make-local-variable 'rmail-summary-buffer)
   (make-local-variable 'rmail-summary-vector)
   (make-local-variable 'rmail-current-message)
   (make-local-variable 'rmail-total-messages)
-  (make-local-variable 'require-final-newline)
-  (setq require-final-newline nil)
   (make-local-variable 'rmail-overlay-list)
   (setq rmail-overlay-list nil)
-  (make-local-variable 'version-control)
-  (setq version-control 'never)
-  (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)
   (make-local-variable 'rmail-message-vector)
+  (make-local-variable 'rmail-msgref-vector)
   (make-local-variable 'rmail-inbox-list)
   (setq rmail-inbox-list (rmail-parse-file-inboxes))
   ;; Provide default set of inboxes for primary mail file ~/RMAIL.
@@ -722,6 +929,37 @@ Instead, these commands are available:
   ;; this gets generated as needed
   (setq rmail-keywords nil))
 
+;; Set up the non-permanent locals associated with Rmail mode.
+(defun rmail-variables ()
+  (make-local-variable 'save-buffer-coding-system)
+  ;; If we don't already have a value for save-buffer-coding-system,
+  ;; get it from buffer-file-coding-system, and clear that
+  ;; because it should be determined in rmail-show-message.
+  (unless save-buffer-coding-system
+    (setq save-buffer-coding-system (or buffer-file-coding-system 'undecided))
+    (setq buffer-file-coding-system nil))
+  ;; Don't let a local variables list in a message cause confusion.
+  (make-local-variable 'enable-local-variables)
+  (setq enable-local-variables nil)
+  (make-local-variable 'revert-buffer-function)
+  (setq revert-buffer-function 'rmail-revert)
+  (make-local-variable 'font-lock-defaults)
+  (setq font-lock-defaults
+       '(rmail-font-lock-keywords
+         t nil nil nil
+         (font-lock-maximum-size . nil)
+         (font-lock-fontify-buffer-function . rmail-fontify-buffer-function)
+         (font-lock-unfontify-buffer-function . rmail-unfontify-buffer-function)
+         (font-lock-inhibit-thing-lock . (lazy-lock-mode fast-lock-mode))))
+  (make-local-variable 'require-final-newline)
+  (setq require-final-newline nil)
+  (make-local-variable 'version-control)
+  (setq version-control 'never)
+  (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))
+
 ;; Handle M-x revert-buffer done in an rmail-mode buffer.
 (defun rmail-revert (arg noconfirm)
   (let ((revert-buffer-function (default-value 'revert-buffer-function)))
@@ -765,13 +1003,12 @@ Instead, these commands are available:
   (interactive)
   (rmail-expunge-and-save)
   ;; Don't switch to the summary buffer even if it was recently visible.
-  (if rmail-summary-buffer
-      (progn
-       (replace-buffer-in-windows rmail-summary-buffer)
-       (bury-buffer rmail-summary-buffer)))
+  (when rmail-summary-buffer
+    (replace-buffer-in-windows rmail-summary-buffer)
+    (bury-buffer rmail-summary-buffer))
   (let ((obuf (current-buffer)))
-    (replace-buffer-in-windows obuf)
-    (bury-buffer obuf)))
+    (quit-window)
+    (replace-buffer-in-windows obuf)))
 
 (defun rmail-bury ()
   "Bury current Rmail buffer and its summary buffer."
@@ -782,10 +1019,9 @@ Instead, these commands are available:
     (if (rmail-summary-exists)
        (let (window)
          (while (setq window (get-buffer-window rmail-summary-buffer))
-           (set-window-buffer window (other-buffer rmail-summary-buffer)))
+           (quit-window nil window))
          (bury-buffer rmail-summary-buffer)))
-    (switch-to-buffer (other-buffer (current-buffer)))
-    (bury-buffer buffer-to-bury)))
+    (quit-window)))
 
 (defun rmail-duplicate-message ()
   "Create a duplicated copy of the current message.
@@ -914,9 +1150,11 @@ It returns t if it got any new messages."
   (or (eq buffer-undo-list t)
       (setq buffer-undo-list nil))
   (let ((all-files (if file-name (list file-name)
-                    rmail-inbox-list)))
+                    rmail-inbox-list))
+       (rmail-enable-multibyte (default-value 'enable-multibyte-characters))
+       found)
     (unwind-protect
-       (let (found)
+       (progn
          (while all-files
            (let ((opoint (point))
                  (new-messages 0)
@@ -970,6 +1208,8 @@ It returns t if it got any new messages."
                    (if (and (not file-name) (not success))
                        (let ((delfiles delete-files)
                              (count 0))
+                         ;; Try to delete the garbage just inserted.
+                         (delete-region (point-min) (point-max))
                          (while delfiles
                            (while (file-exists-p (format "RMAILOSE.%d" count))
                              (setq count (1+ count)))
@@ -1012,10 +1252,7 @@ It returns t if it got any new messages."
                (setq found t))))
          found)
       ;; Don't leave the buffer screwed up if we get a disk-full error.
-      (rmail-show-message))))
-
-(defvar rmail-file-coding-system 'coding-system-iso2022-7
-  "Coding system used in RMAIL file.")
+      (or found (rmail-show-message)))))
 
 (defun rmail-insert-inbox-text (files renamep)
   ;; Detect a locked file now, so that we avoid moving mail
@@ -1023,7 +1260,7 @@ It returns t if it got any new messages."
   (or (memq (file-locked-p buffer-file-name) '(nil t))
       (error "RMAIL file %s is locked"
             (file-name-nondirectory buffer-file-name)))
-  (let (file tofile delete-files movemail popmail)
+  (let (file tofile delete-files movemail popmail got-password)
     (while files
       (setq file (file-truename
                  (expand-file-name (substitute-in-file-name (car files))))
@@ -1062,7 +1299,8 @@ It returns t if it got any new messages."
                 (setq rmail-pop-password
                       (rmail-read-passwd
                        (format "Password for %s: "
-                               (substring file (+ popmail 3))))))
+                               (substring file (+ popmail 3))))
+                      got-password t))
             (if (eq system-type 'windows-nt)
                 ;; cannot have "po:" in file name
                 (setq tofile
@@ -1116,6 +1354,7 @@ It returns t if it got any new messages."
                             (if rmail-preserve-inbox 
                                 (list "-p")
                               nil)
+                            rmail-movemail-flags
                             (list file tofile)
                             (if rmail-pop-password 
                                 (list rmail-pop-password)
@@ -1137,6 +1376,13 @@ It returns t if it got any new messages."
                       (message "movemail: %s"
                                (buffer-substring (point-min)
                                                  (point-max)))
+                      ;; If we just read the password, most likely it is
+                      ;; wrong.  Otherwise, see if there is a specific
+                      ;; reason to think that the problem is a wrong passwd.
+                      (if (or got-password
+                              (re-search-forward rmail-pop-password-error
+                                                 nil t))
+                          (setq rmail-pop-password nil))
                       (sit-for 3)
                       nil))
                 (if errors (kill-buffer errors))))))
@@ -1144,29 +1390,10 @@ It returns t if it got any new messages."
       ;; Either the alternate name (if we renamed)
       ;; or the actual inbox (if not renaming).
       (if (file-exists-p tofile)
-         (let (size)
+         (let ((coding-system-for-read 'no-conversion)
+               size)
            (goto-char (point-max))
-           ;; At first, read the file as is (i.e. no decoding).
-           (setq size (nth 1 (let ((coding-system-for-read 'no-conversion))
-                               (insert-file-contents tofile))))
-           ;; Then, decode the contents one by one.
-           (let ((pos (point)))
-             (cond ((looking-at "^From ") ; new mails.
-                    (forward-line 1)
-                    (while (not (eobp))
-                      (setq pos (point))
-                      (search-forward "\nFrom " nil 'move)
-                      (decode-coding-region pos (point)
-                                            'automatic-conversion)))
-                   ((looking-at "BABYL OPTIONS:\\|\^L") ; Babyl format
-                    (while (not (eobp))
-                      (setq pos (point))
-                      (search-forward "\n^_" nil 'move)
-                      (decode-coding-region pos (point)
-                                            rmail-file-coding-system)))
-                   (t ; Perhaps MMDF format.  Convert all data at once.
-                    (decode-coding-region (point) (point-max)
-                                          'automatic-conversion))))
+           (setq size (nth 1 (insert-file-contents tofile)))
            (goto-char (point-max))
            (or (= (preceding-char) ?\n)
                (zerop size)
@@ -1210,12 +1437,13 @@ Optional DEFAULT is password to start with."
                     (sit-for 3)
                     ;; Try to get back in sync with a real message.
                     (if (re-search-forward
-                         (concat mmdf-delim1 "\\|^From") nil t)
+                         (concat rmail-mmdf-delim1 "\\|^From") nil t)
                         (beginning-of-line)
                       (goto-char (point-max)))))))
     (goto-char (point-min))
     (save-restriction
       (while (not (eobp))
+       (setq start (point))
        (cond ((looking-at "BABYL OPTIONS:");Babyl header
               (if (search-forward "\n\^_" nil t)
                   ;; If we find the proper terminator, delete through there.
@@ -1235,14 +1463,34 @@ Optional DEFAULT is password to start with."
                              (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)))
+              ;; Add an X-Coding-System: header if we don't have one.
+              (save-excursion
+                (goto-char start)
+                (forward-line 1)
+                (if (looking-at "0")
+                    (forward-line 1)
+                  (forward-line 2))
+                (or (save-restriction
+                      (narrow-to-region (point) (point-max))
+                      (rfc822-goto-eoh)
+                      (goto-char (point-min))
+                      (re-search-forward "^X-Coding-System:" nil t))
+                    (insert "X-Coding-System: "
+                            (symbol-name last-coding-system-used)
+                            "\n")))
               (narrow-to-region (point) (point-max)))
              ;;*** MMDF format
              ((let ((case-fold-search t))
-                (looking-at mmdf-delim1))
+                (looking-at rmail-mmdf-delim1))
               (let ((case-fold-search t))
                 (replace-match "\^L\n0, unseen,,\n*** EOOH ***\n")
-                (setq start (point))
-                (re-search-forward mmdf-delim2 nil t)
+                (re-search-forward rmail-mmdf-delim2 nil t)
                 (replace-match "\^_"))
               (save-excursion
                 (save-restriction
@@ -1250,11 +1498,20 @@ Optional DEFAULT is password to start with."
                   (goto-char (point-min))
                   (while (search-forward "\n\^_" nil t); single char "\^_"
                     (replace-match "\n^_")))); 2 chars: "^" and "_"
+              (setq last-coding-system-used nil)
+              (or rmail-enable-mime
+                  (not rmail-enable-multibyte)
+                  (decode-coding-region start (point) 'undecided))
+              (save-excursion
+                (goto-char start)
+                (forward-line 3)
+                (insert "X-Coding-System: "
+                        (symbol-name last-coding-system-used)
+                        "\n"))
               (narrow-to-region (point) (point-max))
               (setq count (1+ count)))
              ;;*** Mail format
              ((looking-at "^From ")
-              (setq start (point))
               (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
               (rmail-nuke-pinhead-header)
               ;; If this message has a Content-Length field,
@@ -1263,6 +1520,11 @@ Optional DEFAULT is password to start with."
                                    (and (re-search-forward "\n\n" nil t)
                                         (1- (point)))))
                      (case-fold-search t)
+                     (quoted-printable-header-field-end
+                      (save-excursion
+                        (re-search-forward
+                         "^content-transfer-encoding:\\(\n?[\t ]\\)*quoted-printable\\(\n?[\t ]\\)*"
+                         header-end t)))
                      (size
                       ;; Get the numeric value from the Content-Length field.
                       (save-excursion
@@ -1288,22 +1550,30 @@ Optional DEFAULT is password to start with."
                                     (and (looking-at "\^L")
                                          (search-forward "\n\^_" nil t))
                                     (let ((case-fold-search t))
-                                      (looking-at mmdf-delim1))
+                                      (looking-at rmail-mmdf-delim1))
                                     (looking-at "From "))))
                          (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
-                           "\\|"
-                           mmdf-delim1 "\\|"
-                           "^BABYL OPTIONS:\\|"
-                           "\^L\n[01],\\)") nil t)
-                  (goto-char (match-beginning 1))
-                (goto-char (point-max)))
-              (setq count (1+ count))
+                       (sit-for 1 0 t)))
+                (if (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))
+                      ;; 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"))))
+
               (save-excursion
                 (save-restriction
                   (narrow-to-region start (point))
@@ -1311,6 +1581,16 @@ Optional DEFAULT is password to start with."
                   (while (search-forward "\n\^_" nil t); single char
                     (replace-match "\n^_")))); 2 chars: "^" and "_"
               (insert ?\^_)
+              (setq last-coding-system-used nil)
+              (or rmail-enable-mime
+                  (not rmail-enable-multibyte)
+                  (decode-coding-region start (point) 'undecided))
+              (save-excursion
+                (goto-char start)
+                (forward-line 3)
+                (insert "X-Coding-System: "
+                        (symbol-name last-coding-system-used)
+                        "\n"))
               (narrow-to-region (point) (point-max)))
              ;;
              ;; This kludge is because some versions of sendmail.el
@@ -1321,6 +1601,46 @@ Optional DEFAULT is password to start with."
              (t (error "Cannot convert to babyl format")))))
     count))
 
+(defun rmail-hex-char-to-integer (character)
+  "Return CHARACTER's value interpreted as a hex digit."
+  (if (and (>= character ?0) (<= character ?9))
+      (- character ?0)
+    (let ((ch (logior character 32)))
+      (if (and (>= ch ?a) (<= ch ?f))
+         (- ch (- ?a 10))
+       (error "Invalid hex digit `%c'" ch)))))
+
+(defun rmail-hex-string-to-integer (hex-string)
+  "Return decimal integer for HEX-STRING."
+  (let ((hex-num 0)
+       (index 0))
+    (while (< index (length hex-string))
+      (setq hex-num (+ (* hex-num 16)
+                      (rmail-hex-char-to-integer (aref hex-string index))))
+      (setq index (1+ index)))
+    hex-num))
+
+(defun rmail-decode-quoted-printable (from to)
+  "Decode Quoted-Printable in the region between FROM and TO."
+  (interactive "r")
+  (goto-char from)
+  (or (markerp to)
+      (setq to (copy-marker to)))
+  (while (search-forward "=" to t)
+    (cond ((eq (following-char) ?\n)
+          (delete-char -1)
+          (delete-char 1))
+         ((looking-at "[0-9A-F][0-9A-F]")
+          (subst-char-in-region
+           (1- (point)) (point) ?=
+           (rmail-hex-string-to-integer
+            (buffer-substring (point) (+ 2 (point)))))
+          (delete-char 2))
+         ((looking-at "=")
+          (delete-char 1))
+         (t
+          (message "Malformed MIME quoted-printable message")))))
+
 ;; Delete the "From ..." line, creating various other headers with
 ;; information from it if they don't already exist.  Now puts the
 ;; original line into a mail-from: header line for debugging and for
@@ -1409,39 +1729,46 @@ delete all header fields whose names match that regexp.
 Otherwise, if `rmail-displayed-headers' is non-nil,
 delete all header fields *except* those whose names match that regexp.
 Otherwise, delete all header fields whose names match `rmail-ignored-headers'."
-  (if (search-forward "\n\n" nil t)
-      (let ((case-fold-search t)
-           (buffer-read-only nil))
-       (if (and rmail-displayed-headers (null ignored-headers))
-           (save-restriction
-             (narrow-to-region (point-min) (point))
-             (let (lim)
-               (goto-char (point-min))
-               (while (save-excursion
-                        (re-search-forward "\n[^ \t]")
-                        (and (not (eobp))
-                             (setq lim (1- (point)))))
-                 (if (save-excursion
-                       (re-search-forward rmail-displayed-headers lim t))
-                     (goto-char lim)
-                   (delete-region (point) lim))))
-             (goto-char (point-min)))
-         (or ignored-headers (setq ignored-headers rmail-ignored-headers))
+  (when (search-forward "\n\n" nil t)
+    (forward-char -1)
+    (let ((case-fold-search t)
+         (buffer-read-only nil))
+      (if (and rmail-displayed-headers (null ignored-headers))
          (save-restriction
            (narrow-to-region (point-min) (point))
-           (while (progn
-                    (goto-char (point-min))
-                    (re-search-forward ignored-headers nil t))
-             (beginning-of-line)
-             (delete-region (point)
-                            (progn (re-search-forward "\n[^ \t]")
-                                   (1- (point))))))))))
+           (let (lim next)
+             (goto-char (point-min))
+             (while (and (not (eobp))
+                         (save-excursion
+                           (if (re-search-forward "\n[^ \t]" nil t)
+                               (setq lim (match-beginning 0)
+                                     next (1+ lim))
+                             (setq lim nil next (point-max)))))
+               (if (save-excursion
+                     (re-search-forward rmail-displayed-headers lim t))
+                 (goto-char next)
+                 (delete-region (point) next))))
+           (goto-char (point-min)))
+       (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))
+           (beginning-of-line)
+           (delete-region (point)
+                          (if (re-search-forward "\n[^ \t]" nil t)
+                              (1- (point))
+                            (point-max)))))))))
 
 (defun rmail-msg-is-pruned ()
   (rmail-maybe-set-message-counters)
   (save-restriction
     (save-excursion
   (narrow-to-region (rmail-msgbeg rmail-current-message) (point-max))
+                 (if (not (or (= ?\n (char-after (point)))
+                              (= ?\n (char-before (1- (point))))))
+                     (insert "\n"))
     (goto-char (point-min))
     (forward-line 1)
       (= (following-char) ?1))))
@@ -1617,6 +1944,7 @@ change the invisible header text."
              (move-marker (aref v i)  nil)
              (setq i (1+ i)))))
     (setq rmail-message-vector nil)
+    (setq rmail-msgref-vector nil)
     (setq rmail-deleted-vector nil)))
 
 (defun rmail-maybe-set-message-counters ()
@@ -1651,6 +1979,13 @@ change the invisible header text."
          (concat rmail-deleted-vector deleted-head))
     (setq rmail-summary-vector
          (vconcat rmail-summary-vector (make-vector total-messages nil)))
+    (setq rmail-msgref-vector
+         (vconcat rmail-msgref-vector (make-vector total-messages nil)))
+    ;; Fill in the new elements of rmail-msgref-vector.
+    (let ((i (1+ (- rmail-total-messages total-messages))))
+      (while (<= i rmail-total-messages)
+       (aset rmail-msgref-vector i (list i))
+       (setq i (1+ i))))
     (goto-char (point-min))
     (or nomsg (message "Counting new messages...done (%d)" total-messages))))
 
@@ -1683,7 +2018,12 @@ change the invisible header text."
        (setq rmail-message-vector
              (apply 'vector (cons (point-min-marker) messages-head))
              rmail-deleted-vector (concat "D" deleted-head)
-             rmail-summary-vector (make-vector rmail-total-messages nil))
+             rmail-summary-vector (make-vector rmail-total-messages nil)
+             rmail-msgref-vector (make-vector (1+ rmail-total-messages) nil))
+       (let ((i 0))
+         (while (<= i rmail-total-messages)
+           (aset rmail-msgref-vector i (list i))
+           (setq i (1+ i))))
        (message "Counting messages...done")))))
        
 (defun rmail-set-message-counters-counter (&optional stop)
@@ -1710,13 +2050,15 @@ change the invisible header text."
   "Show message number N (prefix argument), counting from start of file.
 If summary buffer is currently displayed, update current message there also."
   (interactive "p")
+  (or (eq major-mode 'rmail-mode)
+      (switch-to-buffer rmail-buffer))
   (rmail-maybe-set-message-counters)
   (widen)
   (if (zerop rmail-total-messages)
       (progn (narrow-to-region (point-min) (1- (point-max)))
             (goto-char (point-min))
             (setq mode-line-process nil))
-    (let (blurb)
+    (let (blurb coding-system)
       (if (not n)
          (setq n rmail-current-message)
        (cond ((<= n 0)
@@ -1732,16 +2074,35 @@ If summary buffer is currently displayed, update current message there also."
       (let ((beg (rmail-msgbeg n)))
        (goto-char beg)
        (forward-line 1)
+       (save-excursion
+         (let ((end (rmail-msgend n)))
+           (save-restriction
+             (if (prog1 (= (following-char) ?0)
+                   (forward-line 2)
+                   (narrow-to-region (point) end))
+                 (rfc822-goto-eoh)
+               (search-forward "\n*** EOOH ***\n" end t))
+             (narrow-to-region beg (point))
+             (goto-char (point-min))
+             (if (re-search-forward "^X-Coding-System: *\\(.*\\)$" nil t)
+                 (let ((coding-system (intern (match-string 1))))
+                   (check-coding-system coding-system)
+                   (setq buffer-file-coding-system coding-system))
+               (setq buffer-file-coding-system nil)))))
        ;; Clear the "unseen" attribute when we show a message.
        (rmail-set-attribute "unseen" nil)
-       ;; Reformat the header, or else find the reformatted header.
        (let ((end (rmail-msgend n)))
+         ;; Reformat the header, or else find the reformatted header.
          (if (= (following-char) ?0)
              (rmail-reformat-message beg end)
            (search-forward "\n*** EOOH ***\n" end t)
            (narrow-to-region (point) end)))
        (goto-char (point-min))
        (rmail-display-labels)
+       (if (eq rmail-enable-mime t)
+           (funcall rmail-show-mime-function)
+         (setq rmail-view-buffer rmail-buffer)
+         )
        (rmail-highlight-headers)
        (if transient-mark-mode (deactivate-mark))
        (run-hooks 'rmail-show-message-hook)
@@ -1883,11 +2244,20 @@ or forward if N is negative."
 
 (defun rmail-message-regexp-p (msg regexp)
   "Return t, if for message number MSG, regexp REGEXP matches in the header."
-  (goto-char (rmail-msgbeg msg))
-  (let ((end 
-         (save-excursion 
-           (search-forward "*** EOOH ***" (point-max)) (point))))
-    (re-search-forward regexp end t)))
+  (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))
+         (setq beg (point))
+         (search-forward "\n\n" (point-max)))
+       (setq end (point)))
+      (goto-char beg)
+      (re-search-forward regexp end t))))
 
 (defvar rmail-search-last-regexp nil)
 (defun rmail-search (regexp &optional n)
@@ -2008,8 +2378,12 @@ If N is negative, go backwards instead."
        (i rmail-current-message)
        (case-fold-search t)
        search-regexp found)
+    (if (string-match "\\`[ \t]+" subject)
+       (setq subject (substring subject (match-end 0))))
     (if (string-match "Re:[ \t]*" subject)
        (setq subject (substring subject (match-end 0))))
+    (if (string-match "[ \t]+\\'" subject)
+       (setq subject (substring subject 0 (match-beginning 0))))
     (setq search-regexp (concat "^Subject: *\\(Re: *\\)?"
                                (regexp-quote subject)
                                "\n"))
@@ -2144,6 +2518,7 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
                (total rmail-total-messages)
                (new-message-number rmail-current-message)
                (new-summary nil)
+               (new-msgref (list (list 0)))
                (rmailbuf (current-buffer))
                (buffer-read-only nil)
                (messages rmail-message-vector)
@@ -2155,21 +2530,6 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
                  rmail-deleted-vector nil
                  rmail-summary-vector nil)
 
-           ;; Find each sendmail buffer that is set to reply
-           ;; to a message in this buffer, and update its
-           ;; message number.
-           (let ((bufs (buffer-list)))
-             (while bufs
-               (save-excursion
-                 (set-buffer (car bufs))
-                 (and (boundp 'rmail-send-actions-rmail-buffer)
-                      (eq rmail-send-actions-rmail-buffer rmailbuf)
-                      (setq rmail-send-actions-rmail-msg-number
-                            (rmail-msg-number-after-expunge
-                             deleted
-                             rmail-send-actions-rmail-msg-number))))
-               (setq bufs (cdr bufs))))
-
            (while (<= number total)
              (if (= (aref deleted number) ?D)
                  (progn
@@ -2185,7 +2545,11 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
                              (cons (aref messages number) nil)))
                (setq new-summary
                      (cons (if (= counter number) (aref summary (1- number)))
-                           new-summary)))
+                           new-summary))
+               (setq new-msgref
+                     (cons (aref rmail-msgref-vector number)
+                           new-msgref))
+               (setcar (car new-msgref) counter))
              (if (zerop (% (setq number (1+ number)) 20))
                  (message "Expunging deleted messages...%d" number)))
            (setq messages-tail
@@ -2196,6 +2560,7 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
                  rmail-message-vector (apply 'vector messages-head)
                  rmail-deleted-vector (make-string (1+ counter) ?\ )
                  rmail-summary-vector (vconcat (nreverse new-summary))
+                 rmail-msgref-vector (apply 'vector (nreverse new-msgref))
                  win t)))
       (message "Expunging deleted messages...done")
       (if (not win)
@@ -2243,16 +2608,13 @@ Deleted messages stay in the file until the \\[rmail-expunge] command is given."
 While composing the message, use \\[mail-yank-original] to yank the
 original message into it."
   (interactive)
-  (rmail-start-mail nil nil nil nil nil (current-buffer)))
+  (rmail-start-mail nil nil nil nil nil rmail-view-buffer))
 
 (defun rmail-continue ()
   "Continue composing outgoing message previously being composed."
   (interactive)
   (rmail-start-mail t))
 
-(put 'rmail-send-actions-rmail-buffer 'permanent-local t)
-(put 'rmail-send-actions-rmail-msg-number 'permanent-local t)
-
 (defun rmail-reply (just-sender)
   "Reply to the current message.
 Normally include CC: to all other recipients of original message;
@@ -2261,8 +2623,7 @@ use \\[mail-yank-original] to yank the original message into it."
   (interactive "P")
   (let (from reply-to cc subject date to message-id references
             resent-to resent-cc resent-reply-to
-            (msgnum rmail-current-message)
-            (rmail-buffer (current-buffer)))
+            (msgnum rmail-current-message))
     (save-excursion
       (save-restriction
        (widen)
@@ -2313,33 +2674,41 @@ use \\[mail-yank-original] to yank the original message into it."
                             (string-match rmail-reply-regexp subject))
                           (substring subject (match-end 0))
                         subject))))
-    (rmail-start-mail nil
-      (mail-strip-quoted-names reply-to)
-      subject
-      (rmail-make-in-reply-to-field from date message-id)
-      (if just-sender
-         nil
-       (let* ((cc-list (rmail-dont-reply-to
-                         (mail-strip-quoted-names
-                           (if (null cc) to (concat to ", " cc))))))
-         (if (string= cc-list "") nil cc-list)))
-      (current-buffer)
-      (list (list '(lambda ()
-                    (let ((msgnum rmail-send-actions-rmail-msg-number))
-                      (save-excursion
-                        (set-buffer rmail-send-actions-rmail-buffer)
-                        (if msgnum
-                            (rmail-set-attribute "answered" t msgnum)))))))
-      nil
-      (list (cons "References" (concat (mapconcat 'identity references " ")
-                                      " " message-id))))
-    ;; We keep the rmail buffer and message number in these 
-    ;; buffer-local vars in the sendmail buffer,
-    ;; so that rmail-only-expunge can relocate the message number.
-    (make-local-variable 'rmail-send-actions-rmail-buffer)
-    (make-local-variable 'rmail-send-actions-rmail-msg-number)
-    (setq rmail-send-actions-rmail-buffer rmail-buffer)
-    (setq rmail-send-actions-rmail-msg-number msgnum)))
+    (rmail-start-mail
+     nil
+     ;; Using mail-strip-quoted-names is undesirable with newer mailers
+     ;; 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)
+     subject
+     (rmail-make-in-reply-to-field from date message-id)
+     (if just-sender
+        nil
+       ;; mail-strip-quoted-names is NOT necessary for rmail-dont-reply-to
+       ;; to do its job.
+       (let* ((cc-list (rmail-dont-reply-to
+                       (mail-strip-quoted-names
+                        (if (null cc) to (concat to ", " cc))))))
+        (if (string= cc-list "") nil cc-list)))
+     rmail-view-buffer
+     (list (list 'rmail-mark-message
+                rmail-view-buffer
+                (aref rmail-msgref-vector msgnum)
+                "answered"))
+     nil
+     (list (cons "References" (concat (mapconcat 'identity references " ")
+                                     " " message-id))))))
+
+(defun rmail-mark-message (buffer msgnum-list attribute)
+  "Give BUFFER's message number in MSGNUM-LIST the attribute ATTRIBUTE.
+This is use in the send-actions for message buffers.
+MSGNUM-LIST is a list of the form (MSGNUM)
+which is an element of rmail-msgref-vector."
+  (save-excursion
+    (set-buffer buffer)
+    (if (car msgnum-list)
+       (rmail-set-attribute attribute t (car msgnum-list)))))
 
 (defun rmail-make-in-reply-to-field (from date message-id)
   (cond ((not from)
@@ -2350,12 +2719,14 @@ use \\[mail-yank-original] to yank the original message into it."
          (require 'rfc822)
          (let ((tem (car (rfc822-addresses from))))
            (if message-id
-               (if (string-match
-                    (regexp-quote (if (string-match "@[^@]*\\'" tem)
-                                      (substring tem 0 (match-beginning 0))
-                                      tem))
-                    message-id)
-                   ;; Message-ID is sufficiently informative
+               (if (or (not tem)
+                      (string-match
+                       (regexp-quote (if (string-match "@[^@]*\\'" tem)
+                                         (substring tem 0
+                                                    (match-beginning 0))
+                                       tem))
+                       message-id))
+                   ;; missing From, or Message-ID is sufficiently informative
                    message-id
                    (concat message-id " (" tem ")"))
             ;; Copy TEM, discarding text properties.
@@ -2367,9 +2738,9 @@ use \\[mail-yank-original] to yank the original message into it."
               (if date
                   (concat field "'s message of " date)
                   field)))))
-        ((let* ((foo "[^][\000-\037\177-\377()<>@,;:\\\" ]+")
-                (bar "[^][\000-\037\177-\377()<>@,;:\\\"]+"))
-           ;; Can't use format because format loses on \000 (unix *^&%*^&%$!!)
+        ((let* ((foo "[^][\000-\037()<>@,;:\\\" ]+")
+                (bar "[^][\000-\037()<>@,;:\\\"]+"))
+          ;; These strings both match all non-ASCII characters.
            (or (string-match (concat "\\`[ \t]*\\(" bar
                                      "\\)\\(<" foo "@" foo ">\\)?[ \t]*\\'")
                              ;; "Unix Loser <Foo@bar.edu>" => "Unix Loser"
@@ -2417,15 +2788,10 @@ see the documentation of `rmail-resend'."
                           "]")))
       (if (rmail-start-mail
           nil nil subject nil nil nil
-          (list (list (function
-                       (lambda ()
-                         (let ((msgnum
-                                rmail-send-actions-rmail-msg-number))
-                           (save-excursion
-                             (set-buffer rmail-send-actions-rmail-buffer)
-                             (if msgnum
-                                 (rmail-set-attribute
-                                  "forwarded" t msgnum))))))))
+          (list (list 'rmail-mark-message
+                      forward-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
           ;; so that the Rmail buffer remains visible
@@ -2433,18 +2799,8 @@ see the documentation of `rmail-resend'."
           (and (not rmail-mail-new-frame) (one-window-p t)))
          ;; The mail buffer is now current.
          (save-excursion
-           ;; We keep the rmail buffer and message number in these 
-           ;; buffer-local vars in the sendmail buffer,
-           ;; so that rmail-only-expunge can relocate the message number.
-           (make-local-variable 'rmail-send-actions-rmail-buffer)
-           (make-local-variable 'rmail-send-actions-rmail-msg-number)
-           (setq rmail-send-actions-rmail-buffer forward-buffer)
-           (setq rmail-send-actions-rmail-msg-number msgnum)
            ;; Insert after header separator--before signature if any.
-           (goto-char (point-min))
-           (search-forward-regexp
-            (concat "^" (regexp-quote mail-header-separator) "$"))
-           (forward-line 1)
+           (goto-char (mail-text-start))
            (insert "------- Start of forwarded message -------\n")
            ;; Quote lines with `- ' if they start with `-'.
            (let ((beg (point)) end)
@@ -2452,7 +2808,7 @@ see the documentation of `rmail-resend'."
              (set-marker-insertion-type end t)
              (insert-buffer-substring forward-buffer)
              (goto-char beg)
-             (while (re-search-forward "^-" nil t)
+             (while (re-search-forward "^-" end t)
                (beginning-of-line)
                (insert "- ")
                (forward-line 1))
@@ -2471,17 +2827,15 @@ ADDRESSES should be a single address, a string consisting of several
 addresses separated by commas, or a list of addresses.
 
 Optional FROM is the address to resend the message from, and
-defaults to the username of the person redistributing the message.
-Optional COMMENT is a string that will be inserted as a comment in the
-resent message.
+defaults from the value of `user-mail-address'.
+Optional COMMENT is a string to insert as a comment in the resent message.
 Optional ALIAS-FILE is alternate aliases file to be used by sendmail,
 typically for purposes of moderating a list."
   (interactive "sResend to: ")
   (require 'sendmail)
   (require 'mailalias)
-  (if (not from) (setq from (user-login-name)))
+  (if (not from) (setq from user-mail-address))
   (let ((tembuf (generate-new-buffer " sendmail temp"))
-       (mail-header-separator "")
        (case-fold-search nil)
        (mailbuf (current-buffer)))
     (unwind-protect
@@ -2554,46 +2908,62 @@ typically for purposes of moderating a list."
 (defvar mail-unsent-separator
   (concat "^ *---+ +Unsent message follows +---+ *$\\|"
          "^ *---+ +Returned message +---+ *$\\|"
+         "^ *---+ *Returned mail follows *---+ *$\\|"
          "^Start of returned message$\\|"
          "^ *---+ +Original message +---+ *$\\|"
          "^ *--+ +begin message +--+ *$\\|"
          "^ *---+ +Original message follows +---+ *$\\|"
+         "^ *---+ +Your message follows +---+ *$\\|"
          "^|? *---+ +Message text follows: +---+ *|?$")
   "A regexp that matches the separator before the text of a failed message.")
 
+(defvar mail-mime-unsent-header "^Content-Type: message/rfc822 *$"
+ "A regexp that matches the header of a MIME body part with a failed message.")
+
 (defun rmail-retry-failure ()
   "Edit a mail message which is based on the contents of the current message.
 For a message rejected by the mail system, extract the interesting headers and
 the body of the original message.
-The variable `mail-unsent-separator' should match the string that
+If the failed message is a MIME multipart message, it is searched for a
+body part with a header which matches the variable `mail-mime-unsent-header'.
+Otherwise, the variable `mail-unsent-separator' should match the string that
 delimits the returned original message.
 The variable `rmail-retry-ignored-headers' is a regular expression
 specifying headers which should not be copied into the new message."
   (interactive)
   (require 'mail-utils)
-  (let ((rmail-buffer (current-buffer))
+  (let ((rmail-this-buffer (current-buffer))
        (msgnum rmail-current-message)
        bounce-start bounce-end bounce-indent resending)
     (save-excursion
       ;; Narrow down to just the quoted original message
       (rmail-beginning-of-message)
-      (let ((case-fold-search t))
-       (if (search-forward "This is a MIME-encapsulated message\n\n--" nil t)
+      (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
-                  (buffer-substring (progn (beginning-of-line) (point))
-                                    (progn (end-of-line) (point)))))
-             (or (re-search-forward mail-unsent-separator nil t)
+                  (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 (and (search-forward codestring nil t)
-                      (search-forward "\n\n" nil t))
-                 (error "Cannot find end of Mime data in failed message"))
-             (setq bounce-start (point))
-             (save-excursion
-               (goto-char (point-max))
-               (search-backward codestring)
-               (setq bounce-end (point)))
              (or (search-forward "\n\n" nil t)
-                 (error "Cannot find end of header in failed message")))
+                 (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")
@@ -2631,49 +3001,34 @@ specifying headers which should not be copied into the new message."
     ;; Start sending a new message; default header fields from the original.
     ;; Turn off the usual actions for initializing the message body
     ;; because we want to get only the text from the failure message.
-    (let ((action
-          ;; This function will be called when the user sends the retry.
-          ;; It will mark the bounce message as "retried".
-          (function (lambda ()
-                      (let ((msgnum rmail-send-actions-rmail-msg-number))
-                        (save-excursion
-                          (set-buffer rmail-send-actions-rmail-buffer)
-                          (if msgnum
-                              (rmail-set-attribute "retried" t msgnum)))))))
-         mail-signature mail-setup-hook)
-      (if (rmail-start-mail nil nil nil nil nil rmail-buffer
-                           (list (list action)))
+    (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))
-           ;; We keep the rmail buffer and message number in these 
-           ;; buffer-local vars in the sendmail buffer,
-           ;; so that the rmail-only-expunge can relocate the message number.
-           (make-local-variable 'rmail-send-actions-rmail-buffer)
-           (make-local-variable 'rmail-send-actions-rmail-msg-number)
-           (setq rmail-send-actions-rmail-buffer rmail-buffer)
-           (setq rmail-send-actions-rmail-msg-number msgnum)
            (erase-buffer)
-           (insert-buffer-substring rmail-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:")
-           (goto-char (point-min))
+           (mail-sendmail-delimit-header)
            (save-restriction
-             (search-forward "\n\n")
-             (forward-line -1)
-             (narrow-to-region (point-min) (point))
+             (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"))))
-           (insert mail-header-separator)
+           (goto-char (point-min))
            (mail-position-on-field (if resending "Resent-To" "To") t)
-           (set-buffer rmail-buffer)
+           (set-buffer rmail-this-buffer)
            (rmail-beginning-of-message))))))
 \f
 (defun rmail-summary-exists ()
@@ -2686,12 +3041,16 @@ In fact, the non-nil value returned is the summary buffer itself."
   "t iff in RMAIL buffer and an associated summary buffer is displayed."
   (and rmail-summary-buffer (get-buffer-window rmail-summary-buffer)))
 
-(defvar rmail-redisplay-summary nil
+(defcustom rmail-redisplay-summary nil
   "*Non-nil means Rmail should show the summary when it changes.
-This has an effect only if a summary buffer exists.")
+This has an effect only if a summary buffer exists."
+  :type 'boolean
+  :group 'rmail-summary)
 
-(defvar rmail-summary-window-size nil
-  "*Non-nil means specify the height for an Rmail summary window.")
+(defcustom rmail-summary-window-size nil
+  "*Non-nil means specify the height for an Rmail summary window."
+  :type '(choice (const :tag "Disabled" nil) integer)
+  :group 'rmail-summary)
 
 ;; Put the summary buffer back on the screen, if user wants that.
 (defun rmail-maybe-display-summary ()
@@ -2756,6 +3115,89 @@ This has an effect only if a summary buffer exists.")
            (font-lock-fontify-region (point-min) (point-max))
            (and (not modified) (buffer-modified-p) (set-buffer-modified-p nil)))))))
 
+;;; Speedbar support for RMAIL files.
+(eval-when-compile (require 'speedbspec))
+
+(defvar rmail-speedbar-last-user nil
+  "The last user to be displayed in the speedbar.")
+
+(defvar rmail-speedbar-menu-items
+  '(["Browse Item On Line" speedbar-edit-line t]
+    ["Move message to folder" rmail-move-message-to-folder-on-line
+     (save-excursion (beginning-of-line)
+                    (looking-at "<M> "))])
+  "Additional menu-items to add to speedbar frame.")
+
+(defun rmail-speedbar-buttons (buffer)
+  "Create buttons for BUFFER containing rmail messages.
+Click on the address under Reply to: to reply to this person.
+Under Folders: Click a name to read it, or on the <M> to move the
+current message into that RMAIL folder."
+  (let ((from nil))
+    (save-excursion
+      (set-buffer buffer)
+      (goto-char (point-min))
+      (if (not (re-search-forward "^Reply-To: " nil t))
+         (if (not (re-search-forward "^From:? " nil t))
+             (setq from t)))
+      (if from
+         nil
+       (setq from (buffer-substring (point) (save-excursion
+                                              (end-of-line)
+                                              (point))))))
+    (goto-char (point-min))
+    (if (and (looking-at "Reply to:")
+            (equal from rmail-speedbar-last-user))
+       nil
+      (setq rmail-speedbar-last-user from)
+      (erase-buffer)
+      (insert "Reply To:\n")
+      (if (stringp from)
+         (speedbar-insert-button from 'speedbar-directory-face 'highlight
+                                 'rmail-speedbar-button 'rmail-reply))
+      (insert "Folders:\n")
+      (let* ((case-fold-search nil)
+            (df (directory-files (save-excursion (set-buffer buffer)
+                                                 default-directory)
+                                 nil "^[A-Z0-9]+\\(\\.[A-Z0-9]+\\)?$")))
+       (while df
+         (speedbar-insert-button "<M>" 'speedbar-button-face 'highlight
+                                 'rmail-speedbar-move-message (car df))
+         (speedbar-insert-button (car df) 'speedbar-file-face 'highlight
+                                 'rmail-speedbar-find-file nil t)
+         (setq df (cdr df)))))))
+
+(defun rmail-speedbar-button (text token indent)
+  "Execute an rmail command specified by TEXT.
+The command used is TOKEN.  INDENT is not used."
+  (speedbar-with-attached-buffer
+   (funcall token t)))
+
+(defun rmail-speedbar-find-file (text token indent)
+  "Load in the rmail file TEXT.
+TOKEN and INDENT are not used."
+  (speedbar-with-attached-buffer
+   (message "Loading in RMAIL file %s..." text)
+   (find-file text)))
+
+(defun rmail-move-message-to-folder-on-line ()
+  "If the current line is a folder, move current message to it."
+  (interactive)
+  (save-excursion
+    (beginning-of-line)
+    (if (re-search-forward "<M> " (save-excursion (end-of-line) (point)) t)
+       (progn
+         (forward-char -2)
+         (speedbar-do-function-pointer)))))
+
+(defun rmail-speedbar-move-message (text token indent)
+  "From button TEXT, copy current message to the rmail file specified by TOKEN.
+TEXT and INDENT are not used."
+  (speedbar-with-attached-buffer
+   (message "Moving message to %s" token)
+   (rmail-output-to-rmail-file token)))
+
+
 (provide 'rmail)
 
 ;;; rmail.el ends here