X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=lisp%2Fnnmail.el;h=32d38af148c0353fe87a57584405c75e887c1d76;hb=12107b41ab4feeb1fe8eb26b222045b9ae36e087;hp=002fbf0e75cb859010847604772f862b24a16a26;hpb=a707b63af25b91cb730c12e65156ca364bf49a44;p=elisp%2Fgnus.git- diff --git a/lisp/nnmail.el b/lisp/nnmail.el index 002fbf0..32d38af 100644 --- a/lisp/nnmail.el +++ b/lisp/nnmail.el @@ -1,5 +1,5 @@ ;;; nnmail.el --- mail support functions for the Gnus mail backends -;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000 +;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 ;; Free Software Foundation, Inc. ;; Author: Lars Magne Ingebrigtsen @@ -33,11 +33,11 @@ (require 'custom) (require 'gnus-util) (require 'mail-source) -(require 'mm-util) (eval-and-compile (autoload 'gnus-error "gnus-util") - (autoload 'gnus-buffer-live-p "gnus-util")) + (autoload 'gnus-buffer-live-p "gnus-util") + (autoload 'gnus-add-buffer "gnus")) (defgroup nnmail nil "Reading mail with Gnus." @@ -56,7 +56,7 @@ :group 'nnmail) (defgroup nnmail-split nil - "Organizing the incomming mail in folders." + "Organizing the incoming mail in folders." :group 'nnmail) (defgroup nnmail-files nil @@ -76,8 +76,7 @@ "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 @@ -86,8 +85,8 @@ else, you could do something like this: (setq nnmail-split-methods '((\"mail.4ad\" \"From:.*4ad\") - (\"mail.junk\" \"From:.*Lars\\\\|Subject:.*buy\") - (\"mail.misc\" \"\"))) + (\"mail.junk\" \"From:.*Lars\\\\|Subject:.*buy\") + (\"mail.misc\" \"\"))) As you can see, this variable is a list of lists, where the first element in each \"rule\" is the name of the group (which, by the way, @@ -104,7 +103,8 @@ The last element should always have \"\" as the regexp. 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"))) @@ -115,6 +115,22 @@ If nil, the first match found will be used." :group 'nnmail-split :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." + :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 ".*") + (repeat :value (".*") regexp))) + ;; 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. @@ -145,7 +161,7 @@ number of days) -- this doesn't have to be an integer. This variable can also be `immediate' and `never'." :group 'nnmail-expire :type '(choice (const immediate) - (integer :tag "days") + (number :tag "days") (const never))) (defcustom nnmail-expiry-wait-function nil @@ -159,8 +175,8 @@ Eg.: \(setq nnmail-expiry-wait-function (lambda (newsgroup) - (cond ((string-match \"private\" newsgroup) 31) - ((string-match \"junk\" newsgroup) 1) + (cond ((string-match \"private\" newsgroup) 31) + ((string-match \"junk\" newsgroup) 1) ((string-match \"important\" newsgroup) 'never) (t 7))))" :group 'nnmail-expire @@ -175,10 +191,44 @@ messages end up in that group. If it is a function, the function is called in a buffer narrowed to the message in question. The function receives one argument, the name of the group the message comes from. The return value should be `delete' or a group name (a string)." - :group 'nnmail-expire - :type '(choice (const delete) - (function :format "%v" nnmail-) - string)) + :version "21.1" + :group 'nnmail-expire + :type '(choice (const delete) + (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. @@ -201,6 +251,7 @@ This variable is obsolete; `mail-sources' should be used instead." (defcustom nnmail-scan-directory-mail-source-once nil "*If non-nil, scan all incoming procmail sorted mails once. It scans low-level sorted spools even when not required." + :version "21.1" :group 'nnmail-procmail :type 'boolean) @@ -236,9 +287,9 @@ running (\"xwatch\", etc.) Eg. \(add-hook 'nnmail-read-incoming-hook - (lambda () - (call-process \"/local/bin/mailsend\" nil nil nil - \"read\" nnmail-spool-file))) + (lambda () + (call-process \"/local/bin/mailsend\" nil nil nil + \"read\" nnmail-spool-file))) If you have xwatch running, this will alert it that mail has been read. @@ -299,11 +350,12 @@ discarded after running the split process." :type 'hook) (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 - :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. @@ -315,9 +367,9 @@ the following: 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 + 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. @@ -335,6 +387,12 @@ GROUP: Mail will be stored in GROUP (a string). return value FUNCTION should be a split, which is then recursively processed. +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...)) + 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. @@ -362,13 +420,13 @@ Example: ;; Other mailing lists... (any \"procmail@informatik\\\\.rwth-aachen\\\\.de\" \"procmail.list\") (any \"SmartList@informatik\\\\.rwth-aachen\\\\.de\" \"SmartList.list\") - ;; Both lists below have the same suffix, so prevent - ;; cross-posting to mkpkg.list of messages posted only to - ;; the bugs- list, but allow cross-posting when the - ;; message was really cross-posted. - (any \"bugs-mypackage@somewhere\" \"mypkg.bugs\") - (any \"mypackage@somewhere\" - \"bugs-mypackage\" \"mypkg.list\") - ;; + ;; Both lists below have the same suffix, so prevent + ;; cross-posting to mkpkg.list of messages posted only to + ;; the bugs- list, but allow cross-posting when the + ;; message was really cross-posted. + (any \"bugs-mypackage@somewhere\" \"mypkg.bugs\") + (any \"mypackage@somewhere\" - \"bugs-mypackage\" \"mypkg.list\") + ;; ;; People... (any \"larsi@ifi\\\\.uio\\\\.no\" \"people.Lars Magne Ingebrigtsen\")) ;; Unmatched mail goes to the catch all group. @@ -417,18 +475,33 @@ parameter. It should return nil, `warn' or `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 :type '(repeat symbol)) (defcustom nnmail-split-header-length-limit 512 "Header lines longer than this limit are excluded from the split function." + :version "21.1" :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 t + "Whether the nnmail splitting functionality should MIME decode headers." + :group 'nnmail + :type 'boolean) + ;;; Internal variables. +(defvar nnmail-article-buffer " *nnmail incoming*" + "The buffer used for splitting incoming mails.") + (defvar nnmail-split-history nil "List of group/article elements that say where the previous split put messages.") @@ -460,28 +533,29 @@ parameter. It should return nil, `warn' or `delete'." "Coding system used in nnmail.") (defvar nnmail-incoming-coding-system - mm-text-coding-system + nnheader-text-coding-system "Coding system used in reading inbox") -(defvar nnmail-pathname-coding-system 'iso-8859-1 - "*Coding system for pathname.") +(defvar nnmail-pathname-coding-system nil + "*Coding system for file name.") (defun nnmail-find-file (file) "Insert FILE in server buffer safely." (set-buffer nntp-server-buffer) (delete-region (point-min) (point-max)) (let ((format-alist nil) - (after-insert-file-functions nil)) + (after-insert-file-functions nil)) (condition-case () - (let ((coding-system-for-read nnmail-file-coding-system) - (auto-mode-alist (mm-auto-mode-alist)) - (file-name-coding-system nnmail-pathname-coding-system)) - (insert-file-contents file) + (let ((auto-mode-alist (nnheader-auto-mode-alist)) + (file-name-coding-system nnmail-pathname-coding-system) + (pathname-coding-system nnmail-pathname-coding-system)) + (insert-file-contents-as-coding-system + nnmail-file-coding-system file) t) (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 @@ -495,7 +569,7 @@ parameter. It should return nil, `warn' or `delete'." (expand-file-name group dir) ;; If not, we translate dots into slashes. (expand-file-name - (mm-encode-coding-string + (encode-coding-string (nnheader-replace-chars-in-string group ?. ?/) nnmail-pathname-coding-system) dir)))) @@ -526,8 +600,8 @@ nn*-request-list should have been called before calling this function." (setq group (read buffer)) (unless (stringp group) (setq group (symbol-name group))) - (if (and (numberp (setq max (read nntp-server-buffer))) - (numberp (setq min (read nntp-server-buffer)))) + (if (and (numberp (setq max (read buffer))) + (numberp (setq min (read buffer)))) (push (list group (cons min max)) group-assoc))) (error nil)) @@ -540,7 +614,8 @@ 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." - (let ((coding-system-for-write nnmail-active-file-coding-system)) + (let ((coding-system-for-write nnmail-active-file-coding-system) + (output-coding-system nnmail-active-file-coding-system)) (when file-name (with-temp-file file-name (nnmail-generate-active group-assoc))))) @@ -648,7 +723,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." - ;; 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 ". @@ -677,7 +752,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." - ;; 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 ". @@ -757,7 +832,7 @@ If SOURCE is a directory spec, try to return the group name component." (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 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. @@ -892,10 +967,10 @@ FUNC will be called with the buffer narrowed to each mail." nnmail-split-methods))) (save-excursion ;; Insert the incoming file. - (set-buffer (get-buffer-create " *nnmail incoming*")) + (set-buffer (get-buffer-create nnmail-article-buffer)) (erase-buffer) - (let ((coding-system-for-read nnmail-incoming-coding-system)) - (mm-insert-file-contents incoming)) + (let ((nnheader-file-coding-system nnmail-incoming-coding-system)) + (nnheader-insert-file-contents incoming)) (prog1 (if (zerop (buffer-size)) 0 @@ -920,7 +995,7 @@ 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." - (let ((methods nnmail-split-methods) + (let ((methods (or nnmail-split-methods '(("bogus" "")))) (obuf (current-buffer)) (beg (point-min)) end group-art method grp) @@ -939,6 +1014,10 @@ FUNC will be called with the group name to determine the article number." (erase-buffer) ;; Copy the headers into the work buffer. (insert-buffer-substring obuf beg end) + ;; Decode MIME headers and charsets. + (when nnmail-mail-splitting-decodes + (mime-decode-header-in-region (point-min) (point-max) + nnmail-mail-splitting-charset)) ;; Fold continuation lines. (goto-char (point-min)) (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t) @@ -968,8 +1047,8 @@ FUNC will be called with the group name to determine the article number." (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)) @@ -1017,13 +1096,12 @@ 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) - (let ((trace (nreverse nnmail-split-trace)) - (restore (current-buffer))) + (let ((restore (current-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))) @@ -1044,7 +1122,7 @@ Return the number of characters in the body." (let (lines chars) (save-excursion (goto-char (point-min)) - (unless (search-forward "\n\n" nil t) + (unless (search-forward "\n\n" nil t) (goto-char (point-max)) (insert "\n")) (setq chars (- (point-max) (point))) @@ -1071,7 +1149,7 @@ Return the number of characters in the body." (insert (format "Xref: %s" (system-name))) (while group-alist (insert (format " %s:%d" - (mm-encode-coding-string + (encode-coding-string (caar group-alist) nnmail-pathname-coding-system) (cdar group-alist))) @@ -1088,14 +1166,20 @@ Return the number of characters in the body." (defun nnmail-remove-list-identifiers () "Remove list identifiers from Subject headers." - (let ((regexp (if (stringp nnmail-list-identifiers) nnmail-list-identifiers - (mapconcat 'identity nnmail-list-identifiers " *\\|")))) + (let ((regexp + (if (consp nnmail-list-identifiers) + (mapconcat 'identity nnmail-list-identifiers " *\\|") + nnmail-list-identifiers))) (when regexp (goto-char (point-min)) - (when (re-search-forward - (concat "^Subject: +\\(Re: +\\)?\\(" regexp " *\\)") - nil t) - (delete-region (match-beginning 2) (match-end 0)))))) + (while (re-search-forward + (concat "^Subject: +\\(R[Ee]: +\\)*\\(" regexp " *\\)") + nil t) + (delete-region (match-beginning 2) (match-end 0)) + (beginning-of-line)) + (when (re-search-forward "^Subject: +\\(\\(R[Ee]: +\\)+\\)R[Ee]: +" nil t) + (delete-region (match-beginning 1) (match-end 1)) + (beginning-of-line))))) (defun nnmail-remove-tabs () "Translate TAB characters into SPACE characters." @@ -1110,17 +1194,39 @@ Return the number of characters in the body." (beginning-of-line) (insert "X-Gnus-Broken-Eudora-")) (goto-char (point-min)) - (when (re-search-forward "^In-Reply-To:[^\n]+\\(\n[ \t]+\\)" nil t) - (replace-match "" t t nil 1)))) + (when (re-search-forward "^\\(In-Reply-To:[^\n]+\\)\n[ \t]+" nil t) + (replace-match "\\1" t)))) (custom-add-option 'nnmail-prepare-incoming-header-hook 'nnmail-fix-eudora-headers) ;;; Utility functions +(defun nnmail-do-request-post (accept-func &optional server) + "Utility function to directly post a message to an nnmail-derived group. +Calls ACCEPT-FUNC (which should be `nnchoke-request-accept-article') +to actually put the message in the right group." + (let ((success t)) + (dolist (mbx (message-unquote-tokens + (message-tokenize-header + (message-fetch-field "Newsgroups") ", ")) success) + (let ((to-newsgroup (gnus-group-prefixed-name mbx gnus-command-method))) + (or (gnus-active to-newsgroup) + (gnus-activate-group to-newsgroup) + (if (gnus-y-or-n-p (format "No such group: %s. Create it? " + to-newsgroup)) + (or (and (gnus-request-create-group + to-newsgroup gnus-command-method) + (gnus-activate-group to-newsgroup nil nil + gnus-command-method)) + (error "Couldn't create group %s" to-newsgroup))) + (error "No such group: %s" to-newsgroup)) + (unless (funcall accept-func mbx (nth 1 gnus-command-method)) + (setq success nil)))))) + (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 @@ -1142,7 +1248,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 - (push (format "\"%s\"" split) nnmail-split-trace)) + (push split nnmail-split-trace)) (list (nnmail-expand-newtext split))) ;; Junk the message. @@ -1165,6 +1271,8 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." ;; Builtin : operation. ((eq (car split) ':) + (when nnmail-split-tracing + (push split nnmail-split-trace)) (nnmail-split-it (save-excursion (eval (cdr split))))) ;; Builtin ! operation. @@ -1181,7 +1289,7 @@ 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 - (push (cdr cached-pair) nnmail-split-trace)) + (push split nnmail-split-trace)) (let ((split-rest (cddr split)) (end (match-end 0)) ;; The searched regexp is \(\(FIELD\).*\)\(VALUE\). So, @@ -1213,7 +1321,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (setq value (cdr (assq value nnmail-split-abbrev-alist)))) ;; Someone might want to do a \N sub on this match, so get the ;; correct match positions. - (re-search-backward value start-of-value)) + (re-search-backward (concat "\\<" value "\\>") start-of-value)) (dolist (sp (nnmail-split-it (car split-rest))) (unless (memq sp split-result) (push sp split-result)))))) @@ -1223,22 +1331,29 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (t (let* ((field (nth 0 split)) (value (nth 1 split)) - partial regexp) + partial-front regexp + partial-rear regexp) (if (symbolp value) (setq value (cdr (assq value nnmail-split-abbrev-alist)))) (if (and (>= (length value) 2) (string= ".*" (substring value 0 2))) (setq value (substring value 2) - partial "")) + partial-front "")) + ;; Same trick for the rear of the regexp + (if (and (>= (length value) 2) + (string= ".*" (substring value -2))) + (setq value (substring value 0 -2) + partial-rear "")) (setq regexp (concat "^\\(\\(" (if (symbolp field) (cdr (assq field nnmail-split-abbrev-alist)) field) "\\):.*\\)" - (or partial "\\<") + (or partial-front "\\<") "\\(" value - "\\)\\>")) + "\\)" + (or partial-rear "\\>"))) (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. @@ -1326,6 +1441,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (set-buffer (setq nnmail-cache-buffer (get-buffer-create " *nnmail message-id cache*"))) + (gnus-add-buffer) (when (file-exists-p nnmail-message-id-cache-file) (nnheader-insert-file-contents nnmail-message-id-cache-file)) (set-buffer-modified-p nil) @@ -1358,46 +1474,39 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (defvar group) (defvar group-art-list) (defvar group-art) -(defun nnmail-cache-insert (id) +(defun nnmail-cache-insert (id grp) (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) - (res nil)) + (be nil) + (res 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) - (eval (intern (format "%s-get-new-mail" (car be))))) - (setq res be))) + (eval (intern (format "%s-get-new-mail" (car be))))) + (setq res be))) res)) ;; Fetch the group name corresponding to the message id stored in the @@ -1408,29 +1517,45 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (set-buffer nnmail-cache-buffer) (goto-char (point-max)) (when (search-backward id nil t) - (beginning-of-line) - (skip-chars-forward "^\n\r\t") - (unless (eolp) - (forward-char 1) - (buffer-substring (point) - (progn (end-of-line) (point)))))))) + (beginning-of-line) + (skip-chars-forward "^\n\r\t") + (unless (looking-at "[\r\n]") + (forward-char 1) + (buffer-substring (point) + (progn (end-of-line) (point)))))))) ;; 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) +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 +see which group that message was put in. This group is returned. + +See the Info node `(gnus)Fancy Mail Splitting' for more details." (let* ((refstr (or (message-fetch-field "references") - (message-fetch-field "in-reply-to"))) - (references nil) - (res nil)) + (message-fetch-field "in-reply-to"))) + (references nil) + (res nil) + (regexp (if (consp nnmail-split-fancy-with-parent-ignore-groups) + (mapconcat + (lambda (x) (format "\\(%s\\)" x)) + nnmail-split-fancy-with-parent-ignore-groups + "\\|") + nnmail-split-fancy-with-parent-ignore-groups))) (when refstr (setq references (nreverse (gnus-split-references refstr))) (unless (gnus-buffer-live-p nnmail-cache-buffer) - (nnmail-cache-open)) + (nnmail-cache-open)) (mapcar (lambda (x) - (setq res (or (nnmail-cache-fetch-group x) res)) - (when (string= "drafts" res) - (setq res nil))) - references) + (setq res (or (nnmail-cache-fetch-group x) res)) + (when (or (member res '("delayed" "drafts" "queue")) + (and regexp res (string-match regexp res))) + (setq res nil))) + references) res))) (defun nnmail-cache-id-exists-p (id) @@ -1472,7 +1597,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." ((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) @@ -1539,12 +1664,11 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (setq source (append source (list :predicate - `(lambda (file) - (string-match - ,(concat - (regexp-quote (concat group suffix)) - "$") - file))))))) + (gnus-byte-compile + `(lambda (file) + (string-equal + ,(concat group suffix) + (file-name-nondirectory file))))))))) (when nnmail-fetched-sources (if (member source nnmail-fetched-sources) (setq source nil) @@ -1565,12 +1689,15 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (when (setq new (mail-source-fetch source - `(lambda (file orig-file) - (nnmail-split-incoming - file ',(intern (format "%s-save-mail" method)) - ',spool-func - (nnmail-get-split-group orig-file source) - ',(intern (format "%s-active-number" method)))))) + (gnus-byte-compile + `(lambda (file orig-file) + (nnmail-split-incoming + file ',(intern (format "%s-save-mail" method)) + ',spool-func + (if (equal file orig-file) + nil + (nnmail-get-split-group orig-file ',source)) + ',(intern (format "%s-active-number" method))))))) (incf total new) (incf i))) ;; If we did indeed read any incoming spools, we save all info. @@ -1614,10 +1741,46 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (ignore-errors (time-less-p days (time-since time)))))))) (defun nnmail-expiry-target-group (target group) - (when (nnheader-functionp target) - (setq target (funcall target group))) - (unless (eq target 'delete) - (gnus-request-accept-article target nil nil t))) + ;; Do not invoke this from nntp-server-buffer! At least nnfolder clears + ;; that buffer if the nnfolder group isn't selected. + (let (nnmail-cache-accepted-message-ids) + ;; Don't enter Message-IDs into cache. + ;; Let users hack it in TARGET function. + (when (nnheader-functionp target) + (setq target (funcall target group))) + (unless (eq target 'delete) + (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." @@ -1629,9 +1792,10 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (defun nnmail-write-region (start end filename &optional append visit lockname) "Do a `write-region', and then set the file modes." - (let ((coding-system-for-write nnmail-file-coding-system) - (file-name-coding-system nnmail-pathname-coding-system)) - (write-region start end filename append visit lockname) + (let ((file-name-coding-system nnmail-pathname-coding-system) + (pathname-coding-system nnmail-pathname-coding-system)) + (write-region-as-coding-system + nnmail-file-coding-system start end filename append visit lockname) (set-file-modes filename nnmail-default-file-modes))) ;;; @@ -1729,6 +1893,16 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." his nil))) found)) +(defun nnmail-new-mail-numbers (group) + "Say how many articles has been incorporated to GROUP." + (let ((his (apply 'append nnmail-split-history)) + numbers) + (while his + (when (string= group (caar his)) + (push (cdar his) numbers)) + (setq his (cdr his))) + numbers)) + (defun nnmail-within-headers-p () "Check to see if point is within the headers of a unix mail message. Doesn't change point."