Update.
[elisp/gnus.git-] / lisp / nnmail.el
index e326b09..e761868 100644 (file)
@@ -1,7 +1,7 @@
 ;;; nnmail.el --- mail support functions for the Gnus mail backends
 ;; Copyright (C) 1995,96,97,98 Free Software Foundation, Inc.
 
-;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news, mail
 
 ;; This file is part of GNU Emacs.
@@ -33,7 +33,9 @@
 (require 'custom)
 
 (eval-and-compile
-  (autoload 'gnus-error "gnus-util"))
+  (autoload 'gnus-error "gnus-util")
+  (autoload 'gnus-buffer-live-p "gnus-util")
+  (autoload 'gnus-encode-coding-string "gnus-ems"))
 
 (defgroup nnmail nil
   "Reading mail with Gnus."
@@ -106,14 +108,14 @@ This variable can also have a function as its value."
 
 ;; Suggested by Erik Selberg <speed@cs.washington.edu>.
 (defcustom nnmail-crosspost t
-  "*If non-nil, do crossposting if several split methods match the mail.
+  "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).
 (defcustom nnmail-keep-last-article nil
-  "*If non-nil, nnmail will never delete/move a group's last article.
+  "If non-nil, nnmail will never delete/move a group's last article.
 It can be marked expirable, so it will be deleted when it is no longer last.
 
 You may need to set this variable if other programs are putting
@@ -123,14 +125,14 @@ new mail into folder numbers that Gnus has marked as expired."
   :type 'boolean)
 
 (defcustom nnmail-use-long-file-names nil
-  "*If non-nil the mail backends will use long file and directory names.
+  "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/\"."
   :group 'nnmail-files
   :type 'boolean)
 
 (defcustom nnmail-default-file-modes 384
-  "*Set the mode bits of all new mail files to this integer."
+  "Set the mode bits of all new mail files to this integer."
   :group 'nnmail-files
   :type 'integer)
 
@@ -145,7 +147,7 @@ can also be `immediate' and `never'."
                 (const never)))
 
 (defcustom nnmail-expiry-wait-function nil
-  "*Variable that holds function to specify how old articles should be before they are expired.
+  "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
@@ -164,7 +166,7 @@ Eg.:
                 (function :format "%v" nnmail-)))
 
 (defcustom nnmail-cache-accepted-message-ids nil
-  "*If non-nil, put Message-IDs of Gcc'd articles into the duplicate cache."
+  "If non-nil, put Message-IDs of Gcc'd articles into the duplicate cache."
   :group 'nnmail
   :type 'boolean)
 
@@ -182,7 +184,7 @@ treat all files in that directory as incoming spool files."
   :type 'file)
 
 (defcustom nnmail-crash-box "~/.gnus-crash-box"
-  "*File where Gnus will store mail while processing it."
+  "File where Gnus will store mail while processing it."
   :group 'nnmail-files
   :type 'file)
 
@@ -211,12 +213,12 @@ several files - eg. \".spool[0-9]*\"."
   :type 'boolean)
 
 (defcustom nnmail-delete-file-function 'delete-file
-  "*Function called to delete files in some mail backends."
+  "Function called to delete files in some mail backends."
   :group 'nnmail-files
   :type 'function)
 
 (defcustom nnmail-crosspost-link-function
-  (if (string-match "windows-nt\\|emx" (format "%s" system-type))
+  (if (string-match "windows-nt\\|emx" (symbol-name system-type))
       'copy-file
     'add-name-to-file)
   "*Function called to create a copy of a file.
@@ -278,25 +280,25 @@ If you use `display-time', you could use something like this:
 
 ;; Suggested by Erik Selberg <speed@cs.washington.edu>.
 (defcustom nnmail-prepare-incoming-hook nil
-  "*Hook called before treating incoming mail.
+  "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.
+  "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."
   :group 'nnmail-prepare
   :type 'hook)
 
 (defcustom nnmail-prepare-incoming-message-hook nil
-  "*Hook called narrowed to each message."
+  "Hook called narrowed to each message."
   :group 'nnmail-prepare
   :type 'hook)
 
 (defcustom nnmail-list-identifiers nil
-  "*Regexp that matches list identifiers to be removed.
+  "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)
@@ -304,17 +306,17 @@ This can also be a list of regexps."
                 (repeat :value (".*") regexp)))
 
 (defcustom nnmail-pre-get-new-mail-hook nil
-  "*Hook called just before starting to handle new incoming mail."
+  "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."
+  "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.
+  "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."
@@ -338,10 +340,10 @@ messages will be shown to indicate the current status."
   :type 'integer)
 
 (defcustom nnmail-split-fancy "mail.misc"
-  "*Incoming mail can be split according to this fancy variable.
+  "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 format of this variable is SPLIT, where SPLIT can be one of
 the following:
 
 GROUP: Mail will be stored in GROUP (a string).
@@ -405,7 +407,7 @@ Example:
   :group 'nnmail-split
   :type '(repeat (cons :format "%v" symbol regexp)))
 
-(defcustom nnmail-delete-incoming nil
+(defcustom nnmail-delete-incoming t
   "*If non-nil, the mail backends will delete incoming files after
 splitting."
   :group 'nnmail-retrieve
@@ -539,7 +541,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 (floor (/ seconds rest))
               (range-error (expt 2 16)))))
     (list ms (condition-case nil (round (- seconds (* ms rest)))
               (range-error (expt 2 16))))))
@@ -589,12 +591,12 @@ parameter.  It should return nil, `warn' or `delete'."
                      (nnmail-read-passwd
                       (format "Password for %s: "
                               (substring inbox (+ popmail 3))))))
-             (message "Getting mail from the post office..."))
+             (nnheader-message 5 "Getting mail from the post office..."))
          (when (or (and (file-exists-p tofile)
                         (/= 0 (nnheader-file-size tofile)))
                    (and (file-exists-p inbox)
                         (/= 0 (nnheader-file-size inbox))))
-           (message "Getting mail from %s..." inbox)))
+           (nnheader-message 5 "Getting mail from %s..." inbox)))
        ;; Set TOFILE if have not already done so, and
        ;; rename or copy the file INBOX to TOFILE if and as appropriate.
        (cond
@@ -634,14 +636,14 @@ parameter.  It should return nil, `warn' or `delete'."
                             nil errors nil inbox tofile)
                            (when nnmail-internal-password
                              (list nnmail-internal-password)))))))
+               (push inbox nnmail-moved-inboxes)
                (if (and (not (buffer-modified-p errors))
                         (zerop result))
                    ;; No output => movemail won
                    (progn
                      (unless popmail
                        (when (file-exists-p tofile)
-                         (set-file-modes tofile nnmail-default-file-modes)))
-                     (push inbox nnmail-moved-inboxes))
+                         (set-file-modes tofile nnmail-default-file-modes))))
                  (set-buffer errors)
                  ;; There may be a warning about older revisions.  We
                  ;; ignore those.
