*** empty log message ***
[gnus] / lisp / nnmail.el
index b0f26dc..741d84f 100644 (file)
@@ -106,7 +106,9 @@ Eg.:
 This variable is \"/usr/spool/mail/$user\" by default.
 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.")
+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.")
 
 (defvar nnmail-crash-box "~/.gnus-crash-box"
   "*File where Gnus will store mail while processing it.")
@@ -158,7 +160,7 @@ running (\"xwatch\", etc.)
 Eg.
 
 \(add-hook 'nnmail-read-incoming-hook 
-          (lambda () 
+          (lambda ()
             (start-process \"mailsend\" nil 
                            \"/local/bin/mailsend\" \"read\" \"mbox\")))
 
@@ -171,8 +173,8 @@ If you use `display-time', you could use something like this:
          (lambda ()
            ;; Update the displayed time, since that will clear out
            ;; the flag that says you have mail.
-           (if (eq (process-status \"display-time\") 'run)
-               (display-time-filter display-time-process \"\"))))") 
+           (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))
@@ -222,7 +224,7 @@ 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.
@@ -233,6 +235,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.
@@ -240,11 +246,14 @@ 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
       nnmail-split-fancy
-      ;; Messages from the mailer deamon are not crossposted to any of
+      ;; Messages from the mailer daemon are not crossposted to any of
       ;; the ordinary groups.  Warnings are put in a separate group
       ;; from real errors.
       '(| (\"from\" mail (| (\"subject\" \"warn.*\" \"mail.warning\")
@@ -264,7 +273,7 @@ Example:
 
 (defvar nnmail-split-abbrev-alist
   '((any . "from\\|to\\|cc\\|sender\\|apparently-to\\|resent-from\\|resent-to\\|resent-cc")
-    (mail . "mailer-daemon\\|postmaster"))
+    (mail . "mailer-daemon\\|postmaster\\|uucp"))
   "*Alist of abbreviations allowed in `nnmail-split-fancy'.")
 
 (defvar nnmail-delete-incoming nil
@@ -347,15 +356,18 @@ parameter.  It should return nil, `warn' or `delete'.")
   
 (defun nnmail-date-to-time (date)
   "Convert DATE into time."
-  (let* ((d1 (timezone-parse-date date))
-        (t1 (timezone-parse-time (aref d1 3))))
-    (apply 'encode-time
-          (mapcar (lambda (el)
-                    (and el (string-to-number el)))
-                  (list
-                   (aref t1 2) (aref t1 1) (aref t1 0)
-                   (aref d1 2) (aref d1 1) (aref d1 0)
-                   (aref d1 4))))))
+  (condition-case ()
+      (let* ((d1 (timezone-parse-date date))
+            (t1 (timezone-parse-time (aref d1 3))))
+       (apply 'encode-time
+              (mapcar (lambda (el)
+                        (and el (string-to-number el)))
+                      (list
+                       (aref t1 2) (aref t1 1) (aref t1 0)
+                       (aref d1 2) (aref d1 1) (aref d1 0)
+                       (aref d1 4)))))
+    ;; If we get an error, then we just return a 0 time.
+    (error (list 0 0))))
 
 (defun nnmail-time-less (t1 t2)
   "Say whether time T1 is less than time T2."
@@ -367,7 +379,7 @@ parameter.  It should return nil, `warn' or `delete'.")
   "Convert DAYS into time."
   (let* ((seconds (* 1.0 days 60 60 24))
         (rest (expt 2 16))
-        (ms (condition-case nil (round (/ seconds rest)) 
+        (ms (condition-case nil (round (/ seconds rest))
               (range-error (expt 2 16)))))
     (list ms (condition-case nil (round (- seconds (* ms rest)))
               (range-error (expt 2 16))))))
@@ -378,7 +390,8 @@ parameter.  It should return nil, `warn' or `delete'.")
     ;; Convert date strings to internal time.
     (setq time (nnmail-date-to-time time)))
   (let* ((current (current-time))
-        (rest (if (< (nth 1 current) (nth 1 time)) (expt 2 16))))
+        (rest (when (< (nth 1 current) (nth 1 time))
+                (expt 2 16))))
     (list (- (+ (car current) (if rest -1 0)) (car time))
          (- (+ (or rest 0) (nth 1 current)) (nth 1 time)))))
 
@@ -388,27 +401,29 @@ 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)
-      ;; If getting from mail spool directory,
-      ;; use movemail to move rather than just renaming,
-      ;; so as to interlock with the mailer.
-      (unless (setq popmail (string-match "^po:" (file-name-nondirectory inbox)))
-       (setq movemail t))
-      (when popmail 
-       (setq inbox (file-name-nondirectory inbox)))
-      (when (and movemail
-                ;; On some systems, /usr/spool/mail/foo is a directory
-                ;; and the actual inbox is /usr/spool/mail/foo/foo.
-                (file-directory-p inbox))
-       (setq inbox (expand-file-name (user-login-name) inbox)))
+      (if (setq popmail (string-match
+                        "^po:" (file-name-nondirectory inbox)))
+         (setq inbox (file-name-nondirectory inbox))
+       (setq movemail t)
+       ;; On some systems, /usr/spool/mail/foo is a directory
+       ;; and the actual inbox is /usr/spool/mail/foo/foo.
+       (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.
          nil
        (if popmail
            (progn
              (setq nnmail-internal-password nnmail-pop-password)
-             (when (and nnmail-pop-password-required (not nnmail-pop-password))
+             (when (and nnmail-pop-password-required
+                        (not nnmail-pop-password))
                (setq nnmail-internal-password
                      (nnmail-read-passwd
                       (format "Password for %s: "
@@ -429,23 +444,10 @@ parameter.  It should return nil, `warn' or `delete'.")
               (not (file-exists-p inbox)))
          ;; There is no inbox.
          (setq tofile nil))
-        ((and (not movemail) (not popmail))
-         ;; Try copying.  If that fails (perhaps no space),
-         ;; rename instead.
-         (condition-case nil
-             (copy-file inbox tofile nil)
-           (error
-            ;; Third arg is t so we can replace existing file TOFILE.
-            (rename-file inbox tofile t)))
-         (push inbox nnmail-moved-inboxes)
-         ;; Make the real inbox file empty.
-         ;; Leaving it deleted could cause lossage
-         ;; because mailers often won't create the file.
-         (condition-case ()
-             (write-region (point) (point) inbox)
-           (file-error nil)))
         (t
-         ;; Use movemail.
+         ;; If getting from mail spool directory, use movemail to move
+         ;; rather than just renaming, so as to interlock with the
+         ;; mailer.
          (unwind-protect
              (save-excursion
                (setq errors (generate-new-buffer " *nnmail loss*"))
@@ -457,15 +459,16 @@ 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
+                       (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
@@ -473,8 +476,8 @@ 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
+                         (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 ?\  )
@@ -514,20 +517,16 @@ nn*-request-list should have been called before calling this function."
 (defun nnmail-save-active (group-assoc file-name)
   "Save GROUP-ASSOC in ACTIVE-FILE."
   (when file-name
-    (let (group)
-      (save-excursion
-       (set-buffer (get-buffer-create " *nnmail active*"))
-       (buffer-disable-undo (current-buffer))
-       (erase-buffer)
-       (while group-assoc
-         (setq group (pop group-assoc))
-         (insert (format "%s %d %d y\n" (car group) (cdadr group) 
-                         (caadr group))))
-       (unless (file-exists-p (file-name-directory file-name))
-         (make-directory (file-name-directory file-name) t))
-       (nnmail-write-region
-        1 (point-max) (expand-file-name file-name) nil 'nomesg)
-       (kill-buffer (current-buffer))))))
+    (nnheader-temp-write file-name
+      (nnmail-generate-active group-assoc))))
+
+(defun nnmail-generate-active (alist)
+  "Generate an active file from group-alist ALIST."
+  (erase-buffer)
+  (let (group)
+    (while (setq group (pop alist))
+      (insert (format "%s %d %d y\n" (car group) (cdadr group)
+                     (caadr group))))))
 
 (defun nnmail-get-split-group (file group)
   "Find out whether this FILE is to be split into GROUP only.
@@ -623,26 +622,39 @@ is a spool.  If not using procmail, return GROUP."
       (goto-char end))))
 
 (defun nnmail-search-unix-mail-delim ()
-  "Put point at the beginning of the next message."
-  (let ((case-fold-search t)
-       (delim (concat "^" message-unix-mail-delimiter))
+  "Put point at the beginning of the next 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 (re-search-forward delim nil t)
-         (when (or (looking-at "[^\n :]+ *:")
-                   (looking-at delim)
-                   (looking-at (concat ">" message-unix-mail-delimiter)))
-           (forward-line -1)
-           (setq found 'yes))
-       (setq found 'no)))
+      (if (not (re-search-forward "^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)
     (eq found 'yes)))
 
 (defun nnmail-process-unix-mail-format (func artnum-func)
   (let ((case-fold-search t)
-       (delim (concat "^" message-unix-mail-delimiter))
        start message-id content-length end skip head-end)
     (goto-char (point-min))
-    (if (not (and (re-search-forward delim nil t)
+    (if (not (and (re-search-forward "^From " nil t)
                  (goto-char (match-beginning 0))))
        ;; Possibly wrong format?
        (error "Error, unknown mail format! (Possibly corrupted.)")
@@ -701,10 +713,9 @@ is a spool.  If not using procmail, return GROUP."
          (cond ((or (= skip (point-max))
                     (= (1+ skip) (point-max)))
                 (setq end (point-max)))
-               ((looking-at delim)
+               ((looking-at "From ")
                 (setq end skip))
-               ((looking-at
-                 (concat "[ \t]*\n\\(" delim "\\)"))
+               ((looking-at "[ \t]*\n\\(From \\)")
                 (setq end (match-beginning 1)))
                (t (setq end nil))))
        (if end
@@ -805,7 +816,8 @@ FUNC will be called with the buffer narrowed to each mail."
               (nnmail-process-mmdf-mail-format func artnum-func))
              (t
               (nnmail-process-unix-mail-format func artnum-func))))
-      (if exit-func (funcall exit-func))
+      (when exit-func
+       (funcall exit-func))
       (kill-buffer (current-buffer)))))
 
 ;; Mail crossposts suggested by Brian Edmonds <edmonds@cs.ubc.ca>. 
@@ -858,22 +870,21 @@ 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)))
-                 (push (cons (car method) (funcall func (car method))) 
+                 (push (cons (car method) (funcall func (car method)))
                        group-art))
              ;; This is the final group, which is used as a 
              ;; catch-all.
              (unless group-art
                (setq group-art 
-                     (list (cons (car method) 
+                     (list (cons (car method)
                                  (funcall func (car method)))))))))
        ;; See whether the split methods returned `junk'.
        (if (equal group-art '(junk))
@@ -886,7 +897,7 @@ Return the number of characters in the body."
   (let (lines chars)
     (save-excursion
       (goto-char (point-min))
-      (when (search-forward "\n\n" nil t) 
+      (when (search-forward "\n\n" nil t)
        (setq chars (- (point-max) (point)))
        (setq lines (count-lines (point) (point-max)))
        (forward-char -1)
@@ -901,10 +912,10 @@ Return the number of characters in the body."
   "Insert an Xref line based on the (group . article) alist."
   (save-excursion
     (goto-char (point-min))
-    (when (search-forward "\n\n" nil t) 
+    (when (search-forward "\n\n" nil t)
       (forward-char -1)
       (when (re-search-backward "^Xref: " nil t)
-       (delete-region (match-beginning 0) 
+       (delete-region (match-beginning 0)
                       (progn (forward-line 1) (point))))
       (insert (format "Xref: %s" (system-name)))
       (while group-alist
@@ -964,43 +975,113 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
 (defun nnmail-split-it (split)
   ;; Return a list of groups matching SPLIT.
   (cond
+   ;; nil split
+   ((null split)
+    nil)
+
+   ;; A group name.  Do the \& and \N subs into the string.
    ((stringp split)
-    ;; A group.
-    (list split))
+    (list (nnmail-expand-newtext split)))
+
+   ;; Junk the message.
    ((eq split 'junk)
-    ;; Junk this.
     (list 'junk))
+
+   ;; Builtin & operation.
    ((eq (car split) '&)
     (apply 'nconc (mapcar 'nnmail-split-it (cdr split))))
+
+   ;; Builtin | operation.
    ((eq (car split) '|)
     (let (done)
       (while (and (not done) (cdr split))
        (setq split (cdr split)
              done (nnmail-split-it (car split))))
       done))
+
+   ;; Builtin : operation.
+   ((eq (car split) ':)
+    (nnmail-split-it (eval (cdr split))))
+
+   ;; Check the cache for the regexp for this split.
+   ;; FIX FIX FIX could avoid calling assq twice here
    ((assq split nnmail-split-cache)
-    ;; A compiled match expression.
     (goto-char (point-max))
-    (if (re-search-backward (cdr (assq split nnmail-split-cache)) nil t)
-       (nnmail-split-it (nth 2 split))))
+    ;; FIX FIX FIX problem with re-search-backward is that if you have
+    ;; a split: (from "foo-\\(bar\\|baz\\)@gnus.org "mail.foo.\\1")
+    ;; and someone mails a message with 'To: foo-bar@gnus.org' and
+    ;; 'CC: foo-baz@gnus.org', we'll pick 'mail.foo.baz' as the group
+    ;; if the cc line is a later header, even though the other choice
+    ;; is probably better.  Also, this routine won't do a crosspost
+    ;; when there are two different matches.
+    ;; I guess you could just make this more determined, and it could
+    ;; look for still more matches prior to this one, and recurse
+    ;; on each of the multiple matches hit.  Of course, then you'd
+    ;; want to make sure that nnmail-article-group or nnmail-split-fancy
+    ;; removed duplicates, since there might be more of those.
+    ;; I guess we could also remove duplicates in the & split case, since
+    ;; that's the only thing that can introduce them.
+    (when (re-search-backward (cdr (assq split nnmail-split-cache)) nil t)
+      ;; Someone might want to do a \N sub on this match, so get the
+      ;; correct match positions.
+      (goto-char (match-end 0))
+      (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.
    (t
-    ;; An uncompiled match.
     (let* ((field (nth 0 split))
           (value (nth 1 split))
-          (regexp (concat "^\\(
+          (regexp (concat "^\\(\\("
                           (if (symbolp field)
                               (cdr (assq field nnmail-split-abbrev-alist))
                             field)
-                          "\\):.*\\<\\("
+                          "\\):.*\\)\\<\\("
                           (if (symbolp value)
                               (cdr (assq value nnmail-split-abbrev-alist))
                             value)
                           "\\)\\>")))
-      (setq nnmail-split-cache 
-           (cons (cons split regexp) nnmail-split-cache))
-      (goto-char (point-max))
-      (if (re-search-backward regexp nil t)
-         (nnmail-split-it (nth 2 split)))))))
+      (push (cons split regexp) nnmail-split-cache)
+      ;; Now that it's in the cache, just call nnmail-split-it again
+      ;; on the same split, which will find it immediately in the cache.
+      (nnmail-split-it split)))))
+
+(defun nnmail-expand-newtext (newtext)
+  (let ((len (length newtext))
+       (pos 0)
+       c expanded beg N did-expand)
+    (while (< pos len)
+      (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)))
+    (if did-expand
+       (apply 'concat (nreverse expanded))
+      newtext)))
 
 ;; Get a list of spool files to read.
 (defun nnmail-get-spool-files (&optional group)
@@ -1020,9 +1101,10 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
           (p procmails)
           (crash (when (and (file-exists-p nnmail-crash-box)
                             (> (nnheader-file-size
-                                (file-truename nnmail-crash-box)) 0))
+                                (file-truename nnmail-crash-box))
+                               0))
                    (list nnmail-crash-box))))
-      ;; Remove any directories that inadvertantly match the procmail
+      ;; Remove any directories that inadvertently match the procmail
       ;; suffix, which might happen if the suffix is "". 
       (while p
        (when (file-directory-p (car p))
@@ -1040,9 +1122,24 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
                   (eq nnmail-spool-file 'procmail))
              nil)
             ((listp nnmail-spool-file)
-             (append nnmail-spool-file procmails))
-            ((stringp nnmail-spool-file)
+             (nconc
+              (apply 
+               'nconc
+               (mapcar 
+                (lambda (file)
+                  (if (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))
             ((eq nnmail-spool-file 'pop)
              (cons (format "po:%s" (user-login-name)) procmails))
             (t
@@ -1055,10 +1152,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
@@ -1075,10 +1171,19 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
        (save-excursion
          (or (eq timestamp 'none)
              (set (intern (format "%s-active-timestamp" backend)) 
-                  (current-time)))
+;;; dmoore@ucsd.edu 25.10.96
+;;; it's not always the case that current-time
+;;; does correspond to changes in the file's time.  So just compare
+;;; the file's new time against its own previous time.
+;;;               (current-time)
+                  file-time
+                  ))
          (funcall (intern (format "%s-request-list" backend)))
-         (set (intern (format "%s-group-alist" backend)) 
-              (nnmail-get-active))))
+;;; dmoore@ucsd.edu 25.10.96
+;;; BACKEND-request-list already does this itself!
+;;;      (set (intern (format "%s-group-alist" backend)) 
+;;;           (nnmail-get-active))
+         ))
     t))
 
 (defun nnmail-message-id ()
@@ -1100,8 +1205,8 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
        (setq nnmail-cache-buffer 
             (get-buffer-create " *nnmail message-id cache*")))
       (buffer-disable-undo (current-buffer))
-      (and (file-exists-p nnmail-message-id-cache-file)
-          (insert-file-contents nnmail-message-id-cache-file))
+      (when (file-exists-p nnmail-message-id-cache-file)
+       (insert-file-contents nnmail-message-id-cache-file))
       (set-buffer-modified-p nil)
       (current-buffer))))
 
@@ -1114,10 +1219,10 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
       (set-buffer nnmail-cache-buffer)
       ;; Weed out the excess number of Message-IDs.
       (goto-char (point-max))
-      (and (search-backward "\n" nil t nnmail-message-id-cache-length)
-          (progn
-            (beginning-of-line)
-            (delete-region (point-min) (point))))
+      (when (search-backward "\n" nil t nnmail-message-id-cache-length)
+       (progn
+         (beginning-of-line)
+         (delete-region (point-min) (point))))
       ;; Save the buffer.
       (or (file-exists-p (file-name-directory nnmail-message-id-cache-file))
          (make-directory (file-name-directory nnmail-message-id-cache-file)
@@ -1147,6 +1252,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
   (run-hooks 'nnmail-prepare-incoming-message-hook)
   ;; If this is a duplicate message, then we do not save it.
   (let* ((duplication (nnmail-cache-id-exists-p message-id))
+        (case-fold-search t)
         (action (when duplication
                   (cond
                    ((memq nnmail-treat-duplicates '(warn delete))
@@ -1155,16 +1261,14 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
                     (funcall nnmail-treat-duplicates message-id))
                    (t
                     nnmail-treat-duplicates))))
-        (group-art (nreverse (nnmail-article-group artnum-func))))
+        group-art)
     ;; Let the backend save the article (or not).
     (cond
-     ((null group-art)
-      (delete-region (point-min) (point-max)))
      ((not duplication)
       (nnmail-cache-insert message-id)
-      (funcall func group-art))
+      (funcall func (setq group-art
+                         (nreverse (nnmail-article-group artnum-func)))))
      ((eq action 'delete)
-      (delete-region (point-min) (point-max))
       (setq group-art nil))
      ((eq action 'warn)
       ;; We insert a warning.
@@ -1179,11 +1283,15 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
         "Message-ID: " newid "\n"
         "Gnus-Warning: This is a duplicate of message " message-id "\n")
        (nnmail-cache-insert newid)
-       (funcall func group-art)))
+       (funcall func (setq group-art
+                           (nreverse (nnmail-article-group artnum-func))))))
      (t
-      (funcall func group-art)))
+      (funcall func (setq group-art
+                         (nreverse (nnmail-article-group artnum-func))))))
     ;; Add the group-art list to the history list.
-    (push group-art nnmail-split-history)))
+    (if group-art
+       (push group-art nnmail-split-history)
+      (delete-region (point-min) (point-max)))))
 
 ;;; Get new mail.
 
@@ -1196,7 +1304,8 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
                                   &optional group spool-func)
   "Read new incoming mail."
   ;; Nix out the previous split history.
-  (setq nnmail-split-history nil)
+  (unless group
+    (setq nnmail-split-history nil))
   (let* ((spools (nnmail-get-spool-files group))
         (group-in group)
         incoming incomings spool)
@@ -1214,7 +1323,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
        (setq spool (pop spools))
        ;; We read each spool file if either the spool is a POP-mail
        ;; spool, or the file exists.  We can't check for the
-       ;; existance of POPped mail.
+       ;; existence of POPped mail.
        (when (or (string-match "^po:" spool)
                  (and (file-exists-p spool)
                       (> (nnheader-file-size (file-truename spool)) 0)))
@@ -1226,7 +1335,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
            (setq group (nnmail-get-split-group spool group-in))
            ;; We split the mail
            (nnmail-split-incoming 
-            nnmail-crash-box (intern (format "%s-save-mail" method)) 
+            nnmail-crash-box (intern (format "%s-save-mail" method))
             spool-func group (intern (format "%s-active-number" method)))
            ;; Check whether the inbox is to be moved to the special tmp dir. 
            (setq incoming
@@ -1284,20 +1393,26 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
             (nnmail-time-less days (nnmail-time-since time)))))))
 
 (defvar nnmail-read-passwd nil)
-(defun nnmail-read-passwd (prompt)
-  (unless nnmail-read-passwd
-    (if (load "passwd" t)
-       (setq nnmail-read-passwd 'read-passwd)
-      (autoload 'ange-ftp-read-passwd "ange-ftp")
-      (setq nnmail-read-passwd 'ange-ftp-read-passwd)))
-  (funcall nnmail-read-passwd prompt))
+(defun nnmail-read-passwd (prompt &rest args)
+  "Read a password using PROMPT.
+If ARGS, PROMPT is used as an argument to `format'."
+  (let ((prompt
+        (if args
+            (apply 'format prompt args)
+          prompt)))
+    (unless nnmail-read-passwd
+      (if (load "passwd" t)
+         (setq nnmail-read-passwd 'read-passwd)
+       (autoload 'ange-ftp-read-passwd "ange-ftp")
+       (setq nnmail-read-passwd 'ange-ftp-read-passwd)))
+    (funcall nnmail-read-passwd prompt)))
 
 (defun nnmail-check-syntax ()
   "Check (and modify) the syntax of the message in the current buffer."
   (save-restriction
     (message-narrow-to-head)
     (let ((case-fold-search t))
-      (unless (re-search-forward "^Message-Id:" nil t)
+      (unless (re-search-forward "^Message-ID:" nil t)
        (insert "Message-ID: " (nnmail-message-id) "\n")))))
 
 (defun nnmail-write-region (start end filename &optional append visit lockname)
@@ -1373,14 +1488,24 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
   (buffer-disable-undo (current-buffer))
   (erase-buffer)
   (let ((history nnmail-split-history)
-       elem ga)
+       elem)
     (while (setq elem (pop history))
-      (insert (mapcar (lambda (ga)
-                       (concat (car ga) ":" (int-to-string (cdr ga))))
-                     elem
-                     ", ")
+      (insert (mapconcat (lambda (ga)
+                          (concat (car ga) ":" (int-to-string (cdr ga))))
+                        elem
+                        ", ")
              "\n"))
     (goto-char (point-min))))
+
+(defun nnmail-new-mail-p (group)
+  "Say whether GROUP has new mail."
+  (let ((his nnmail-split-history)
+       found)
+    (while his
+      (when (assoc group (pop his))
+       (setq found t
+             his nil)))
+    found))
        
 (run-hooks 'nnmail-load-hook)