]> code.delx.au - gnu-emacs/blobdiff - lisp/mail/rmail.el
(mail-unsent-separator): Handle "returned message follows".
[gnu-emacs] / lisp / mail / rmail.el
index 44f3c520c9a939df8ee235ca8463bd8afb35c923..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,97 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\
   :group 'rmail)
 
 
-(defvar rmail-movemail-program nil
-  "If non-nil, name of program for fetching new mail.")
+(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 '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,
@@ -129,7 +135,7 @@ value is the user's name.)
 It is useful to set this variable in the site customization file.")
 
 ;;;###autoload
-(defcustom rmail-ignored-headers "^via:\\|^mail-from:\\|^origin:\\|^references:\\|^status:\\|^received:\\|^x400-originator:\\|^x400-recipients:\\|^x400-received:\\|^x400-mts-identifier:\\|^x400-content-type:\\|^\\(resent-\\|\\)message-id:\\|^summary-line:\\|^resent-date:\\|^nntp-posting-host:\\|^path:\\|^x-char.*:\\|^x-face:\\|^x-mailer:\\|^delivered-to:\\|^lines:\\|^mime-version:\\|^content-transfer-encoding:"
+(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)
@@ -229,10 +235,12 @@ still the current message in the Rmail buffer.")
 (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.")
@@ -242,13 +250,16 @@ 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.
@@ -259,38 +270,25 @@ In a summary buffer, this holds the RMAIL buffer it is a summary for.")
 ;; 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-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-overlay-list 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-reference-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.")
+(put 'rmail-overlay-list 'permanent-local t)
 
 ;; These are used by autoloaded rmail-summary.
 
@@ -299,13 +297,22 @@ by substituting the new message number into the existing list.")
 (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)
+(put 'rmail-last-regexp 'permanent-local t)
+
 (defcustom rmail-default-file "~/xmail"
   "*Default file name for \\[rmail-output]."
   :type 'file
@@ -315,6 +322,35 @@ by substituting the new message number into the existing list.")
   :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
@@ -371,6 +407,29 @@ by substituting the new message number into the existing list.")
      "\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)
@@ -393,42 +452,11 @@ by substituting the new message number into the existing list.")
           (let ((rmail-total-messages total))
             (,@ body))))
        (rmail-maybe-display-summary))))
-
-(defvar rmail-view-buffer nil
-  "Buffer which holds RMAIL message for MIME displaying.")
-
-;; 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 provide for using MIME feature in RMAIL.
-When starting rmail, this feature is requrired if rmail-enable-mime
-is non-nil.")
-
 \f
 ;;;; *** Rmail Mode ***
 
