(spam-summary-prepare-exit): use spam-process-ham-in-spam-groups
[gnus] / lisp / nnmail.el
index 94193ee..0c08331 100644 (file)
@@ -1,5 +1,5 @@
 ;;; nnmail.el --- mail support functions for the Gnus mail backends
 ;;; nnmail.el --- mail support functions for the Gnus mail backends
-;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001
+;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
 ;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
@@ -28,6 +28,7 @@
 
 (eval-when-compile (require 'cl))
 
 
 (eval-when-compile (require 'cl))
 
+(require 'gnus)                                ; for macro gnus-kill-buffer, at least
 (require 'nnheader)
 (require 'message)
 (require 'custom)
 (require 'nnheader)
 (require 'message)
 (require 'custom)
@@ -36,9 +37,8 @@
 (require 'mm-util)
 
 (eval-and-compile
 (require 'mm-util)
 
 (eval-and-compile
-  (autoload 'gnus-error "gnus-util")
-  (autoload 'gnus-buffer-live-p "gnus-util")
-  (autoload 'gnus-add-buffer "gnus"))
+  (autoload 'gnus-add-buffer "gnus")
+  (autoload 'gnus-kill-buffer "gnus"))
 
 (defgroup nnmail nil
   "Reading mail with Gnus."
 
 (defgroup nnmail nil
   "Reading mail with Gnus."
@@ -57,7 +57,7 @@
   :group 'nnmail)
 
 (defgroup nnmail-split nil
   :group 'nnmail)
 
 (defgroup nnmail-split nil
-  "Organizing the incomming mail in folders."
+  "Organizing the incoming mail in folders."
   :group 'nnmail)
 
 (defgroup nnmail-files nil
   :group 'nnmail)
 
 (defgroup nnmail-files nil
@@ -77,8 +77,7 @@
   "Various mail options."
   :group 'nnmail)
 
   "Various mail options."
   :group 'nnmail)
 
-(defcustom nnmail-split-methods
-  '(("mail.misc" ""))
+(defcustom nnmail-split-methods '(("mail.misc" ""))
   "*Incoming mail will be split according to this variable.
 
 If you'd like, for instance, one mail group for mail from the
   "*Incoming mail will be split according to this variable.
 
 If you'd like, for instance, one mail group for mail from the
@@ -105,7 +104,8 @@ The last element should always have \"\" as the regexp.
 
 This variable can also have a function as its value."
   :group 'nnmail-split
 
 This variable can also have a function as its value."
   :group 'nnmail-split
-  :type '(choice (repeat :tag "Alist" (group (string :tag "Name") regexp))
+  :type '(choice (repeat :tag "Alist" (group (string :tag "Name")
+                                            (choice regexp function)))
                 (function-item nnmail-split-fancy)
                 (function :tag "Other")))
 
                 (function-item nnmail-split-fancy)
                 (function :tag "Other")))
 
@@ -117,8 +117,16 @@ If nil, the first match found will be used."
   :type 'boolean)
 
 (defcustom nnmail-split-fancy-with-parent-ignore-groups nil
   :type 'boolean)
 
 (defcustom nnmail-split-fancy-with-parent-ignore-groups nil
-  "Regexp that matches group names to be ignored when applying
-`nnmail-split-fancy-with-parent'.  This can also be a list of regexps."
+  "Regexp that matches group names to be ignored when applying `nnmail-split-fancy-with-parent'.
+This can also be a list of regexps."
+  :group 'nnmail-split
+  :type '(choice (const :tag "none" nil)
+                (regexp :value ".*")
+                (repeat :value (".*") regexp)))
+
+(defcustom nnmail-cache-ignore-groups nil
+  "Regexp that matches group names to be ignored when inserting message ids into the cache (`nnmail-cache-insert').
+This can also be a list of regexps."
   :group 'nnmail-split
   :type '(choice (const :tag "none" nil)
                 (regexp :value ".*")
   :group 'nnmail-split
   :type '(choice (const :tag "none" nil)
                 (regexp :value ".*")
@@ -159,10 +167,10 @@ can also be `immediate' and `never'."
 
 (defcustom nnmail-expiry-wait-function nil
   "Variable that holds function to specify how old articles should be before they are expired.
 
 (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
-\"old\".  It can also return the values `never' and `immediate'.
+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 \"old\".
+It can also return the values `never' and `immediate'.
 
 Eg.:
 
 
 Eg.:
 
@@ -190,6 +198,39 @@ The return value should be `delete' or a group name (a string)."
                 (function :format "%v" nnmail-)
                 string))
 
                 (function :format "%v" nnmail-)
                 string))
 
+(defcustom nnmail-fancy-expiry-targets nil
+  "Determine expiry target based on articles using fancy techniques.
+
+This is a list of (\"HEADER\" \"REGEXP\" \"TARGET\") entries.  If
+`nnmail-expiry-target' is set to the function
+`nnmail-fancy-expiry-target' and HEADER of the article matches REGEXP,
+the message will be expired to a group determined by invoking
+`format-time-string' with TARGET used as the format string and the
+time extracted from the articles' Date header (if missing the current
+time is used).
+
+In the special cases that HEADER is the symbol `to-from', the regexp
+will try to match against both the From and the To header.
+
+Example:
+
+\(setq nnmail-fancy-expiry-targets
+      '((to-from \"boss\" \"nnfolder:Work\")
+       (\"Subject\" \"IMPORTANT\" \"nnfolder:IMPORTANT.%Y.%b\")
+       (\"from\" \".*\" \"nnfolder:Archive-%Y\")))
+
+In this case, articles containing the string \"boss\" in the To or the
+From header will be expired to the group \"nnfolder:Work\";
+articles containing the sting \"IMPORTANT\" in the Subject header will
+be expired to the group \"nnfolder:IMPORTANT.YYYY.MMM\"; and
+everything else will be expired to \"nnfolder:Archive-YYYY\"."
+  :group 'nnmail-expire
+  :type '(repeat (list (choice :tag "Match against"
+                              (string :tag "Header")
+                              (const to-from))
+                      regexp
+                      (string :tag "Target group format string"))))
+
 (defcustom nnmail-cache-accepted-message-ids nil
   "If non-nil, put Message-IDs of Gcc'd articles into the duplicate cache.
 If non-nil, also update the cache when copy or move articles."
 (defcustom nnmail-cache-accepted-message-ids nil
   "If non-nil, put Message-IDs of Gcc'd articles into the duplicate cache.
 If non-nil, also update the cache when copy or move articles."
@@ -309,12 +350,18 @@ discarded after running the split process."
   :group 'nnmail-split
   :type 'hook)
 
   :group 'nnmail-split
   :type 'hook)
 
+(defcustom nnmail-spool-hook nil
+  "*A hook called when a new article is spooled."
+  :group 'nnmail
+  :type 'hook)
+
 (defcustom nnmail-large-newsgroup 50
 (defcustom nnmail-large-newsgroup 50
-  "*The number of the articles which indicates a large newsgroup.
+  "*The number of the articles which indicates a large newsgroup or nil.
 If the number of the articles is greater than the value, verbose
 messages will be shown to indicate the current status."
   :group 'nnmail-various
 If the number of the articles is greater than the value, verbose
 messages will be shown to indicate the current status."
   :group 'nnmail-various
-  :type 'integer)
+  :type '(choice (const :tag "infinite" nil)
+                 (number :tag "count")))
 
 (defcustom 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.
@@ -328,7 +375,7 @@ GROUP: Mail will be stored in GROUP (a string).
 \(FIELD VALUE [- RESTRICT [- RESTRICT [...]]] SPLIT): If the message
   field FIELD (a regexp) contains VALUE (a regexp), store the messages
   as specified by SPLIT.  If RESTRICT (a regexp) matches some string
 \(FIELD VALUE [- RESTRICT [- RESTRICT [...]]] SPLIT): If the message
   field FIELD (a regexp) contains VALUE (a regexp), store the messages
   as specified by SPLIT.  If RESTRICT (a regexp) matches some string
-  after FIELD and before the end of the matched VALUE, return NIL,
+  after FIELD and before the end of the matched VALUE, return nil,
   otherwise process SPLIT.  Multiple RESTRICTs add up, further
   restricting the possibility of processing SPLIT.
 
   otherwise process SPLIT.  Multiple RESTRICTs add up, further
   restricting the possibility of processing SPLIT.
 
@@ -349,8 +396,8 @@ GROUP: Mail will be stored in GROUP (a string).
 junk: Mail will be deleted.  Use with care!  Do not submerge in water!
   Example:
   (setq nnmail-split-fancy
 junk: Mail will be deleted.  Use with care!  Do not submerge in water!
   Example:
   (setq nnmail-split-fancy
-        '(| (\"Subject\" \"MAKE MONEY FAST\" junk)
-            ...other.rules.omitted...))
+       '(| (\"Subject\" \"MAKE MONEY FAST\" junk)
+           ...other.rules.omitted...))
 
 FIELD must match a complete field name.  VALUE must match a complete
 word according to the `nnmail-split-fancy-syntax-table' syntax table.
 
 FIELD must match a complete field name.  VALUE must match a complete
 word according to the `nnmail-split-fancy-syntax-table' syntax table.
@@ -434,7 +481,7 @@ parameter.  It should return nil, `warn' or `delete'."
                 (const warn)
                 (const delete)))
 
                 (const warn)
                 (const delete)))
 
-(defcustom nnmail-extra-headers nil
+(defcustom nnmail-extra-headers '(To Newsgroups)
   "*Extra headers to parse."
   :version "21.1"
   :group 'nnmail
   "*Extra headers to parse."
   :version "21.1"
   :group 'nnmail
@@ -446,6 +493,25 @@ parameter.  It should return nil, `warn' or `delete'."
   :group 'nnmail
   :type 'integer)
 
   :group 'nnmail
   :type 'integer)
 
+(defcustom nnmail-mail-splitting-charset nil
+  "Default charset to be used when splitting incoming mail."
+  :group 'nnmail
+  :type 'symbol)
+
+(defcustom nnmail-mail-splitting-decodes nil
+  "Whether the nnmail splitting functionality should MIME decode headers."
+  :group 'nnmail
+  :type 'boolean)
+
+(defcustom nnmail-split-fancy-match-partial-words nil
+  "Whether to match partial words when fancy splitting.
+Normally, regexes given in `nnmail-split-fancy' are implicitly surrounded
+by \"\\=\\<...\\>\".  If this variable is true, they are not implicitly\
+ surrounded
+by anything."
+  :group 'nnmail
+  :type 'boolean)
+
 ;;; Internal variables.
 
 (defvar nnmail-article-buffer " *nnmail incoming*"
 ;;; Internal variables.
 
 (defvar nnmail-article-buffer " *nnmail incoming*"
@@ -454,13 +520,12 @@ parameter.  It should return nil, `warn' or `delete'."
 (defvar nnmail-split-history nil
   "List of group/article elements that say where the previous split put messages.")
 
 (defvar nnmail-split-history nil
   "List of group/article elements that say where the previous split put messages.")
 
-(defvar nnmail-split-fancy-syntax-table nil
+(defvar nnmail-split-fancy-syntax-table
+  (let ((table (make-syntax-table)))
+    ;; support the %-hack
+    (modify-syntax-entry ?\% "." table)
+    table)
   "Syntax table used by `nnmail-split-fancy'.")
   "Syntax table used by `nnmail-split-fancy'.")
-(unless (syntax-table-p nnmail-split-fancy-syntax-table)
-  (setq nnmail-split-fancy-syntax-table
-       (copy-syntax-table (standard-syntax-table)))
-  ;; support the %-hack
-  (modify-syntax-entry ?\% "." nnmail-split-fancy-syntax-table))
 
 (defvar nnmail-prepare-save-mail-hook nil
   "Hook called before saving mail.")
 
 (defvar nnmail-prepare-save-mail-hook nil
   "Hook called before saving mail.")
@@ -470,11 +535,6 @@ parameter.  It should return nil, `warn' or `delete'."
 
 \f
 
 
 \f
 
-(defconst nnmail-version "nnmail 1.0"
-  "nnmail version.")
-
-\f
-
 (defun nnmail-request-post (&optional server)
   (mail-send-and-exit nil))
 
 (defun nnmail-request-post (&optional server)
   (mail-send-and-exit nil))
 
@@ -486,7 +546,7 @@ parameter.  It should return nil, `warn' or `delete'."
   "Coding system used in reading inbox")
 
 (defvar nnmail-pathname-coding-system nil
   "Coding system used in reading inbox")
 
 (defvar nnmail-pathname-coding-system nil
-  "*Coding system for pathname.")
+  "*Coding system for file name.")
 
 (defun nnmail-find-file (file)
   "Insert FILE in server buffer safely."
 
 (defun nnmail-find-file (file)
   "Insert FILE in server buffer safely."
@@ -503,7 +563,7 @@ parameter.  It should return nil, `warn' or `delete'."
       (file-error nil))))
 
 (defun nnmail-group-pathname (group dir &optional file)
       (file-error nil))))
 
 (defun nnmail-group-pathname (group dir &optional file)
-  "Make pathname for GROUP."
+  "Make file name for GROUP."
   (concat
    (let ((dir (file-name-as-directory (expand-file-name dir))))
      (setq group (nnheader-replace-duplicate-chars-in-string
   (concat
    (let ((dir (file-name-as-directory (expand-file-name dir))))
      (setq group (nnheader-replace-duplicate-chars-in-string
@@ -670,7 +730,7 @@ If SOURCE is a directory spec, try to return the group name component."
 
 (defsubst nnmail-search-unix-mail-delim ()
   "Put point at the beginning of the next Unix mbox message."
 
 (defsubst nnmail-search-unix-mail-delim ()
   "Put point at the beginning of the next Unix mbox message."
-  ;; Algorithm used to find the the next article in the
+  ;; Algorithm used to find the next article in the
   ;; brain-dead Unix mbox format:
   ;;
   ;; 1) Search for "^From ".
   ;; brain-dead Unix mbox format:
   ;;
   ;; 1) Search for "^From ".
@@ -699,7 +759,7 @@ If SOURCE is a directory spec, try to return the group name component."
 
 (defun nnmail-search-unix-mail-delim-backward ()
   "Put point at the beginning of the current Unix mbox message."
 
 (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
+  ;; Algorithm used to find the next article in the
   ;; brain-dead Unix mbox format:
   ;;
   ;; 1) Search for "^From ".
   ;; brain-dead Unix mbox format:
   ;;
   ;; 1) Search for "^From ".
@@ -734,7 +794,9 @@ If SOURCE is a directory spec, try to return the group name component."
     (if (not (and (re-search-forward "^From " nil t)
                  (goto-char (match-beginning 0))))
        ;; Possibly wrong format?
     (if (not (and (re-search-forward "^From " nil t)
                  (goto-char (match-beginning 0))))
        ;; Possibly wrong format?
-       (error "Error, unknown mail format! (Possibly corrupted.)")
+       (error "Error, unknown mail format! (Possibly corrupted %s `%s'.)"
+              (if (buffer-file-name) "file" "buffer")
+              (or (buffer-file-name) (buffer-name)))
       ;; Carry on until the bitter end.
       (while (not (eobp))
        (setq start (point)
       ;; Carry on until the bitter end.
       (while (not (eobp))
        (setq start (point)
@@ -744,8 +806,8 @@ If SOURCE is a directory spec, try to return the group name component."
         start
         (if (search-forward "\n\n" nil t)
             (1- (point))
         start
         (if (search-forward "\n\n" nil t)
             (1- (point))
-        ;; This will never happen, but just to be on the safe side --
-      ;; if there is no head-body delimiter, we search a bit manually.
+          ;; This will never happen, but just to be on the safe side --
+          ;; if there is no head-body delimiter, we search a bit manually.
           (while (and (looking-at "From \\|[^ \t]+:")
                       (not (eobp)))
             (forward-line 1))
           (while (and (looking-at "From \\|[^ \t]+:")
                       (not (eobp)))
             (forward-line 1))
@@ -777,12 +839,12 @@ If SOURCE is a directory spec, try to return the group name component."
        (goto-char (point-max))
        (widen)
        (setq head-end (point))
        (goto-char (point-max))
        (widen)
        (setq head-end (point))
-   ;; We try the Content-Length value.  The idea: skip over the header
-   ;; separator, then check what happens content-length bytes into the
-    ;; message body.  This should be either the end ot the buffer, the
-       ;; message separator or a blank line followed by the separator.
-      ;; The blank line should probably be deleted.  If neither of the
-       ;; three is met, the content-length header is probably invalid.
+       ;; We try the Content-Length value.  The idea: skip over the header
+       ;; separator, then check what happens content-length bytes into the
+       ;; message body.  This should be either the end of the buffer, the
+       ;; message separator or a blank line followed by the separator.
+       ;; The blank line should probably be deleted.  If neither of the
+       ;; three is met, the content-length header is probably invalid.
        (when content-length
          (forward-line 1)
          (setq skip (+ (point) content-length))
        (when content-length
          (forward-line 1)
          (setq skip (+ (point) content-length))
@@ -831,8 +893,8 @@ If SOURCE is a directory spec, try to return the group name component."
         start
         (if (search-forward "\n\n" nil t)
             (1- (point))
         start
         (if (search-forward "\n\n" nil t)
             (1- (point))
-        ;; This will never happen, but just to be on the safe side --
-      ;; if there is no head-body delimiter, we search a bit manually.
+          ;; This will never happen, but just to be on the safe side --
+          ;; if there is no head-body delimiter, we search a bit manually.
           (while (and (looking-at "From \\|[^ \t]+:")
                       (not (eobp)))
             (forward-line 1))
           (while (and (looking-at "From \\|[^ \t]+:")
                       (not (eobp)))
             (forward-line 1))
@@ -878,7 +940,7 @@ If SOURCE is a directory spec, try to return the group name component."
      (if (search-forward "\n\n" nil t)
         (1- (point))
        ;; This will never happen, but just to be on the safe side --
      (if (search-forward "\n\n" nil t)
         (1- (point))
        ;; This will never happen, but just to be on the safe side --
-      ;; if there is no head-body delimiter, we search a bit manually.
+       ;; if there is no head-body delimiter, we search a bit manually.
        (while (and (looking-at "From \\|[^ \t]+:")
                   (not (eobp)))
         (forward-line 1))
        (while (and (looking-at "From \\|[^ \t]+:")
                   (not (eobp)))
         (forward-line 1))
@@ -942,10 +1004,9 @@ FUNC will be called with the buffer narrowed to each mail."
 (defun nnmail-article-group (func &optional trace)
   "Look at the headers and return an alist of groups that match.
 FUNC will be called with the group name to determine the article number."
 (defun nnmail-article-group (func &optional trace)
   "Look at the headers and return an alist of groups that match.
 FUNC will be called with the group name to determine the article number."
-  (let ((methods nnmail-split-methods)
+  (let ((methods (or nnmail-split-methods '(("bogus" ""))))
        (obuf (current-buffer))
        (obuf (current-buffer))
-       (beg (point-min))
-       end group-art method grp)
+       group-art method grp)
     (if (and (sequencep methods)
             (= (length methods) 1))
        ;; If there is only just one group to put everything in, we
     (if (and (sequencep methods)
             (= (length methods) 1))
        ;; If there is only just one group to put everything in, we
@@ -954,13 +1015,21 @@ FUNC will be called with the group name to determine the article number."
              (list (cons (caar methods) (funcall func (caar methods)))))
       ;; We do actual comparison.
       (save-excursion
              (list (cons (caar methods) (funcall func (caar methods)))))
       ;; We do actual comparison.
       (save-excursion
-       ;; Find headers.
-       (goto-char beg)
-       (setq end (if (search-forward "\n\n" nil t) (point) (point-max)))
+       ;; Copy the article into the work buffer.
        (set-buffer nntp-server-buffer)
        (erase-buffer)
        (set-buffer nntp-server-buffer)
        (erase-buffer)
-       ;; Copy the headers into the work buffer.
-       (insert-buffer-substring obuf beg end)
+       (insert-buffer-substring obuf)
+       ;; Narrow to headers.
+       (narrow-to-region
+        (goto-char (point-min))
+        (if (search-forward "\n\n" nil t)
+            (point)
+          (point-max)))
+       (goto-char (point-min))
+       ;; Decode MIME headers and charsets.
+       (when nnmail-mail-splitting-decodes
+         (let ((mail-parse-charset nnmail-mail-splitting-charset))
+           (mail-decode-encoded-word-region (point-min) (point-max))))
        ;; Fold continuation lines.
        (goto-char (point-min))
        (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
        ;; Fold continuation lines.
        (goto-char (point-min))
        (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
@@ -973,7 +1042,7 @@ FUNC will be called with the group name to determine the article number."
        (while (not (eobp))
          (unless (< (move-to-column nnmail-split-header-length-limit)
                     nnmail-split-header-length-limit)
        (while (not (eobp))
          (unless (< (move-to-column nnmail-split-header-length-limit)
                     nnmail-split-header-length-limit)
-           (delete-region (point) (progn (end-of-line) (point))))
+           (delete-region (point) (gnus-point-at-eol)))
          (forward-line 1))
        ;; Allow washing.
        (goto-char (point-min))
          (forward-line 1))
        ;; Allow washing.
        (goto-char (point-min))
@@ -990,8 +1059,8 @@ FUNC will be called with the group name to determine the article number."
                       (or (funcall nnmail-split-methods)
                           '("bogus"))
                     (error
                       (or (funcall nnmail-split-methods)
                           '("bogus"))
                     (error
-                     (nnheader-message 5
-                                       "Error in `nnmail-split-methods'; using `bogus' mail group")
+                     (nnheader-message
+                      5 "Error in `nnmail-split-methods'; using `bogus' mail group")
                      (sit-for 1)
                      '("bogus")))))
              (setq split (gnus-remove-duplicates split))
                      (sit-for 1)
                      '("bogus")))))
              (setq split (gnus-remove-duplicates split))
@@ -1039,16 +1108,16 @@ FUNC will be called with the group name to determine the article number."
                                  (funcall func (car method)))))))))
        ;; Produce a trace if non-empty.
        (when (and trace nnmail-split-trace)
                                  (funcall func (car method)))))))))
        ;; Produce a trace if non-empty.
        (when (and trace nnmail-split-trace)
-         (let ((trace (nreverse nnmail-split-trace))
-               (restore (current-buffer)))
+         (let ((restore (current-buffer)))
            (nnheader-set-temp-buffer "*Split Trace*")
            (gnus-add-buffer)
            (nnheader-set-temp-buffer "*Split Trace*")
            (gnus-add-buffer)
-           (while trace
-             (insert (car trace) "\n")
-             (setq trace (cdr trace)))
+           (dolist (trace (nreverse nnmail-split-trace))
+             (prin1 trace (current-buffer))
+             (insert "\n"))
            (goto-char (point-min))
            (gnus-configure-windows 'split-trace)
            (set-buffer restore)))
            (goto-char (point-min))
            (gnus-configure-windows 'split-trace)
            (set-buffer restore)))
+       (widen)
        ;; See whether the split methods returned `junk'.
        (if (equal group-art '(junk))
            nil
        ;; See whether the split methods returned `junk'.
        (if (equal group-art '(junk))
            nil
@@ -1121,7 +1190,8 @@ Return the number of characters in the body."
              nil t)
        (delete-region (match-beginning 2) (match-end 0))
        (beginning-of-line))
              nil t)
        (delete-region (match-beginning 2) (match-end 0))
        (beginning-of-line))
-      (when (re-search-forward "^Subject: +\\(\\(R[Ee]: +\\)+\\)R[Ee]: +" nil t)
+      (when (re-search-forward "^Subject: +\\(\\(R[Ee]: +\\)+\\)R[Ee]: +"
+                              nil t)
        (delete-region (match-beginning 1) (match-end 1))
        (beginning-of-line)))))
 
        (delete-region (match-beginning 1) (match-end 1))
        (beginning-of-line)))))
 
@@ -1170,7 +1240,7 @@ to actually put the message in the right group."
 
 (defun nnmail-split-fancy ()
   "Fancy splitting method.
 
 (defun nnmail-split-fancy ()
   "Fancy splitting method.
-See the documentation for the variable `nnmail-split-fancy' for documentation."
+See the documentation for the variable `nnmail-split-fancy' for details."
   (let ((syntab (syntax-table)))
     (unwind-protect
        (progn
   (let ((syntab (syntax-table)))
     (unwind-protect
        (progn
@@ -1192,7 +1262,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
      ;; A group name.  Do the \& and \N subs into the string.
      ((stringp split)
       (when nnmail-split-tracing
      ;; A group name.  Do the \& and \N subs into the string.
      ((stringp split)
       (when nnmail-split-tracing
-       (push (format "\"%s\"" split) nnmail-split-trace))
+       (push split nnmail-split-trace))
       (list (nnmail-expand-newtext split)))
 
      ;; Junk the message.
       (list (nnmail-expand-newtext split)))
 
      ;; Junk the message.
@@ -1215,6 +1285,8 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
 
      ;; Builtin : operation.
      ((eq (car split) ':)
 
      ;; Builtin : operation.
      ((eq (car split) ':)
+      (when nnmail-split-tracing
+       (push split nnmail-split-trace))
       (nnmail-split-it (save-excursion (eval (cdr split)))))
 
      ;; Builtin ! operation.
       (nnmail-split-it (save-excursion (eval (cdr split)))))
 
      ;; Builtin ! operation.
@@ -1231,13 +1303,13 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
        (while (and (goto-char end-point)
                    (re-search-backward (cdr cached-pair) nil t))
          (when nnmail-split-tracing
        (while (and (goto-char end-point)
                    (re-search-backward (cdr cached-pair) nil t))
          (when nnmail-split-tracing
-           (push (cdr cached-pair) nnmail-split-trace))
+           (push split nnmail-split-trace))
          (let ((split-rest (cddr split))
                (end (match-end 0))
          (let ((split-rest (cddr split))
                (end (match-end 0))
-             ;; The searched regexp is \(\(FIELD\).*\)\(VALUE\).  So,
-               ;; start-of-value is the the point just before the
-              ;; beginning of the value, whereas after-header-name is
-               ;; the point just after the field name.
+               ;; The searched regexp is \(\(FIELD\).*\)\(VALUE\).
+               ;; So, start-of-value is the point just before the
+               ;; beginning of the value, whereas after-header-name
+               ;; is the point just after the field name.
                (start-of-value (match-end 1))
                (after-header-name (match-end 2)))
            ;; Start the next search just before the beginning of the
                (start-of-value (match-end 1))
                (after-header-name (match-end 2)))
            ;; Start the next search just before the beginning of the
@@ -1261,11 +1333,11 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
              (let ((value (nth 1 split)))
                (if (symbolp value)
                    (setq value (cdr (assq value nnmail-split-abbrev-alist))))
              (let ((value (nth 1 split)))
                (if (symbolp value)
                    (setq value (cdr (assq value nnmail-split-abbrev-alist))))
-       ;; Someone might want to do a \N sub on this match, so get the
+               ;; Someone might want to do a \N sub on this match, so get the
                ;; correct match positions.
                (re-search-backward value start-of-value))
              (dolist (sp (nnmail-split-it (car split-rest)))
                ;; correct match positions.
                (re-search-backward value start-of-value))
              (dolist (sp (nnmail-split-it (car split-rest)))
-               (unless (memq sp split-result)
+               (unless (member sp split-result)
                  (push sp split-result))))))
        split-result))
 
                  (push sp split-result))))))
        split-result))
 
@@ -1273,8 +1345,9 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
      (t
       (let* ((field (nth 0 split))
             (value (nth 1 split))
      (t
       (let* ((field (nth 0 split))
             (value (nth 1 split))
-            partial-front regexp
-            partial-rear  regexp)
+            partial-front
+            partial-rear
+            regexp)
        (if (symbolp value)
            (setq value (cdr (assq value nnmail-split-abbrev-alist))))
        (if (and (>= (length value) 2)
        (if (symbolp value)
            (setq value (cdr (assq value nnmail-split-abbrev-alist))))
        (if (and (>= (length value) 2)
@@ -1286,6 +1359,9 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
                 (string= ".*" (substring value -2)))
            (setq value (substring value 0 -2)
                  partial-rear ""))
                 (string= ".*" (substring value -2)))
            (setq value (substring value 0 -2)
                  partial-rear ""))
+       (when nnmail-split-fancy-match-partial-words
+         (setq partial-front ""
+               partial-rear ""))
        (setq regexp (concat "^\\(\\("
                             (if (symbolp field)
                                 (cdr (assq field nnmail-split-abbrev-alist))
        (setq regexp (concat "^\\(\\("
                             (if (symbolp field)
                                 (cdr (assq field nnmail-split-abbrev-alist))
@@ -1410,51 +1486,50 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
                           nnmail-message-id-cache-file nil 'silent)
       (set-buffer-modified-p nil)
       (setq nnmail-cache-buffer nil)
                           nnmail-message-id-cache-file nil 'silent)
       (set-buffer-modified-p nil)
       (setq nnmail-cache-buffer nil)
-      (kill-buffer (current-buffer)))))
+      (gnus-kill-buffer (current-buffer)))))
 
 ;; Compiler directives.
 (defvar group)
 (defvar group-art-list)
 (defvar group-art)
 
 ;; Compiler directives.
 (defvar group)
 (defvar group-art-list)
 (defvar group-art)
-(defun nnmail-cache-insert (id)
+(defun nnmail-cache-insert (id grp)
+  (run-hook-with-args 'nnmail-spool-hook 
+                     id grp)
   (when nnmail-treat-duplicates
     ;; Store some information about the group this message is written
   (when nnmail-treat-duplicates
     ;; Store some information about the group this message is written
-    ;; to.  This function might have been called from various places.
-    ;; Sometimes, a function up in the calling sequence has an
-    ;; argument GROUP which is bound to a string, the group name.  At
-    ;; other times, there is a function up in the calling sequence
-    ;; which has an argument GROUP-ART which is a list of pairs, and
-    ;; the car of a pair is a group name.  Should we check that the
-    ;; length of the list is equal to 1? -- kai
-    (let ((g nil))
-      (cond ((and (boundp 'group) group)
-            (setq g group))
-           ((and (boundp 'group-art-list) group-art-list
-                 (listp group-art-list))
-            (setq g (caar group-art-list)))
-           ((and (boundp 'group-art) group-art (listp group-art))
-            (setq g (caar group-art)))
-           (t (setq g "")))
-      (unless (gnus-buffer-live-p nnmail-cache-buffer)
-       (nnmail-cache-open))
-      (save-excursion
-       (set-buffer nnmail-cache-buffer)
-       (goto-char (point-max))
-       (if (and g (not (string= "" g))
-                (gnus-methods-equal-p gnus-command-method
-                                      (nnmail-cache-primary-mail-backend)))
-           (insert id "\t" g "\n")
-         (insert id "\n"))))))
+    ;; to.  This is passed in as the grp argument -- all locations this
+    ;; has been called from have been checked and the group is available.
+    ;; The only ambiguous case is nnmail-check-duplication which will only
+    ;; pass the first (of possibly >1) group which matches. -Josh
+    (unless (gnus-buffer-live-p nnmail-cache-buffer)
+      (nnmail-cache-open))
+    (save-excursion
+      (set-buffer nnmail-cache-buffer)
+      (goto-char (point-max))
+      (if (and grp (not (string= "" grp))
+              (gnus-methods-equal-p gnus-command-method
+                                    (nnmail-cache-primary-mail-backend)))
+         (let ((regexp (if (consp nnmail-cache-ignore-groups)
+                           (mapconcat 'identity nnmail-cache-ignore-groups
+                                      "\\|")
+                         nnmail-cache-ignore-groups)))
+           (unless (and regexp (string-match regexp grp))
+             (insert id "\t" grp "\n")))
+       (insert id "\n")))))
 
 (defun nnmail-cache-primary-mail-backend ()
   (let ((be-list (cons gnus-select-method gnus-secondary-select-methods))
        (be nil)
 
 (defun nnmail-cache-primary-mail-backend ()
   (let ((be-list (cons gnus-select-method gnus-secondary-select-methods))
        (be nil)
-       (res nil))
+       (res nil)
+        (get-new-mail nil))
     (while (and (null res) be-list)
       (setq be (car be-list))
       (setq be-list (cdr be-list))
       (when (and (gnus-method-option-p be 'respool)
     (while (and (null res) be-list)
       (setq be (car be-list))
       (setq be-list (cdr be-list))
       (when (and (gnus-method-option-p be 'respool)
-                (eval (intern (format "%s-get-new-mail" (car be)))))
+                 (setq get-new-mail
+                       (intern (format "%s-get-new-mail" (car be))))
+                 (boundp get-new-mail)
+                (symbol-value get-new-mail))
        (setq res be)))
     res))
 
        (setq res be)))
     res))
 
@@ -1468,17 +1543,16 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
       (when (search-backward id nil t)
        (beginning-of-line)
        (skip-chars-forward "^\n\r\t")
       (when (search-backward id nil t)
        (beginning-of-line)
        (skip-chars-forward "^\n\r\t")
-       (unless (eolp)
+       (unless (looking-at "[\r\n]")
          (forward-char 1)
          (forward-char 1)
-         (buffer-substring (point)
-                           (progn (end-of-line) (point))))))))
+         (buffer-substring (point) (gnus-point-at-eol)))))))
 
 ;; Function for nnmail-split-fancy: look up all references in the
 ;; cache and if a match is found, return that group.
 (defun nnmail-split-fancy-with-parent ()
   "Split this message into the same group as its parent.
 This function can be used as an entry in `nnmail-split-fancy', for
 
 ;; Function for nnmail-split-fancy: look up all references in the
 ;; cache and if a match is found, return that group.
 (defun nnmail-split-fancy-with-parent ()
   "Split this message into the same group as its parent.
 This function can be used as an entry in `nnmail-split-fancy', for
-example like this: (: nnmail-split-fancy)
+example like this: (: nnmail-split-fancy-with-parent)
 For a message to be split, it looks for the parent message in the
 References or In-Reply-To header and then looks in the message id
 cache file (given by the variable `nnmail-message-id-cache-file') to
 For a message to be split, it looks for the parent message in the
 References or In-Reply-To header and then looks in the message id
 cache file (given by the variable `nnmail-message-id-cache-file') to
@@ -1501,7 +1575,7 @@ See the Info node `(gnus)Fancy Mail Splitting' for more details."
        (nnmail-cache-open))
       (mapcar (lambda (x)
                (setq res (or (nnmail-cache-fetch-group x) res))
        (nnmail-cache-open))
       (mapcar (lambda (x)
                (setq res (or (nnmail-cache-fetch-group x) res))
-               (when (or (string= "drafts" res)
+               (when (or (member res '("delayed" "drafts" "queue"))
                          (and regexp res (string-match regexp res)))
                  (setq res nil)))
              references)
                          (and regexp res (string-match regexp res)))
                  (setq res nil)))
              references)
@@ -1529,7 +1603,7 @@ See the Info node `(gnus)Fancy Mail Splitting' for more details."
                   (cond
                    ((memq nnmail-treat-duplicates '(warn delete))
                     nnmail-treat-duplicates)
                   (cond
                    ((memq nnmail-treat-duplicates '(warn delete))
                     nnmail-treat-duplicates)
-                   ((nnheader-functionp nnmail-treat-duplicates)
+                   ((functionp nnmail-treat-duplicates)
                     (funcall nnmail-treat-duplicates message-id))
                    (t
                     nnmail-treat-duplicates))))
                     (funcall nnmail-treat-duplicates message-id))
                    (t
                     nnmail-treat-duplicates))))
@@ -1546,7 +1620,7 @@ See the Info node `(gnus)Fancy Mail Splitting' for more details."
      ((not duplication)
       (funcall func (setq group-art
                          (nreverse (nnmail-article-group artnum-func))))
      ((not duplication)
       (funcall func (setq group-art
                          (nreverse (nnmail-article-group artnum-func))))
-      (nnmail-cache-insert message-id))
+      (nnmail-cache-insert message-id (caar group-art)))
      ((eq action 'delete)
       (setq group-art nil))
      ((eq action 'warn)
      ((eq action 'delete)
       (setq group-art nil))
      ((eq action 'warn)
@@ -1695,10 +1769,41 @@ See the Info node `(gnus)Fancy Mail Splitting' for more details."
   (let (nnmail-cache-accepted-message-ids)
     ;; Don't enter Message-IDs into cache.
     ;; Let users hack it in TARGET function.
   (let (nnmail-cache-accepted-message-ids)
     ;; Don't enter Message-IDs into cache.
     ;; Let users hack it in TARGET function.
-    (when (nnheader-functionp target)
+    (when (functionp target)
       (setq target (funcall target group)))
     (unless (eq target 'delete)
       (setq target (funcall target group)))
     (unless (eq target 'delete)
-      (gnus-request-accept-article target nil nil t))))
+      (when (or (gnus-request-group target)
+               (gnus-request-create-group target))
+       (let ((group-art (gnus-request-accept-article target nil nil t)))
+         (when (consp group-art)
+           (gnus-group-mark-article-read target (cdr group-art))))))))
+
+(defun nnmail-fancy-expiry-target (group)
+  "Returns a target expiry group determined by `nnmail-fancy-expiry-targets'."
+  (let* (header
+        (case-fold-search nil)
+        (from (or (message-fetch-field "from") ""))
+        (to (or (message-fetch-field "to") ""))
+        (date (date-to-time
+               (or (message-fetch-field "date") (current-time-string))))
+        (target 'delete))
+    (dolist (regexp-target-pair (reverse nnmail-fancy-expiry-targets) target)
+      (setq header (car regexp-target-pair))
+      (cond
+       ;; If the header is to-from then match against the
+       ;; To or From header
+       ((and (equal header 'to-from)
+            (or (string-match (cadr regexp-target-pair) from)
+                (and (string-match message-dont-reply-to-names from)
+                     (string-match (cadr regexp-target-pair) to))))
+       (setq target (format-time-string (caddr regexp-target-pair) date)))
+       ((and (not (equal header 'to-from))
+            (string-match (cadr regexp-target-pair)
+                          (or
+                           (message-fetch-field header)
+                           "")))
+       (setq target
+             (format-time-string (caddr regexp-target-pair) date)))))))
 
 (defun nnmail-check-syntax ()
   "Check (and modify) the syntax of the message in the current buffer."
 
 (defun nnmail-check-syntax ()
   "Check (and modify) the syntax of the message in the current buffer."
@@ -1795,7 +1900,7 @@ See the Info node `(gnus)Fancy Mail Splitting' for more details."
   "Remove all instances of GROUP from `nnmail-split-history'."
   (let ((history nnmail-split-history))
     (while history
   "Remove all instances of GROUP from `nnmail-split-history'."
   (let ((history nnmail-split-history))
     (while history
-      (setcar history (gnus-delete-if (lambda (e) (string= (car e) group))
+      (setcar history (gnus-remove-if (lambda (e) (string= (car e) group))
                                      (car history)))
       (pop history))
     (setq nnmail-split-history (delq nil nnmail-split-history))))
                                      (car history)))
       (pop history))
     (setq nnmail-split-history (delq nil nnmail-split-history))))