*** empty log message ***
[gnus] / lisp / nnmail.el
index 7ddb1c8..fa93388 100644 (file)
@@ -1,5 +1,5 @@
 ;;; nnmail.el --- mail support functions for the Gnus mail backends
-;; Copyright (C) 1995 Free Software Foundation, Inc.
+;; Copyright (C) 1995,96 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
 ;; Keywords: news, mail
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
 
 ;;; Code:
 
 (require 'nnheader)
-(require 'rmail)
 (require 'timezone)
-(require 'sendmail)
+(require 'message)
 (eval-when-compile (require 'cl))
 
 (defvar nnmail-split-methods
@@ -65,19 +65,30 @@ If nil, the first match found will be used.")
 
 ;; Added by gord@enci.ucalgary.ca (Gordon Matzigkeit).
 (defvar nnmail-keep-last-article nil
-  "*If non-nil, nnmail will never delete the last expired article in a
-directory.  You may need to set this variable if other programs are putting
+  "*If non-nil, nnmail will never delete the last expired article in a directory.  
+You may need to set this variable if other programs are putting
 new mail into folder numbers that Gnus has marked as expired.")
 
+(defvar nnmail-use-long-file-names nil
+  "*If non-nil the mail backends will use long file and directory names.
+If nil, groups like \"mail.misc\" will end up in directories like
+\"mail/misc/\".")
+
+(defvar nnmail-default-file-modes ?\600
+  "Set the mode bits of all new mail files to this integer.")
+
 (defvar nnmail-expiry-wait 7
-  "*Articles that are older than `nnmail-expiry-wait' days will be expired.")
+  "*Expirable articles that are older than this will be expired.
+This variable can either be a number (which will be interpreted as a
+number of days) -- this doesn't have to be an integer.  This variable
+can also be `immediate' and `never'.")
 
 (defvar nnmail-expiry-wait-function nil
   "*Variable that holds function to specify how old articles should be before they are expired.
   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'. 
+\"old\".  It can also return the values `never' and `immediate'.
 
 Eg.:
 
@@ -85,6 +96,7 @@ Eg.:
       (lambda (newsgroup)
         (cond ((string-match \"private\" newsgroup) 31)
               ((string-match \"junk\" newsgroup) 1)
+             ((string-match \"important\" newsgroup) 'never)
              (t 7))))")
 
 (defvar nnmail-spool-file 
@@ -96,6 +108,9 @@ 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.")
 
+(defvar nnmail-crash-box "~/.gnus-crash-box"
+  "*File where Gnus will store mail while processing it.")
+
 (defvar nnmail-use-procmail nil
   "*If non-nil, the mail backends will look in `nnmail-procmail-directory' for spool files.
 The file(s) in `nnmail-spool-file' will also be read.")
@@ -104,7 +119,7 @@ The file(s) in `nnmail-spool-file' will also be read.")
   "*When using procmail (and the like), incoming mail is put in this directory.
 The Gnus mail backends will read the mail from this directory.")
 
-(defvar nnmail-procmail-suffix ".spool"
+(defvar nnmail-procmail-suffix "\\.spool"
   "*Suffix of files created by procmail (and the like).
 This variable might be a suffix-regexp to match the suffixes of
 several files - eg. \".spool[0-9]*\".")
@@ -115,15 +130,28 @@ several files - eg. \".spool[0-9]*\".")
 (defvar nnmail-delete-file-function 'delete-file
   "Function called to delete files in some mail backends.")
 
+(defvar nnmail-crosspost-link-function 'add-name-to-file
+  "Function called to create a copy of a file.
+This is `add-name-to-file' by default, which means that crossposts
+will use hard links.  If your file system doesn't allow hard
+links, you could set this variable to `copy-file' instead.")
+
 (defvar nnmail-movemail-program "movemail"
   "*A command to be executed to move mail from the inbox.
-The default is \"movemail\".")
+The default is \"movemail\".
+
+This can also be a function.  In that case, the function will be
+called with two parameters -- the name of the INBOX file, and the file
+to be moved to.")
+
+(defvar nnmail-pop-password-required nil
+  "*Non-nil if a password is required when reading mail using POP.")
 
 (defvar nnmail-read-incoming-hook nil
   "*Hook that will be run after the incoming mail has been transferred.
 The incoming mail is moved from `nnmail-spool-file' (which normally is
 something like \"/usr/spool/mail/$user\") to the user's home
-directory. This hook is called after the incoming mail box has been
+directory.  This hook is called after the incoming mail box has been
 emptied, and can be used to call any mail box programs you have
 running (\"xwatch\", etc.)
 
@@ -146,11 +174,38 @@ If you use `display-time', you could use something like this:
            (if (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))
+
 ;; Suggested by Erik Selberg <speed@cs.washington.edu>.
 (defvar nnmail-prepare-incoming-hook nil
   "*Hook called before treating incoming mail.
 The hook is run in a buffer with all the new, incoming mail.")
 
+(defvar nnmail-prepare-incoming-header-hook nil
+  "*Hook called narrowed to the headers of each message.
+This can be used to remove excessive spaces (and stuff like
+that) from the headers before splitting and saving the messages.")
+
+(defvar nnmail-prepare-incoming-message-hook nil
+  "*Hook called narrowed to each message.")
+
+(defvar nnmail-list-identifiers nil
+  "Regexp that match list identifiers to be removed.
+This can also be a list of regexps.")
+
+(defvar nnmail-pre-get-new-mail-hook nil
+  "Hook called just before starting to handle new incoming mail.")
+
+(defvar nnmail-post-get-new-mail-hook nil
+  "Hook called just after finishing handling new incoming mail.")
+
+(defvar nnmail-split-hook nil
+  "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.")
+
 ;; Suggested by Mejia Pablo J <pjm9806@usl.edu>.
 (defvar nnmail-tmp-directory nil
   "*If non-nil, use this directory for temporary storage when reading incoming mail.")
@@ -179,8 +234,8 @@ GROUP: Mail will be stored in GROUP (a string).
 \(& SPLIT...): Process each SPLIT expression.
 
 FIELD must match a complete field name.  VALUE must match a complete
-word according to the fundamental mode syntax table.  You can use .*
-in the regexps to match partial field names or words.
+word according to the `nnmail-split-fancy-syntax-table' syntax table.
+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'.
@@ -189,7 +244,7 @@ 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\")
@@ -208,7 +263,7 @@ Example:
          \"misc.misc\"))")
 
 (defvar nnmail-split-abbrev-alist
-  '((any . "from\\|to\\|cc\\|sender\\|apparently-to")
+  '((any . "from\\|to\\|cc\\|sender\\|apparently-to\\|resent-from\\|resent-to\\|resent-cc")
     (mail . "mailer-daemon\\|postmaster"))
   "*Alist of abbreviations allowed in `nnmail-split-fancy'.")
 
@@ -218,13 +273,45 @@ Example:
 (defvar nnmail-message-id-cache-length 1000
   "*The approximate number of Message-IDs nnmail will keep in its cache.
 If this variable is nil, no checking on duplicate messages will be
-perfomed.")
+performed.")
 
 (defvar nnmail-message-id-cache-file "~/.nnmail-cache"
   "*The file name of the nnmail Message-ID cache.")
 
-(defvar nnmail-delete-duplicates nil
-  "*If non-nil, nnmail will delete any duplicate mails it sees.")
+(defvar nnmail-treat-duplicates 'warn
+  "*If non-nil, nnmail keep a cache of Message-IDs to discover mail duplicates.
+Three values are legal: nil, which means that nnmail is not to keep a
+Message-ID cache; `warn', which means that nnmail should insert extra
+headers to warn the user about the duplication (this is the default);
+and `delete', which means that nnmail will delete duplicated mails.
+
+This variable can also be a function.  It will be called from a buffer
+narrowed to the article in question with the Message-ID as a
+parameter.  It should return nil, `warn' or `delete'.")
+
+;;; Internal variables.
+
+(defvar nnmail-split-history nil
+  "List of group/article elements that say where the previous split put messages.")
+
+(defvar nnmail-pop-password nil
+  "*Password to use when reading mail from a POP server, if required.")
+
+(defvar nnmail-split-fancy-syntax-table nil
+  "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-moved-inboxes nil
+  "List of inboxes that have been moved.")
+
+(defvar nnmail-internal-password nil)
 
 \f
 
@@ -240,120 +327,172 @@ perfomed.")
   "Insert FILE in server buffer safely."
   (set-buffer nntp-server-buffer)
   (erase-buffer)
-  (condition-case ()
-      (progn (insert-file-contents file) t)
-    (file-error nil)))
+  (let ((format-alist nil)
+        (after-insert-file-functions nil))
+    (condition-case ()
+       (progn (insert-file-contents file) t)
+      (file-error nil))))
 
-(defun nnmail-article-pathname (group mail-dir)
+(defun nnmail-group-pathname (group dir &optional file)
   "Make pathname for GROUP."
-  (concat (file-name-as-directory (expand-file-name mail-dir))
-         (nnmail-replace-chars-in-string group ?. ?/) "/"))
-
-(defun nnmail-replace-chars-in-string (string from to)
-  "Replace characters in STRING from FROM to TO."
-  (let ((string (substring string 0))  ;Copy string.
-       (len (length string))
-       (idx 0))
-    ;; Replace all occurrences of FROM with TO.
-    (while (< idx len)
-      (if (= (aref string idx) from)
-         (aset string idx to))
-      (setq idx (1+ idx)))
-    string))
-
-(defun nnmail-days-between (date1 date2)
-  ;; Return the number of days between date1 and date2.
-  (let ((d1 (mapcar (lambda (s) (and s (string-to-int s)) )
-                   (timezone-parse-date date1)))
-       (d2 (mapcar (lambda (s) (and s (string-to-int s)) )
-                   (timezone-parse-date date2))))
-    (- (timezone-absolute-from-gregorian 
-       (nth 1 d1) (nth 2 d1) (car d1))
-       (timezone-absolute-from-gregorian 
-       (nth 1 d2) (nth 2 d2) (car d2)))))
-
-;; Function taken from rmail.el.
-(defun nnmail-move-inbox (inbox tofile)
-  (let ((inbox (file-truename
-               (expand-file-name (substitute-in-file-name inbox))))
-       movemail popmail errors)
-    ;; Check whether the inbox is to be moved to the special tmp dir. 
-    (if nnmail-tmp-directory
-       (setq tofile (concat (file-name-as-directory nnmail-tmp-directory)
-                            (file-name-nondirectory tofile))))
-    ;; Make the filename unique.
-    (setq tofile (nnmail-make-complex-temp-name (expand-file-name tofile)))
-    ;; We create the directory the tofile is to reside in if it
-    ;; doesn't exist.
-    (or (file-exists-p (file-name-directory tofile))
-       (make-directory tofile 'parents))
-    ;; If getting from mail spool directory,
-    ;; use movemail to move rather than just renaming,
-    ;; so as to interlock with the mailer.
-    (or (setq popmail (string-match "^po:" (file-name-nondirectory inbox)))
+  (concat
+   (let ((dir (file-name-as-directory (expand-file-name dir))))
+     ;; If this directory exists, we use it directly.
+     (if (or nnmail-use-long-file-names 
+            (file-directory-p (concat dir group)))
+        (concat dir group "/")
+       ;; If not, we translate dots into slashes.
+       (concat dir (nnheader-replace-chars-in-string group ?. ?/) "/")))
+   (or file "")))
+  
+(defun nnmail-date-to-time (date)
+  "Convert DATE into time."
+  (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))))))
+
+(defun nnmail-time-less (t1 t2)
+  "Say whether time T1 is less than time T2."
+  (or (< (car t1) (car t2))
+      (and (= (car t1) (car t2))
+          (< (nth 1 t1) (nth 1 t2)))))
+
+(defun nnmail-days-to-time (days)
+  "Convert DAYS into time."
+  (let* ((seconds (* 1.0 days 60 60 24))
+        (rest (expt 2 16))
+        (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))))))
+
+(defun nnmail-time-since (time)
+  "Return the time since TIME, which is either an internal time or a date."
+  (when (stringp time)
+    ;; 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))))
+    (list (- (+ (car current) (if rest -1 0)) (car time))
+         (- (+ (or rest 0) (nth 1 current)) (nth 1 time)))))
+
+;; Function rewritten from rmail.el.
+(defun nnmail-move-inbox (inbox)
+  "Move INBOX to `nnmail-crash-box'."
+  (if (not (file-writable-p nnmail-crash-box))
+      (gnus-error 1 "Can't write to crash box %s.  Not moving mail."
+                 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))
-    (if popmail (setq inbox (file-name-nondirectory inbox)))
-    (if movemail
-       ;; On some systems, /usr/spool/mail/foo is a directory
-       ;; and the actual inbox is /usr/spool/mail/foo/foo.
-       (if (file-directory-p inbox)
-           (setq inbox (expand-file-name (user-login-name) inbox))))
-    (if popmail
-       (message "Getting mail from post office ...")
-      (if (or (and (file-exists-p tofile)
-                  (/= 0 (nth 7 (file-attributes tofile))))
-             (and (file-exists-p inbox)
-                  (/= 0 (nth 7 (file-attributes inbox)))))
-         (message "Getting mail from %s..." inbox)))
- &nbs