+(defvar rmail-enable-multibyte nil)
+
 ;;;###autoload
 (defun rmail (&optional file-name-arg)
   "Read and edit incoming mail.
@@ -452,46 +480,64 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
               (setq rmail-enable-mime nil))))
   (let* ((file-name (expand-file-name (or file-name-arg rmail-file-name)))
         (existed (get-file-buffer file-name))
-        (coding-system-for-read 'no-conversion)
-        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)
            (progn (rmail-mode-2)
                   (setq run-mail-hook t)))
-      (kill-local-variable 'enable-multibyte-characters)
       (setq run-mail-hook t)
       (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
@@ -510,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)
@@ -530,13 +578,6 @@ If `rmail-display-summary' is non-nil, make a summary for this RMAIL file."
           ;; Non-empty file in non-RMAIL format.  Add header and convert.
           (setq convert t)
           (rmail-insert-rmail-file-header)))
-    (if (and (not convert)
-            (not rmail-enable-mime)
-            rmail-file-coding-system)
-       ;; Decode coding system of BABYL part at the head only.  The
-       ;; remaining non BABYL parts are decoded in
-       ;; rmail-convert-to-babyl-format if necessary.
-       (rmail-decode-babyl-format))
     ;; If file was not a Babyl file or if there are
     ;; Unix format messages added at the end,
     ;; convert file as necessary.
@@ -555,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:
@@ -570,23 +614,31 @@ 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.
+;; 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)
-       pos)
+       (coding-system rmail-file-coding-system)
+       from to)
     (goto-char (point-min))
-    (search-forward "\n\^_" nil t)     ; Skip BYBYL header.
-    (setq pos (point))
-    (message "Decoding messages...")
-    (while (search-forward "\n\^_" nil t)
-      (decode-coding-region pos (point) rmail-file-coding-system)
-      (setq pos (point)))
-    (message "Decoding messages...done")
-    (set-buffer-file-coding-system rmail-file-coding-system)
-    (set-buffer-modified-p modifiedp)))
+    (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))
@@ -802,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.
@@ -825,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 ()
@@ -842,16 +896,8 @@ Instead, these commands are available:
   (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)
@@ -863,16 +909,8 @@ Instead, these commands are available:
   (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)
@@ -891,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)))
@@ -898,10 +967,7 @@ Instead, these commands are available:
     (if (revert-buffer arg noconfirm)
        ;; If the user said "yes", and we changed something,
        ;; reparse the messages.
-       ;; But, we don't have to convert coding system because backup
-       ;; files should have been saved by Emacs' internal format.
-       (let ((rmail-file-coding-system nil)
-             (enable-multibyte-characters nil))
+       (progn
          (rmail-convert-file)
          (goto-char (point-max))
          (rmail-mode)))))
@@ -937,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."
@@ -954,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.
@@ -1086,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)
@@ -1186,7 +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))))
+      (or found (rmail-show-message)))))
 
 (defun rmail-insert-inbox-text (files renamep)
   ;; Detect a locked file now, so that we avoid moving mail
@@ -1288,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)
@@ -1396,10 +1463,27 @@ 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-file-coding-system)
+                  (not rmail-enable-multibyte)
                   (decode-coding-region start (point)
-                                        rmail-file-coding-system))
+                                        (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))
@@ -1414,9 +1498,16 @@ 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 enable-multibyte-characters)
+                  (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
@@ -1429,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
@@ -1458,18 +1554,26 @@ Optional DEFAULT is password to start with."
                                     (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
-                           "\\|"
-                           rmail-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))
@@ -1477,9 +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 enable-multibyte-characters)
+                  (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
@@ -1490,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
@@ -1578,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))))
@@ -1900,7 +2058,7 @@ If summary buffer is currently displayed, update current message there also."
       (progn (narrow-to-region (point-min) (1- (point-max)))
             (goto-char (point-min))
             (setq mode-line-process nil))
-    (let (blurb)
+    (let (blurb coding-system)
       (if (not n)
          (setq n rmail-current-message)
        (cond ((<= n 0)
@@ -1916,10 +2074,25 @@ 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)
@@ -2071,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)
@@ -2196,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"))
@@ -2552,9 +2738,9 @@ which is an element of rmail-msgref-vector."
               (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"
@@ -2614,10 +2800,7 @@ see the documentation of `rmail-resend'."
          ;; The mail buffer is now current.
          (save-excursion
            ;; 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)
@@ -2653,7 +2836,6 @@ typically for purposes of moderating a list."
   (require 'mailalias)
   (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
@@ -2726,10 +2908,12 @@ 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.")
 
@@ -2773,13 +2957,11 @@ specifying headers which should not be copied into the new message."
              (or (re-search-forward mail-mime-unsent-header nil t)
                  (error "Cannot find beginning of header in failed message"))
              (or (search-forward "\n\n" nil t)
-                 (error "Cannot find end of Mime data in failed message"))
+                 (error "Cannot find start of Mime data in failed message"))
              (setq bounce-start (point))
-             (or (search-forward codestring nil t)
-                 (error "Cannot find end of Mime data in failed message"))
-             (setq bounce-end (match-beginning 0))
-;            (or (search-forward "\n\n" nil t)
-;                (error "Cannot find end of header in failed message"))
+             (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)
@@ -2836,17 +3018,15 @@ specifying headers which should not be copied into the new message."
                (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-this-buffer)
            (rmail-beginning-of-message))))))
@@ -2869,7 +3049,7 @@ This has an effect only if a summary buffer exists."
 
 (defcustom rmail-summary-window-size nil
   "*Non-nil means specify the height for an Rmail summary window."
-  :type 'boolean
+  :type '(choice (const :tag "Disabled" nil) integer)
   :group 'rmail-summary)
 
 ;; Put the summary buffer back on the screen, if user wants that.
@@ -2935,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