@@ -650,8 +652,8 @@ parameter.  It should return nil, `warn' or `delete'."
                      (progn
                        (unless popmail
                          (when (file-exists-p tofile)
-                           (set-file-modes tofile nnmail-default-file-modes)))
-                       (push inbox nnmail-moved-inboxes))
+                           (set-file-modes
+                            tofile nnmail-default-file-modes))))
                    ;; Probably a real error.
                    (subst-char-in-region (point-min) (point-max) ?\n ?\  )
                    (goto-char (point-max))
@@ -665,7 +667,7 @@ parameter.  It should return nil, `warn' or `delete'."
                                     (buffer-string) result))
                      (error "%s" (buffer-string)))
                    (setq tofile nil)))))))
-       (message "Getting mail from %s...done" inbox)
+       (nnheader-message 5 "Getting mail from %s...done" inbox)
        (and errors
             (buffer-name errors)
             (kill-buffer errors))
@@ -715,10 +717,12 @@ return nil if FILE is a spool file or the procmail group for which it
 is a spool.  If not using procmail, return GROUP."
   (if (or (eq nnmail-spool-file 'procmail)
          nnmail-use-procmail)
-      (if (string-match (concat "^" (expand-file-name
-                                    (file-name-as-directory
-                                     nnmail-procmail-directory))
-                               "\\([^/]*\\)" nnmail-procmail-suffix "$")
+      (if (string-match (concat "^" (regexp-quote
+                                    (expand-file-name
+                                     (file-name-as-directory
+                                      nnmail-procmail-directory)))
+                               "\\([^/]*\\)"
+                               nnmail-procmail-suffix "$")
                        (expand-file-name file))
          (let ((procmail-group (substring (expand-file-name file)
                                           (match-beginning 1)
@@ -752,7 +756,7 @@ is a spool.  If not using procmail, return GROUP."
       (goto-char (point-min))
       (when (looking-at ">From ")
        (replace-match "X-From-Line: ") )
-      (gnus-run-hooks 'nnmail-prepare-incoming-header-hook)
+      (run-hooks 'nnmail-prepare-incoming-header-hook)
       (goto-char (point-max))
       ;; Find the Message-ID header.
       (save-excursion
@@ -873,7 +877,7 @@ is a spool.  If not using procmail, return GROUP."
                  (goto-char (match-beginning 0))))
        ;; Possibly wrong format?
        (progn
-         (pop-to-buffer (find-file-noselect nnmail-current-spool))
+         (pop-to-buffer (nnheader-find-file-noselect nnmail-current-spool))
          (error "Error, unknown mail format! (Possibly corrupted.)"))
       ;; Carry on until the bitter end.
       (while (not (eobp))
@@ -912,7 +916,7 @@ is a spool.  If not using procmail, return GROUP."
          ;; having a (possibly) faulty header.
          (beginning-of-line)
          (insert "X-"))
-       (gnus-run-hooks 'nnmail-prepare-incoming-header-hook)
+       (run-hooks 'nnmail-prepare-incoming-header-hook)
        ;; Find the end of this article.
        (goto-char (point-max))
        (widen)
@@ -960,7 +964,7 @@ is a spool.  If not using procmail, return GROUP."
                  (forward-line 1)))
        ;; Possibly wrong format?
        (progn
-         (pop-to-buffer (find-file-noselect nnmail-current-spool))
+         (pop-to-buffer (nnheader-find-file-noselect nnmail-current-spool))
          (error "Error, unknown mail format! (Possibly corrupted.)"))
       ;; Carry on until the bitter end.
       (while (not (eobp))
@@ -987,7 +991,7 @@ is a spool.  If not using procmail, return GROUP."
              (insert "Original-")))
          (forward-line 1)
          (insert "Message-ID: " (setq message-id (nnmail-message-id)) "\n"))
-       (gnus-run-hooks 'nnmail-prepare-incoming-header-hook)
+       (run-hooks 'nnmail-prepare-incoming-header-hook)
        ;; Find the end of this article.
        (goto-char (point-max))
        (widen)
@@ -1024,7 +1028,7 @@ FUNC will be called with the buffer narrowed to each mail."
       (nnheader-insert-file-contents incoming)
       (unless (zerop (buffer-size))
        (goto-char (point-min))
-       (save-excursion (gnus-run-hooks 'nnmail-prepare-incoming-hook))
+       (save-excursion (run-hooks 'nnmail-prepare-incoming-hook))
        ;; Handle both babyl, MMDF and unix mail formats, since movemail will
        ;; use the former when fetching from a mailbox, the latter when
        ;; fetching from a file.
@@ -1065,8 +1069,19 @@ FUNC will be called with the group name to determine the article number."
        (goto-char (point-min))
        (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
          (replace-match " " t t))
+       ;; Nuke pathologically long headers.  Since Gnus applies
+       ;; pathologically complex regexps to the buffer, lines
+       ;; that are looong will take longer than the Universe's
+       ;; existence to process.
+       (goto-char (point-min))
+       (while (not (eobp))
+         (end-of-line)
+         (if (> (current-column) 1024)
+             (gnus-delete-line)
+           (forward-line 1)))
        ;; Allow washing.
-       (gnus-run-hooks 'nnmail-split-hook)
+       (goto-char (point-min))
+       (run-hooks 'nnmail-split-hook)
        (if (and (symbolp nnmail-split-methods)
                 (fboundp nnmail-split-methods))
            (let ((split
@@ -1077,11 +1092,11 @@ FUNC will be called with the group name to determine the article number."
                       (or (funcall nnmail-split-methods)
                           '("bogus"))
                     (error
-                     (message
+                     (nnheader-message 5
                       "Error in `nnmail-split-methods'; using `bogus' mail group")
                      (sit-for 1)
                      '("bogus")))))
-             (setq split (remove-duplicates split :test 'equal))
+             (setq split (gnus-remove-duplicates split))
              ;; The article may be "cross-posted" to `junk'.  What
              ;; to do?  Just remove the `junk' spec.  Don't really
              ;; see anything else to do...
@@ -1494,7 +1509,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
       (message-fetch-field header))))
 
 (defun nnmail-check-duplication (message-id func artnum-func)
-  (gnus-run-hooks 'nnmail-prepare-incoming-message-hook)
+  (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)
@@ -1551,7 +1566,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
       ;; We first activate all the groups.
       (nnmail-activate method)
       ;; Allow the user to hook.
-      (gnus-run-hooks 'nnmail-pre-get-new-mail-hook)
+      (run-hooks 'nnmail-pre-get-new-mail-hook)
       ;; Open the message-id cache.
       (nnmail-cache-open)
       ;; The we go through all the existing spool files and split the
@@ -1596,12 +1611,12 @@ See the documentation for the variable `nnmail-split-fancy' for documentation."
         (nnmail-get-value "%s-active-file" method))
        (when exit-func
          (funcall exit-func))
-       (gnus-run-hooks 'nnmail-read-incoming-hook)
+       (run-hooks 'nnmail-read-incoming-hook)
        (nnheader-message 3 "%s: Reading incoming mail...done" method))
       ;; Close the message-id cache.
       (nnmail-cache-close)
       ;; Allow the user to hook.
-      (gnus-run-hooks 'nnmail-post-get-new-mail-hook)
+      (run-hooks 'nnmail-post-get-new-mail-hook)
       ;; Delete all the temporary files.
       (while incomings
        (setq incoming (pop incomings))
@@ -1739,11 +1754,12 @@ If ARGS, PROMPT is used as an argument to `format'."
        (princ "\n")))))
 
 (defun nnmail-purge-split-history (group)
+  "Remove all instances of GROUP from `nnmail-split-history'."
   (let ((history nnmail-split-history)
        prev)
     (while history
-      (setcar history (delete-if (lambda (e) (string= (car e) group))
-                                (car history)))
+      (setcar history (gnus-delete-if (lambda (e) (string= (car e) group))
+                                     (car history)))
       (pop history))
     (setq nnmail-split-history (delq nil nnmail-split-history))))
 
@@ -1766,6 +1782,14 @@ If ARGS, PROMPT is used as an argument to `format'."
          (substring inbox (match-end (string-match "^po:" inbox)))))
     (pop3-movemail crashbox)))
 
+(defun nnmail-within-headers-p ()
+  "Check to see if point is within the headers of a unix mail message.
+Doesn't change point."
+  (let ((pos (point)))
+    (save-excursion
+      (and (nnmail-search-unix-mail-delim-backward)
+          (not (search-forward "\n\n" pos t))))))
+
 (run-hooks 'nnmail-load-hook)
 
 (provide 'nnmail)