*** empty log message ***
[gnus] / lisp / nnmail.el
index 07f941d..8d13ab9 100644 (file)
@@ -1,5 +1,5 @@
 ;;; nnmail.el --- mail support functions for the Gnus mail backends
-;; Copyright (C) 1995,96 Free Software Foundation, Inc.
+;; Copyright (C) 1995,96,97 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
 ;; Keywords: news, mail
 (require 'timezone)
 (require 'message)
 (eval-when-compile (require 'cl))
+(require 'custom)
 
-(defvar nnmail-split-methods
+(defgroup nnmail nil
+  "Reading mail with Gnus."
+  :group 'gnus)
+
+(defgroup nnmail-retrieve nil
+  "Retrieving new mail."
+  :group 'nnmail)
+
+(defgroup nnmail-prepare nil
+  "Preparing (or mangling) new mail after retrival."
+  :group 'nnmail)
+
+(defgroup nnmail-duplicate nil
+  "Handling of duplicate mail messages."
+  :group 'nnmail)
+
+(defgroup nnmail-split nil
+  "Organizing the incomming mail in folders."
+  :group 'nnmail)
+
+(defgroup nnmail-files nil
+  "Mail files."
+  :group 'gnus-files
+  :group 'nnmail)
+
+(defgroup nnmail-expire nil
+  "Expiring old mail."
+  :group 'nnmail)
+
+(defgroup nnmail-procmail nil
+  "Interfacing with procmail and other mail agents."
+  :group 'nnmail)
+
+(defgroup nnmail-various nil
+  "Various mail options."
+  :group 'nnmail)
+
+(defcustom nnmail-split-methods
   '(("mail.misc" ""))
-  "*Incoming mail will be split according to this variable.
+  "Incoming mail will be split according to this variable.
 
 If you'd like, for instance, one mail group for mail from the
 \"4ad-l\" mailing list, one group for junk mail and one for everything
@@ -56,35 +94,52 @@ mail belongs in that group.
 
 The last element should always have \"\" as the regexp.
 
-This variable can also have a function as its value.")
+This variable can also have a function as its value."
+  :group 'nnmail-split
+  :type '(choice (repeat :tag "Alist" (group (string :tag "Name") regexp))
+                (function-item nnmail-split-fancy)
+                (function :tag "Other")))
 
 ;; Suggested by Erik Selberg <speed@cs.washington.edu>.
-(defvar nnmail-crosspost t
-  "*If non-nil, do crossposting if several split methods match the mail.
-If nil, the first match found will be used.")
+(defcustom nnmail-crosspost t
+  "If non-nil, do crossposting if several split methods match the mail.
+If nil, the first match found will be used."
+  :group 'nnmail-split
+  :type 'boolean)
 
 ;; Added by gord@enci.ucalgary.ca (Gordon Matzigkeit).
-(defvar nnmail-keep-last-article nil
-  "*If non-nil, nnmail will never delete the last expired article in a directory.  
+(defcustom nnmail-keep-last-article nil
+  "If non-nil, nnmail will never delete the last expired article in a directory.  
 You may need to set this variable if other programs are putting
-new mail into folder numbers that Gnus has marked as expired.")
+new mail into folder numbers that Gnus has marked as expired."
+  :group 'nnmail-procmail
+  :group 'nnmail-various
+  :type 'boolean)
 
-(defvar nnmail-use-long-file-names nil
-  "*If non-nil the mail backends will use long file and directory names.
+(defcustom nnmail-use-long-file-names nil
+  "If non-nil the mail backends will use long file and directory names.
 If nil, groups like \"mail.misc\" will end up in directories like
-\"mail/misc/\".")
+\"mail/misc/\"."
+  :group 'nnmail-files
+  :type 'boolean)
 
-(defvar nnmail-default-file-modes 384
-  "Set the mode bits of all new mail files to this integer.")
+(defcustom nnmail-default-file-modes 384
+  "Set the mode bits of all new mail files to this integer."
+  :group 'nnmail-files
+  :type 'integer)
 
-(defvar nnmail-expiry-wait 7
+(defcustom nnmail-expiry-wait 7
   "*Expirable articles that are older than this will be expired.
 This variable can either be a number (which will be interpreted as a
 number of days) -- this doesn't have to be an integer.  This variable
-can also be `immediate' and `never'.")
-
-(defvar nnmail-expiry-wait-function nil
-  "*Variable that holds function to specify how old articles should be before they are expired.
+can also be `immediate' and `never'."
+  :group 'nnmail-expire
+  :type '(choice (const immediate)
+                (integer :tag "days")
+                (const never)))
+
+(defcustom nnmail-expiry-wait-function nil
+  "Variable that holds function to specify how old articles should be before they are expired.
   The function will be called with the name of the group that the
 expiry is to be performed in, and it should return an integer that
 says how many days an article can be stored before it is considered
@@ -92,14 +147,17 @@ says how many days an article can be stored before it is considered
 
 Eg.:
 
-(setq nnmail-expiry-wait-function
+\(setq nnmail-expiry-wait-function
       (lambda (newsgroup)
         (cond ((string-match \"private\" newsgroup) 31)
               ((string-match \"junk\" newsgroup) 1)
              ((string-match \"important\" newsgroup) 'never)
-             (t 7))))")
+             (t 7))))"
+  :group 'nnmail-expire
+  :type '(choice (const :tag "nnmail-expiry-wait" nil)
+                (function :format "%v" nnmail-)))
 
-(defvar nnmail-spool-file 
+(defcustom nnmail-spool-file 
   (or (getenv "MAIL")
       (concat "/usr/spool/mail/" (user-login-name)))
   "Where the mail backends will look for incoming mail.
@@ -108,49 +166,75 @@ If this variable is nil, no mail backends will read incoming mail.
 If this variable is a list, all files mentioned in this list will be
 used as incoming mailboxes.
 If this variable is a directory (i. e., it's name ends with a \"/\"),
-treat all files in that directory as incoming spool files.")
+treat all files in that directory as incoming spool files."
+  :group 'nnmail-files
+  :type 'file)
 
-(defvar nnmail-crash-box "~/.gnus-crash-box"
-  "*File where Gnus will store mail while processing it.")
+(defcustom nnmail-crash-box "~/.gnus-crash-box"
+  "File where Gnus will store mail while processing it."
+  :group 'nnmail-files
+  :type 'file)
 
-(defvar nnmail-use-procmail nil
+(defcustom nnmail-use-procmail nil
   "*If non-nil, the mail backends will look in `nnmail-procmail-directory' for spool files.
-The file(s) in `nnmail-spool-file' will also be read.")
+The file(s) in `nnmail-spool-file' will also be read."
+  :group 'nnmail-procmail
+  :type 'boolean)
 
-(defvar nnmail-procmail-directory "~/incoming/"
+(defcustom nnmail-procmail-directory "~/incoming/"
   "*When using procmail (and the like), incoming mail is put in this directory.
-The Gnus mail backends will read the mail from this directory.")
+The Gnus mail backends will read the mail from this directory."
+  :group 'nnmail-procmail
+  :type 'directory)
 
-(defvar nnmail-procmail-suffix "\\.spool"
+(defcustom nnmail-procmail-suffix "\\.spool"
   "*Suffix of files created by procmail (and the like).
 This variable might be a suffix-regexp to match the suffixes of
-several files - eg. \".spool[0-9]*\".")
+several files - eg. \".spool[0-9]*\"."
+  :group 'nnmail-procmail
+  :type 'regexp)
 
-(defvar nnmail-resplit-incoming nil
-  "*If non-nil, re-split incoming procmail sorted mail.")
+(defcustom nnmail-resplit-incoming nil
+  "*If non-nil, re-split incoming procmail sorted mail."
+  :group 'nnmail-procmail
+  :type 'boolean)
 
-(defvar nnmail-delete-file-function 'delete-file
-  "Function called to delete files in some mail backends.")
+(defcustom nnmail-delete-file-function 'delete-file
+  "Function called to delete files in some mail backends."
+  :group 'nnmail-files
+  :type 'function)
 
-(defvar nnmail-crosspost-link-function 'add-name-to-file
+(defcustom nnmail-crosspost-link-function 'add-name-to-file
   "Function called to create a copy of a file.
 This is `add-name-to-file' by default, which means that crossposts
 will use hard links.  If your file system doesn't allow hard
-links, you could set this variable to `copy-file' instead.")
+links, you could set this variable to `copy-file' instead."
+  :group 'nnmail-files
+  :type '(radio (function-item add-name-to-file)
+               (function-item copy-file)
+               (function :tag "Other")))
 
-(defvar nnmail-movemail-program "movemail"
+(defcustom nnmail-movemail-program "movemail"
   "*A command to be executed to move mail from the inbox.
 The default is \"movemail\".
 
 This can also be a function.  In that case, the function will be
 called with two parameters -- the name of the INBOX file, and the file
-to be moved to.")
-
-(defvar nnmail-pop-password-required nil
-  "*Non-nil if a password is required when reading mail using POP.")
-
-(defvar nnmail-read-incoming-hook nil
-  "*Hook that will be run after the incoming mail has been transferred.
+to be moved to."
+  :group 'nnmail-files
+  :group 'nnmail-retrieve
+  :type 'string)
+
+(defcustom nnmail-pop-password-required nil
+  "*Non-nil if a password is required when reading mail using POP."
+  :group 'nnmail-retrieve
+  :type 'boolean)
+
+(defcustom nnmail-read-incoming-hook 
+  (if (eq system-type 'windows-nt)
+      '(nnheader-ms-strip-cr)
+    nil)
+  "Hook that will be run after the incoming mail has been transferred.
 The incoming mail is moved from `nnmail-spool-file' (which normally is
 something like \"/usr/spool/mail/$user\") to the user's home
 directory.  This hook is called after the incoming mail box has been
@@ -174,57 +258,79 @@ If you use `display-time', you could use something like this:
            ;; Update the displayed time, since that will clear out
            ;; the flag that says you have mail.
            (when (eq (process-status \"display-time\") 'run)
-             (display-time-filter display-time-process \"\"))))")
-
-(when (eq system-type 'windows-nt)
-  (add-hook 'nnmail-prepare-incoming-hook 'nnheader-ms-strip-cr))
+             (display-time-filter display-time-process \"\"))))"
+  :group 'nnmail-prepare
+  :type 'hook)
 
 ;; Suggested by Erik Selberg <speed@cs.washington.edu>.
-(defvar nnmail-prepare-incoming-hook nil
-  "*Hook called before treating incoming mail.
-The hook is run in a buffer with all the new, incoming mail.")
-
-(defvar nnmail-prepare-incoming-header-hook nil
-  "*Hook called narrowed to the headers of each message.
+(defcustom nnmail-prepare-incoming-hook nil
+  "Hook called before treating incoming mail.
+The hook is run in a buffer with all the new, incoming mail."
+  :group 'nnmail-prepare
+  :type 'hook)
+
+(defcustom nnmail-prepare-incoming-header-hook nil
+  "Hook called narrowed to the headers of each message.
 This can be used to remove excessive spaces (and stuff like
-that) from the headers before splitting and saving the messages.")
-
-(defvar nnmail-prepare-incoming-message-hook nil
-  "*Hook called narrowed to each message.")
-
-(defvar nnmail-list-identifiers nil
-  "Regexp that match list identifiers to be removed.
-This can also be a list of regexps.")
-
-(defvar nnmail-pre-get-new-mail-hook nil
-  "Hook called just before starting to handle new incoming mail.")
-
-(defvar nnmail-post-get-new-mail-hook nil
-  "Hook called just after finishing handling new incoming mail.")
-
-(defvar nnmail-split-hook nil
+that) from the headers before splitting and saving the messages."
+  :group 'nnmail-prepare
+  :type 'hook)
+
+(defcustom nnmail-prepare-incoming-message-hook nil
+  "Hook called narrowed to each message."
+  :group 'nnmail-prepare
+  :type 'hook)
+
+(defcustom nnmail-list-identifiers nil
+  "Regexp that matches list identifiers to be removed.
+This can also be a list of regexps."
+  :group 'nnmail-prepare
+  :type '(choice (const :tag "none" nil)
+                regexp
+                (repeat regexp)))
+
+(defcustom nnmail-pre-get-new-mail-hook nil
+  "Hook called just before starting to handle new incoming mail."
+  :group 'nnmail-retrieve
+  :type 'hook)
+
+(defcustom nnmail-post-get-new-mail-hook nil
+  "Hook called just after finishing handling new incoming mail."
+  :group 'nnmail-retrieve
+  :type 'hook)
+
+(defcustom nnmail-split-hook nil
   "Hook called before deciding where to split an article.
 The functions in this hook are free to modify the buffer
 contents in any way they choose -- the buffer contents are
-discarded after running the split process.")
+discarded after running the split process."
+  :group 'nnmail-split
+  :type 'hook)
 
 ;; Suggested by Mejia Pablo J <pjm9806@usl.edu>.
-(defvar nnmail-tmp-directory nil
-  "*If non-nil, use this directory for temporary storage when reading incoming mail.")
-
-(defvar nnmail-large-newsgroup 50
+(defcustom nnmail-tmp-directory nil
+  "*If non-nil, use this directory for temporary storage.
+Used when reading incoming mail."
+  :group 'nnmail-files
+  :group 'nnmail-retrieve
+  :type '(choice (const :tag "default" nil)
+                (directory :format "%v")))
+
+(defcustom nnmail-large-newsgroup 50
   "*The number of the articles which indicates a large newsgroup.
 If the number of the articles is greater than the value, verbose
-messages will be shown to indicate the current status.")
+messages will be shown to indicate the current status."
+  :group 'nnmail-various
+  :type 'integer)
 
-(defvar nnmail-split-fancy "mail.misc"
-  "*Incoming mail can be split according to this fancy variable.
+(defcustom nnmail-split-fancy "mail.misc"
+  "Incoming mail can be split according to this fancy variable.
 To enable this, set `nnmail-split-methods' to `nnmail-split-fancy'.
 
 The format is this variable is SPLIT, where SPLIT can be one of
 the following:
 
-GROUP: Mail will be stored in GROUP (a string).
+GROUP: Mail will be stored in GROUP (a string).  
 
 \(FIELD VALUE SPLIT): If the message field FIELD (a regexp) contains
   VALUE (a regexp), store the messages as specified by SPLIT.
@@ -235,6 +341,10 @@ GROUP: Mail will be stored in GROUP (a string).
 
 \(& SPLIT...): Process each SPLIT expression.
 
+\(: FUNCTION optional args): Call FUNCTION with the optional args, in
+  the buffer containing the message headers.  The return value FUNCTION
+  should be a split, which is then recursively processed.
+
 FIELD must match a complete field name.  VALUE must match a complete
 word according to the `nnmail-split-fancy-syntax-table' syntax table.
 You can use .* in the regexps to match partial field names or words.
@@ -242,6 +352,9 @@ You can use .* in the regexps to match partial field names or words.
 FIELD and VALUE can also be lisp symbols, in that case they are expanded
 as specified in `nnmail-split-abbrev-alist'.
 
+GROUP can contain \\& and \\N which will substitute from matching
+\\(\\) patterns in the previous VALUE.
+
 Example:
 
 \(setq nnmail-split-methods 'nnmail-split-fancy
@@ -262,25 +375,39 @@ Example:
             ;; People...
             (any \"larsi@ifi\\\\.uio\\\\.no\" \"people.Lars Magne Ingebrigtsen\"))
          ;; Unmatched mail goes to the catch all group.
-         \"misc.misc\"))")
+         \"misc.misc\"))"
+  :group 'nnmail-split
+  ;; Sigh!
+  :type 'sexp)
 
-(defvar nnmail-split-abbrev-alist
+(defcustom nnmail-split-abbrev-alist
   '((any . "from\\|to\\|cc\\|sender\\|apparently-to\\|resent-from\\|resent-to\\|resent-cc")
-    (mail . "mailer-daemon\\|postmaster"))
-  "*Alist of abbreviations allowed in `nnmail-split-fancy'.")
-
-(defvar nnmail-delete-incoming nil
-  "*If non-nil, the mail backends will delete incoming files after splitting.")
-
-(defvar nnmail-message-id-cache-length 1000
+    (mail . "mailer-daemon\\|postmaster\\|uucp"))
+  "Alist of abbreviations allowed in `nnmail-split-fancy'."
+  :group 'nnmail-split
+  :type '(repeat (cons :format "%v" symbol regexp)))
+
+(defcustom nnmail-delete-incoming t
+  "*If non-nil, the mail backends will delete incoming files after
+splitting."
+  :group 'nnmail-retrieve
+  :type 'boolean)
+
+(defcustom nnmail-message-id-cache-length 1000
   "*The approximate number of Message-IDs nnmail will keep in its cache.
 If this variable is nil, no checking on duplicate messages will be
-performed.")
-
-(defvar nnmail-message-id-cache-file "~/.nnmail-cache"
-  "*The file name of the nnmail Message-ID cache.")
-
-(defvar nnmail-treat-duplicates 'warn
+performed."
+  :group 'nnmail-duplicate
+  :type '(choice (const :tag "disable" nil)
+                (integer :format "%v")))
+
+(defcustom nnmail-message-id-cache-file "~/.nnmail-cache"
+  "*The file name of the nnmail Message-ID cache."
+  :group 'nnmail-duplicate
+  :group 'nnmail-files
+  :type 'file)
+
+(defcustom nnmail-treat-duplicates 'warn
   "*If non-nil, nnmail keep a cache of Message-IDs to discover mail duplicates.
 Three values are legal: nil, which means that nnmail is not to keep a
 Message-ID cache; `warn', which means that nnmail should insert extra
@@ -289,7 +416,11 @@ and `delete', which means that nnmail will delete duplicated mails.
 
 This variable can also be a function.  It will be called from a buffer
 narrowed to the article in question with the Message-ID as a
-parameter.  It should return nil, `warn' or `delete'.")
+parameter.  It should return nil, `warn' or `delete'."
+  :group 'nnmail-duplicate
+  :type '(choice (const :tag "off" nil)
+                (const warn)
+                (const delete)))
 
 ;;; Internal variables.
 
@@ -346,7 +477,7 @@ parameter.  It should return nil, `warn' or `delete'.")
        ;; If not, we translate dots into slashes.
        (concat dir (nnheader-replace-chars-in-string group ?. ?/) "/")))
    (or file "")))
-  
+
 (defun nnmail-date-to-time (date)
   "Convert DATE into time."
   (condition-case ()
@@ -358,7 +489,8 @@ parameter.  It should return nil, `warn' or `delete'.")
                       (list
                        (aref t1 2) (aref t1 1) (aref t1 0)
                        (aref d1 2) (aref d1 1) (aref d1 0)
-                       (aref d1 4)))))
+                       (number-to-string
+                        (* 60 (timezone-zone-to-minute (aref d1 4))))))))
     ;; If we get an error, then we just return a 0 time.
     (error (list 0 0))))
 
@@ -394,6 +526,10 @@ parameter.  It should return nil, `warn' or `delete'.")
   (if (not (file-writable-p nnmail-crash-box))
       (gnus-error 1 "Can't write to crash box %s.  Not moving mail."
                  nnmail-crash-box)
+    ;; If the crash box exists and is empty, we delete it.
+    (when (and (file-exists-p nnmail-crash-box)
+              (zerop (nnheader-file-size (file-truename nnmail-crash-box))))
+      (delete-file nnmail-crash-box))
     (let ((inbox (file-truename (expand-file-name inbox)))
          (tofile (file-truename (expand-file-name nnmail-crash-box)))
          movemail popmail errors)
@@ -406,13 +542,15 @@ parameter.  It should return nil, `warn' or `delete'.")
        (when (file-directory-p inbox)
          (setq inbox (expand-file-name (user-login-name) inbox))))
       (if (member inbox nnmail-moved-inboxes)
-         ;; We don't try to move an already moced inbox.
+         ;; We don't try to move an already moved inbox.
          nil
        (if popmail
            (progn
-             (setq nnmail-internal-password nnmail-pop-password)
+             (when (and nnmail-pop-password
+                        (not nnmail-internal-password))
+               (setq nnmail-internal-password nnmail-pop-password))
              (when (and nnmail-pop-password-required
-                        (not nnmail-pop-password))
+                        (not nnmail-internal-password))
                (setq nnmail-internal-password
                      (nnmail-read-passwd
                       (format "Password for %s: "
@@ -448,16 +586,17 @@ parameter.  It should return nil, `warn' or `delete'.")
                     'call-process
                     (append
                      (list
-                      (expand-file-name nnmail-movemail-program
-                                        exec-directory)
+                      (expand-file-name 
+                       nnmail-movemail-program exec-directory)
                       nil errors nil inbox tofile)
                      (when nnmail-internal-password
                        (list nnmail-internal-password))))))
                (if (not (buffer-modified-p errors))
                    ;; No output => movemail won
                    (progn
-                     (or popmail
-                         (set-file-modes tofile nnmail-default-file-modes))
+                     (unless popmail
+                       (when (file-exists-p tofile)
+                         (set-file-modes tofile nnmail-default-file-modes)))
                      (push inbox nnmail-moved-inboxes))
                  (set-buffer errors)
                  ;; There may be a warning about older revisions.  We
@@ -465,8 +604,9 @@ parameter.  It should return nil, `warn' or `delete'.")
                  (goto-char (point-min))
                  (if (search-forward "older revision" nil t)
                      (progn
-                       (or popmail
-                           (set-file-modes tofile nnmail-default-file-modes))
+                       (unless popmail
+                         (when (file-exists-p tofile)
+                           (set-file-modes tofile nnmail-default-file-modes)))
                        (push inbox nnmail-moved-inboxes))
                    ;; Probably a real error.
                    (subst-char-in-region (point-min) (point-max) ?\n ?\  )
@@ -481,6 +621,7 @@ parameter.  It should return nil, `warn' or `delete'.")
                                     (buffer-string)))
                      (error "%s" (buffer-string)))
                    (setq tofile nil)))))))
+       (message "Getting mail from %s...done" inbox)
        (and errors
             (buffer-name errors)
             (kill-buffer errors))
@@ -544,8 +685,8 @@ is a spool.  If not using procmail, return GROUP."
 (defun nnmail-process-babyl-mail-format (func artnum-func)
   (let ((case-fold-search t)
        start message-id content-length do-search end)
+    (goto-char (point-min))
     (while (not (eobp))
-      (goto-char (point-min))
       (re-search-forward
        "\f\n0, *unseen,+\n\\(\\*\\*\\* EOOH \\*\\*\\*\n\\)?" nil t)
       (goto-char (match-end 0))
@@ -558,8 +699,12 @@ is a spool.  If not using procmail, return GROUP."
             (search-forward-regexp "^[^:]*\\( .*\\|\\)$" nil t)
             (search-forward "\1f\f"))
         (point)))
+      ;; Unquote the ">From " line, if any.
+      (goto-char (point-min))
+      (when (looking-at ">From ")
+       (replace-match "X-From-Line: ") )
       (run-hooks 'nnmail-prepare-incoming-header-hook)
-      (widen)
+      (goto-char (point-max))
       ;; Find the Message-ID header.
       (save-excursion
        (if (re-search-backward "^Message-ID:[ \t]*\\(<[^>]*>\\)" nil t)
@@ -587,6 +732,7 @@ is a spool.  If not using procmail, return GROUP."
                      ;; a (possibly) faulty header.
                      (progn (insert "X-") t))))
          (setq do-search t)
+       (widen)
        (if (or (= (+ (point) content-length) (point-max))
                (save-excursion
                  (goto-char (+ (point) content-length))
@@ -595,6 +741,7 @@ is a spool.  If not using procmail, return GROUP."
              (goto-char (+ (point) content-length))
              (setq do-search nil))
          (setq do-search t)))
+      (widen)
       ;; Go to the beginning of the next article - or to the end
       ;; of the buffer.  
       (when do-search
@@ -632,6 +779,37 @@ is a spool.  If not using procmail, return GROUP."
                           (= (following-char) ?\n)))
                     (save-excursion
                       (forward-line 1)
+                      (while (looking-at ">From ")
+                        (forward-line 1))
+                      (looking-at "[^ \t:]+[ \t]*:")))
+           (setq found 'yes)))))
+    (beginning-of-line)
+    (eq found 'yes)))
+
+(defun nnmail-search-unix-mail-delim-backward ()
+  "Put point at the beginning of the current Unix mbox message."
+  ;; Algorithm used to find the the next article in the
+  ;; brain-dead Unix mbox format:
+  ;;
+  ;; 1) Search for "^From ".
+  ;; 2) If we find it, then see whether the previous
+  ;;    line is blank and the next line looks like a header.
+  ;; Then it's possible that this is a mail delim, and we use it.
+  (let ((case-fold-search nil)
+       found)
+    (while (not found)
+      (if (not (re-search-backward "^From " nil t))
+         (setq found 'no)
+       (save-excursion
+         (beginning-of-line)
+         (when (and (or (bobp)
+                        (save-excursion
+                          (forward-line -1)
+                          (= (following-char) ?\n)))
+                    (save-excursion
+                      (forward-line 1)
+                      (while (looking-at ">From ")
+                        (forward-line 1))
                       (looking-at "[^ \t:]+[ \t]*:")))
            (setq found 'yes)))))
     (beginning-of-line)
@@ -789,7 +967,7 @@ FUNC will be called with the buffer narrowed to each mail."
       (set-buffer (get-buffer-create " *nnmail incoming*"))
       (buffer-disable-undo (current-buffer))
       (erase-buffer)
-      (nnheader-insert-file-contents-literally incoming)
+      (nnheader-insert-file-contents incoming)
       (unless (zerop (buffer-size))
        (goto-char (point-min))
        (save-excursion (run-hooks 'nnmail-prepare-incoming-hook))
@@ -837,11 +1015,7 @@ FUNC will be called with the group name to determine the article number."
        (run-hooks 'nnmail-split-hook)
        (if (and (symbolp nnmail-split-methods)
                 (fboundp nnmail-split-methods))
-           ;; `nnmail-split-methods' is a function, so we just call 
-           ;; this function here and use the result.
-           (setq group-art
-                 (mapcar
-                  (lambda (group) (cons group (funcall func group)))
+           (let ((split
                   (condition-case nil
                       (or (funcall nnmail-split-methods)
                           '("bogus"))
@@ -850,6 +1024,13 @@ FUNC will be called with the group name to determine the article number."
                       "Error in `nnmail-split-methods'; using `bogus' mail group")
                      (sit-for 1)
                      '("bogus")))))
+             (unless (equal split '(junk))
+               ;; `nnmail-split-methods' is a function, so we just call 
+               ;; this function here and use the result.
+               (setq group-art
+                     (mapcar
+                      (lambda (group) (cons group (funcall func group)))
+                      split))))
          ;; Go through the split methods to find a match.
          (while (and methods (or nnmail-crosspost (not group-art)))
            (goto-char (point-max))
@@ -857,12 +1038,11 @@ FUNC will be called with the group name to determine the article number."
            (if (or methods
                    (not (equal "" (nth 1 method))))
                (when (and
-                      (condition-case ()
-                          (if (stringp (nth 1 method))
-                              (re-search-backward (cadr method) nil t)
-                            ;; Function to say whether this is a match.
-                            (funcall (nth 1 method) (car method)))
-                        (error nil))
+                      (ignore-errors
+                        (if (stringp (nth 1 method))
+                            (re-search-backward (cadr method) nil t)
+                          ;; Function to say whether this is a match.
+                          (funcall (nth 1 method) (car method))))
                       ;; Don't enter the article into the same 
                       ;; group twice.
                       (not (assoc (car method) group-art)))
@@ -917,7 +1097,7 @@ Return the number of characters in the body."
   "Remove excessive whitespace from all headers."
   (goto-char (point-min))
   (while (re-search-forward "^\\([^ :]+: \\) +" nil t)
-    (replace-match "\\1" t t)))
+    (replace-match "\\1" t)))
 
 (defun nnmail-remove-list-identifiers ()
   "Remove list identifiers from Subject headers."
@@ -926,7 +1106,7 @@ Return the number of characters in the body."
     (when regexp
       (goto-char (point-min))
       (when (re-search-forward
-            (concat "Subject: +\\(Re: +\\)?\\(" regexp "\\) *")
+            (concat "^Subject: +\\(Re: +\\)?\\(" regexp "\\) *")
             nil t)
        (delete-region (match-beginning 2) (match-end 0))))))
 
@@ -1013,7 +1193,11 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
       ;; Someone might want to do a \N sub on this match, so get the
       ;; correct match positions.
       (goto-char (match-end 0))
-      (re-search-backward (nth 1 split) (match-end 1))
+      (let ((value (nth 1 split)))
+       (re-search-backward (if (symbolp value)
+                               (cdr (assq value nnmail-split-abbrev-alist))
+                             value)
+                           (match-end 1)))
       (nnmail-split-it (nth 2 split))))
 
    ;; Not in cache, compute a regexp for the field/value pair.
@@ -1034,35 +1218,38 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
       ;; on the same split, which will find it immediately in the cache.
       (nnmail-split-it split)))))
 
-;;; based on bbdb-auto-expand-newtext, except for getting the
-;;; text from the current buffer, not a string.
-;;; FIX FIX FIX, this could be sped up, if it ends up being slow
 (defun nnmail-expand-newtext (newtext)
-  (let ((pos 0)
-       (len (length newtext))
-       (expanded-newtext ""))
+  (let ((len (length newtext))
+       (pos 0)
+       c expanded beg N did-expand)
     (while (< pos len)
-      (setq expanded-newtext
-           (concat expanded-newtext
-                   (let ((c (aref newtext pos)))
-                     (if (= ?\\ c)
-                         (cond ((= ?\& (setq c (aref newtext
-                                                     (setq pos (1+ pos)))))
-                                (buffer-substring (match-beginning 0)
-                                                  (match-end 0)))
-                               ((and (>= c ?1) 
-                                     (<= c ?9))
-                                ;; return empty string if N'th
-                                ;; sub-regexp did not match:
-                                (let ((n (- c ?0)))
-                                  (if (match-beginning n)
-                                      (buffer-substring (match-beginning n)
-                                                        (match-end n))
-                                    "")))
-                               (t (char-to-string c)))
-                       (char-to-string c)))))
+      (setq beg pos)
+      (while (and (< pos len)
+                 (not (= (aref newtext pos) ?\\)))
+       (setq pos (1+ pos)))
+      (unless (= beg pos)
+       (push (substring newtext beg pos) expanded))
+      (when (< pos len)
+       ;; we hit a \, expand it.
+       (setq did-expand t)
+       (setq pos (1+ pos))
+       (setq c (aref newtext pos))
+       (if (not (or (= c ?\&)
+                    (and (>= c ?1)
+                         (<= c ?9))))
+           ;; \ followed by some character we don't expand
+           (push (char-to-string c) expanded)
+         ;; \& or \N
+         (if (= c ?\&)
+             (setq N 0)
+           (setq N (- c ?0)))
+         (when (match-beginning N)
+           (push (buffer-substring (match-beginning N) (match-end N))
+                 expanded))))
       (setq pos (1+ pos)))
-    expanded-newtext))
+    (if did-expand
+       (apply 'concat (nreverse expanded))
+      newtext)))
 
 ;; Get a list of spool files to read.
 (defun nnmail-get-spool-files (&optional group)
@@ -1108,19 +1295,19 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
                'nconc
                (mapcar 
                 (lambda (file)
-                  (if (file-directory-p file)
+                  (if (and (not (string-match "^po:" file))
+                           (file-directory-p file))
                       (nnheader-directory-regular-files file)
                     (list file)))
                 nnmail-spool-file))
               procmails))
-            ((and (stringp nnmail-spool-file)
-                  (not (file-directory-p nnmail-spool-file)))
-             (cons nnmail-spool-file procmails))
-            ((and (stringp nnmail-spool-file)
-                  (file-directory-p nnmail-spool-file))
-             (nconc
-              (nnheader-directory-regular-files nnmail-spool-file)
-              procmails))
+            ((stringp nnmail-spool-file)
+             (if (and (not (string-match "^po:" nnmail-spool-file))
+                      (file-directory-p nnmail-spool-file))
+                 (nconc 
+                  (nnheader-directory-regular-files nnmail-spool-file)
+                  procmails)
+               (cons nnmail-spool-file procmails)))
             ((eq nnmail-spool-file 'pop)
              (cons (format "po:%s" (user-login-name)) procmails))
             (t
@@ -1133,10 +1320,9 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
   (let (file timestamp file-time)
     (if (or (not (symbol-value (intern (format "%s-group-alist" backend))))
            force
-           (and (setq file (condition-case ()
-                               (symbol-value (intern (format "%s-active-file" 
-                                                             backend)))
-                             (error nil)))
+           (and (setq file (ignore-errors
+                             (symbol-value (intern (format "%s-active-file" 
+                                                           backend)))))
                 (setq file-time (nth 5 (file-attributes file)))
                 (or (not
                      (setq timestamp
@@ -1188,7 +1374,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
             (get-buffer-create " *nnmail message-id cache*")))
       (buffer-disable-undo (current-buffer))
       (when (file-exists-p nnmail-message-id-cache-file)
-       (insert-file-contents nnmail-message-id-cache-file))
+       (nnheader-insert-file-contents nnmail-message-id-cache-file))
       (set-buffer-modified-p nil)
       (current-buffer))))
 
@@ -1307,7 +1493,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
        ;; spool, or the file exists.  We can't check for the
        ;; existence of POPped mail.
        (when (or (string-match "^po:" spool)
-                 (and (file-exists-p spool)
+                 (and (file-exists-p (file-truename spool))
                       (> (nnheader-file-size (file-truename spool)) 0)))
          (nnheader-message 3 "%s: Reading incoming mail..." method)
          (when (and (nnmail-move-inbox spool)
@@ -1326,8 +1512,9 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
                    (if nnmail-tmp-directory
                        (concat 
                         (file-name-as-directory nnmail-tmp-directory)
-                        (file-name-nondirectory (concat temp "Incoming")))
-                     (concat temp "Incoming")))))
+                        (file-name-nondirectory
+                         (concat (file-name-as-directory temp) "Incoming")))
+                     (concat (file-name-as-directory temp) "Incoming")))))
            (rename-file nnmail-crash-box incoming t)
            (push incoming incomings))))
       ;; If we did indeed read any incoming spools, we save all info. 
@@ -1466,18 +1653,15 @@ If ARGS, PROMPT is used as an argument to `format'."
   (interactive)
   (unless nnmail-split-history
     (error "No current split history"))
-  (pop-to-buffer "*nnmail split history*")
-  (buffer-disable-undo (current-buffer))
-  (erase-buffer)
-  (let ((history nnmail-split-history)
-       elem ga)
-    (while (setq elem (pop history))
-      (insert (mapconcat (lambda (ga)
-                          (concat (car ga) ":" (int-to-string (cdr ga))))
-                        elem
-                        ", ")
-             "\n"))
-    (goto-char (point-min))))
+  (with-output-to-temp-buffer "*nnmail split history*"
+    (let ((history nnmail-split-history)
+         elem)
+      (while (setq elem (pop history))
+       (princ (mapconcat (lambda (ga)
+                           (concat (car ga) ":" (int-to-string (cdr ga))))
+                         elem
+                         ", "))
+       (princ "\n")))))
 
 (defun nnmail-new-mail-p (group)
   "Say whether GROUP has new mail."
@@ -1488,7 +1672,14 @@ If ARGS, PROMPT is used as an argument to `format'."
        (setq found t
              his nil)))
     found))
-       
+
+(defun nnmail-pop3-movemail (inbox crashbox)
+  "Function to move mail from INBOX on a pop3 server to file CRASHBOX."
+  (require 'pop3)
+  (let ((pop3-maildrop
+         (substring inbox (match-end (string-match "^po:" inbox)))))
+    (pop3-movemail crashbox)))
+
 (run-hooks 'nnmail-load-hook)
            
 (provide 'nnmail)