;;; nnmail.el --- mail support functions for the Gnus mail backends
-;; Copyright (C) 1995,96,97 Free Software Foundation, Inc.
+;; Copyright (C) 1995,96,97,98 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
;; Keywords: news, mail
;;; Code:
+(eval-when-compile (require 'cl))
+
(require 'nnheader)
(require 'timezone)
(require 'message)
-(eval-when-compile (require 'cl))
(require 'custom)
+(eval-and-compile
+ (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."
:group 'gnus)
(defcustom nnmail-split-methods
'(("mail.misc" ""))
- "Incoming mail will be split according to this variable.
+ "*Incoming mail will be split according to this variable.
If you'd like, for instance, one mail group for mail from the
\"4ad-l\" mailing list, one group for junk mail and one for everything
;; Added by gord@enci.ucalgary.ca (Gordon Matzigkeit).
(defcustom nnmail-keep-last-article nil
- "If non-nil, nnmail will never delete the last expired article in a directory.
+ "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
new mail into folder numbers that Gnus has marked as expired."
:group 'nnmail-procmail
:type '(choice (const :tag "nnmail-expiry-wait" nil)
(function :format "%v" nnmail-)))
-(defcustom nnmail-spool-file
+(defcustom nnmail-cache-accepted-message-ids nil
+ "If non-nil, put Message-IDs of Gcc'd articles into the duplicate cache."
+ :group 'nnmail
+ :type 'boolean)
+
+(defcustom nnmail-spool-file
(or (getenv "MAIL")
(concat "/usr/spool/mail/" (user-login-name)))
- "Where the mail backends will look for incoming mail.
+ "*Where the mail backends will look for incoming mail.
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
:group 'nnmail-files
:type 'function)
-(defcustom nnmail-crosspost-link-function 'add-name-to-file
- "Function called to create a copy of a file.
+(defcustom nnmail-crosspost-link-function
+ (if (string-match "windows-nt\\|emx" (format "%s" system-type))
+ 'copy-file
+ '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."
:group 'nnmail-retrieve
:type 'boolean)
-(defcustom nnmail-read-incoming-hook
+(defcustom nnmail-read-incoming-hook
(if (eq system-type 'windows-nt)
'(nnheader-ms-strip-cr)
nil)
- "Hook that will be run after the incoming mail has been transferred.
+ "*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
Eg.
-\(add-hook 'nnmail-read-incoming-hook
+\(add-hook 'nnmail-read-incoming-hook
(lambda ()
- (start-process \"mailsend\" nil
+ (start-process \"mailsend\" nil
\"/local/bin/mailsend\" \"read\" \"mbox\")))
If you have xwatch running, this will alert it that mail has been
-read.
+read.
If you use `display-time', you could use something like this:
This can also be a list of regexps."
:group 'nnmail-prepare
:type '(choice (const :tag "none" nil)
- regexp
- (repeat regexp)))
+ (regexp :value ".*")
+ (repeat :value (".*") regexp)))
(defcustom nnmail-pre-get-new-mail-hook nil
"Hook called just before starting to handle new incoming mail."
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.
\(| SPLIT...): Process each SPLIT expression until one of them matches.
A SPLIT expression is said to match if it will cause the mail
- message to be stored in one or more groups.
+ message to be stored in one or more groups.
\(& SPLIT...): Process each SPLIT expression.
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.
+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'.
(defcustom nnmail-split-abbrev-alist
'((any . "from\\|to\\|cc\\|sender\\|apparently-to\\|resent-from\\|resent-to\\|resent-cc")
- (mail . "mailer-daemon\\|postmaster\\|uucp"))
- "Alist of abbreviations allowed in `nnmail-split-fancy'."
+ (mail . "mailer-daemon\\|postmaster\\|uucp")
+ (to . "to\\|cc\\|apparently-to\\|resent-to\\|resent-cc")
+ (from . "from\\|sender\\|resent-from")
+ (nato . "to\\|cc\\|resent-to\\|resent-cc")
+ (naany . "from\\|to\\|cc\\|sender\\|resent-from\\|resent-to\\|resent-cc"))
+ "*Alist of abbreviations allowed in `nnmail-split-fancy'."
:group 'nnmail-split
:type '(repeat (cons :format "%v" symbol regexp)))
-(defcustom nnmail-delete-incoming t
+(defcustom nnmail-delete-incoming nil
"*If non-nil, the mail backends will delete incoming files after
splitting."
:group 'nnmail-retrieve
(defvar nnmail-split-history nil
"List of group/article elements that say where the previous split put messages.")
+(defvar nnmail-current-spool nil)
+
(defvar nnmail-pop-password nil
"*Password to use when reading mail from a POP server, if required.")
(defun nnmail-request-post (&optional server)
(mail-send-and-exit nil))
+(defvar nnmail-file-coding-system 'raw-text
+ "Coding system used in nnmail.")
+
(defun nnmail-find-file (file)
"Insert FILE in server buffer safely."
(set-buffer nntp-server-buffer)
(let ((format-alist nil)
(after-insert-file-functions nil))
(condition-case ()
- (progn (insert-file-contents file) t)
+ (let ((coding-system-for-read nnmail-file-coding-system)
+ (pathname-coding-system 'binary))
+ (insert-file-contents file)
+ t)
(file-error nil))))
+(defvar nnmail-pathname-coding-system
+ 'iso-8859-1
+ "*Coding system for pathname.")
+
(defun nnmail-group-pathname (group dir &optional file)
"Make pathname for GROUP."
(concat
(let ((dir (file-name-as-directory (expand-file-name dir))))
+ (setq group (nnheader-translate-file-chars group))
;; If this directory exists, we use it directly.
- (if (or nnmail-use-long-file-names
+ (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 ?. ?/) "/")))
+ (concat dir
+ (gnus-encode-coding-string
+ (nnheader-replace-chars-in-string group ?. ?/)
+ nnmail-pathname-coding-system)
+ "/")))
(or file "")))
(defun nnmail-date-to-time (date)
(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."
+ (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 (setq popmail (string-match
- "^po:" (file-name-nondirectory inbox)))
- (setq inbox (file-name-nondirectory inbox))
+ (let ((tofile (file-truename (expand-file-name nnmail-crash-box)))
+ (popmail (string-match "^po:" inbox))
+ movemail errors result)
+ (unless popmail
+ (setq inbox (file-truename (expand-file-name inbox)))
(setq movemail t)
;; On some systems, /usr/spool/mail/foo is a directory
;; and the actual inbox is /usr/spool/mail/foo/foo.
(nnmail-read-passwd
(format "Password for %s: "
(substring inbox (+ popmail 3))))))
- (message "Getting mail from post office ..."))
+ (message "Getting mail from the post office..."))
(when (or (and (file-exists-p tofile)
(/= 0 (nnheader-file-size tofile)))
(and (file-exists-p inbox)
(message "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
+ (cond
((file-exists-p tofile)
;; The crash box exists already.
t)
(save-excursion
(setq errors (generate-new-buffer " *nnmail loss*"))
(buffer-disable-undo errors)
- (let ((default-directory "/"))
- (if (nnheader-functionp nnmail-movemail-program)
- (funcall nnmail-movemail-program inbox tofile)
- (apply
- 'call-process
- (append
- (list
- (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))
+ (if (nnheader-functionp nnmail-movemail-program)
+ (condition-case err
+ (progn
+ (funcall nnmail-movemail-program inbox tofile)
+ (setq result 0))
+ (error
+ (save-excursion
+ (set-buffer errors)
+ (insert (prin1-to-string err))
+ (setq result 255))))
+ (let ((default-directory "/"))
+ (setq result
+ (apply
+ 'call-process
+ (append
+ (list
+ (expand-file-name
+ nnmail-movemail-program exec-directory)
+ nil errors nil inbox tofile)
+ (when nnmail-internal-password
+ (list nnmail-internal-password)))))))
+ (if (and (not (buffer-modified-p errors))
+ (zerop result))
;; No output => movemail won
(progn
(unless popmail
(when (looking-at "movemail: ")
(delete-region (point-min) (match-end 0)))
(unless (yes-or-no-p
- (format "movemail: %s. Continue? "
- (buffer-string)))
+ (format "movemail: %s (%d return). Continue? "
+ (buffer-string) result))
(error "%s" (buffer-string)))
(setq tofile nil)))))))
(message "Getting mail from %s...done" inbox)
(save-excursion
(set-buffer nntp-server-buffer)
(goto-char (point-min))
- (while (re-search-forward
+ (while (re-search-forward
"^\\([^ \t]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+\\)" nil t)
;; We create an alist with `(GROUP (LOW . HIGH))' elements.
(push (list (match-string 1)
group-assoc)))
group-assoc))
+(defvar nnmail-active-file-coding-system
+ 'iso-8859-1
+ "*Coding system for active file.")
+
(defun nnmail-save-active (group-assoc file-name)
"Save GROUP-ASSOC in ACTIVE-FILE."
- (when file-name
- (nnheader-temp-write file-name
- (nnmail-generate-active group-assoc))))
+ (let ((coding-system-for-write nnmail-active-file-coding-system))
+ (when file-name
+ (nnheader-temp-write file-name
+ (nnmail-generate-active group-assoc)))))
(defun nnmail-generate-active (alist)
"Generate an active file from group-alist ALIST."
(let ((procmail-group (substring (expand-file-name file)
(match-beginning 1)
(match-end 1))))
- (if group
+ (if group
(if (string-equal group procmail-group)
group
nil)
(defun nnmail-process-babyl-mail-format (func artnum-func)
(let ((case-fold-search t)
start message-id content-length do-search end)
- (goto-char (point-min))
(while (not (eobp))
+ (goto-char (point-min))
(re-search-forward
"\f\n0, *unseen,+\n\\(\\*\\*\\* EOOH \\*\\*\\*\n\\)?" nil t)
(goto-char (match-end 0))
"\n")))
;; Look for a Content-Length header.
(if (not (save-excursion
- (and (re-search-backward
+ (and (re-search-backward
"^Content-Length:[ \t]*\\([0-9]+\\)" start t)
(setq content-length (string-to-int
- (buffer-substring
+ (buffer-substring
(match-beginning 1)
(match-end 1))))
;; We destroy the header, since none of
(setq do-search t)))
(widen)
;; Go to the beginning of the next article - or to the end
- ;; of the buffer.
+ ;; of the buffer.
(when do-search
(if (re-search-forward "^\1f" nil t)
(goto-char (match-beginning 0))
(setq end (point-max))))
(goto-char end))))
-(defun nnmail-search-unix-mail-delim ()
+(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
;; brain-dead Unix mbox format:
(= (following-char) ?\n)))
(save-excursion
(forward-line 1)
- (while (looking-at ">From ")
+ (while (looking-at ">From \\|From ")
(forward-line 1))
(looking-at "[^ \n\t:]+[ \n\t]*:")))
(setq found 'yes)))))
(= (following-char) ?\n)))
(save-excursion
(forward-line 1)
- (while (looking-at ">From ")
+ (while (looking-at ">From \\|From ")
(forward-line 1))
(looking-at "[^ \n\t:]+[ \n\t]*:")))
(setq found 'yes)))))
(if (not (and (re-search-forward "^From " nil t)
(goto-char (match-beginning 0))))
;; Possibly wrong format?
- (error "Error, unknown mail format! (Possibly corrupted.)")
+ (progn
+ (pop-to-buffer (find-file-noselect nnmail-current-spool))
+ (error "Error, unknown mail format! (Possibly corrupted.)"))
;; Carry on until the bitter end.
(while (not (eobp))
(setq start (point)
end nil)
;; Find the end of the head.
(narrow-to-region
- start
+ start
(if (search-forward "\n\n" nil t)
(1- (point))
;; This will never happen, but just to be on the safe side --
;; if there is no head-body delimiter, we search a bit manually.
(while (and (looking-at "From \\|[^ \t]+:")
(not (eobp)))
- (forward-line 1)
- (point))))
+ (forward-line 1))
+ (point)))
;; Find the Message-ID header.
(goto-char (point-min))
(if (re-search-forward "^Message-ID[ \t]*:[ \n\t]*\\(<[^>]+>\\)" nil t)
"^Content-Length:[ \t]*\\([0-9]+\\)" nil t))
(setq content-length nil)
(setq content-length (string-to-int (match-string 1)))
- ;; We destroy the header, since none of the backends ever
+ ;; We destroy the header, since none of the backends ever
;; use it, and we do not want to confuse other mailers by
;; having a (possibly) faulty header.
(beginning-of-line)
(t (setq end nil))))
(if end
(goto-char end)
- ;; No Content-Length, so we find the beginning of the next
+ ;; No Content-Length, so we find the beginning of the next
;; article or the end of the buffer.
(goto-char head-end)
(or (nnmail-search-unix-mail-delim)
(if (not (and (re-search-forward delim nil t)
(forward-line 1)))
;; Possibly wrong format?
- (error "Error, unknown mail format! (Possibly corrupted.)")
+ (progn
+ (pop-to-buffer (find-file-noselect nnmail-current-spool))
+ (error "Error, unknown mail format! (Possibly corrupted.)"))
;; Carry on until the bitter end.
(while (not (eobp))
(setq start (point))
;; Find the end of the head.
(narrow-to-region
- start
+ start
(if (search-forward "\n\n" nil t)
(1- (point))
;; This will never happen, but just to be on the safe side --
;; if there is no head-body delimiter, we search a bit manually.
(while (and (looking-at "From \\|[^ \t]+:")
(not (eobp)))
- (forward-line 1)
- (point))))
+ (forward-line 1))
+ (point)))
;; Find the Message-ID header.
(goto-char (point-min))
(if (re-search-forward "^Message-ID[ \t]*:[ \n\t]*\\(<[^>]+>\\)" nil t)
(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
- ;; fetches from a file.
+ ;; fetching from a file.
(cond ((or (looking-at "\^L")
(looking-at "BABYL OPTIONS:"))
(nnmail-process-babyl-mail-format func artnum-func))
(funcall exit-func))
(kill-buffer (current-buffer)))))
-;; Mail crossposts suggested by Brian Edmonds <edmonds@cs.ubc.ca>.
(defun nnmail-article-group (func)
"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)
(obuf (current-buffer))
(beg (point-min))
- end group-art method)
- (if (and (sequencep methods) (= (length methods) 1))
+ end group-art method regrepp)
+ (if (and (sequencep methods)
+ (= (length methods) 1))
;; If there is only just one group to put everything in, we
;; just return a list with just this one method in.
(setq group-art
(fboundp nnmail-split-methods))
(let ((split
(condition-case nil
+ ;; `nnmail-split-methods' is a function, so we
+ ;; just call this function here and use the
+ ;; result.
(or (funcall nnmail-split-methods)
'("bogus"))
(error
- (message
+ (message
"Error in `nnmail-split-methods'; using `bogus' mail group")
(sit-for 1)
'("bogus")))))
- (unless (equal split '(junk))
- ;; `nnmail-split-methods' is a function, so we just call
- ;; this function here and use the result.
+ (setq split (remove-duplicates split :test 'equal))
+ ;; The article may be "cross-posted" to `junk'. What
+ ;; to do? Just remove the `junk' spec. Don't really
+ ;; see anything else to do...
+ (let (elem)
+ (while (setq elem (car (memq 'junk split)))
+ (setq split (delq elem split))))
+ (when split
(setq group-art
(mapcar
(lambda (group) (cons group (funcall func group)))
split))))
;; Go through the split methods to find a match.
- (while (and methods (or nnmail-crosspost (not group-art)))
+ (while (and methods
+ (or nnmail-crosspost
+ (not group-art)))
(goto-char (point-max))
- (setq method (pop methods))
+ (setq method (pop methods)
+ regrepp nil)
(if (or methods
(not (equal "" (nth 1 method))))
(when (and
(ignore-errors
(if (stringp (nth 1 method))
- (re-search-backward (cadr method) nil t)
+ (progn
+ (setq regrepp
+ (string-match "\\\\[0-9&]" (car 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
+ ;; 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 (if regrepp
+ (replace-match
+ (car method) nil nil (car method))
+ (car method))
+ (funcall func (car method)))
group-art))
- ;; This is the final group, which is used as a
+ ;; This is the final group, which is used as a
;; catch-all.
(unless group-art
- (setq group-art
+ (setq group-art
(list (cons (car method)
(funcall func (car method)))))))))
;; See whether the split methods returned `junk'.
(if (equal group-art '(junk))
nil
- (nreverse (delq 'junk group-art)))))))
+ ;; The article may be "cross-posted" to `junk'. What
+ ;; to do? Just remove the `junk' spec. Don't really
+ ;; see anything else to do...
+ (let (elem)
+ (while (setq elem (car (memq 'junk group-art)))
+ (setq group-art (delq elem group-art)))
+ (nreverse group-art)))))))
(defun nnmail-insert-lines ()
"Insert how many lines there are in the body of the mail.
(progn (forward-line 1) (point))))
(insert (format "Xref: %s" (system-name)))
(while group-alist
- (insert (format " %s:%d" (caar group-alist) (cdar group-alist)))
+ (insert (format " %s:%d"
+ (gnus-encode-coding-string (caar group-alist)
+ nnmail-pathname-coding-system)
+ (cdar group-alist)))
(setq group-alist (cdr group-alist)))
(insert "\n"))))
;;; Utility functions
-;; Written by byer@mv.us.adobe.com (Scott Byer).
(defun nnmail-make-complex-temp-name (prefix)
(let ((newname (make-temp-name prefix))
(newprefix prefix))
;; Builtin : operation.
((eq (car split) ':)
- (nnmail-split-it (eval (cdr split))))
+ (nnmail-split-it (save-excursion (eval (cdr split)))))
;; Check the cache for the regexp for this split.
;; FIX FIX FIX could avoid calling assq twice here
(if (null nnmail-spool-file)
;; No spool file whatsoever.
nil
- (let* ((procmails
+ (let* ((procmails
;; If procmail is used to get incoming mail, the files
;; are stored in this directory.
(and (file-exists-p nnmail-procmail-directory)
(or (eq nnmail-spool-file 'procmail)
nnmail-use-procmail)
- (directory-files
- nnmail-procmail-directory
- t (concat (if group (concat "^" group) "")
+ (directory-files
+ nnmail-procmail-directory
+ t (concat (if group (concat "^" (regexp-quote group)) "")
nnmail-procmail-suffix "$"))))
(p procmails)
(crash (when (and (file-exists-p nnmail-crash-box)
0))
(list nnmail-crash-box))))
;; Remove any directories that inadvertently match the procmail
- ;; suffix, which might happen if the suffix is "".
+ ;; suffix, which might happen if the suffix is "".
(while p
(when (file-directory-p (car p))
(setq procmails (delete (car p) procmails)))
(setq p (cdr p)))
;; Return the list of spools.
- (append
+ (append
crash
(cond ((and group
(or (eq nnmail-spool-file 'procmail)
nil)
((listp nnmail-spool-file)
(nconc
- (apply
+ (apply
'nconc
- (mapcar
+ (mapcar
(lambda (file)
(if (and (not (string-match "^po:" file))
(file-directory-p file))
((stringp nnmail-spool-file)
(if (and (not (string-match "^po:" nnmail-spool-file))
(file-directory-p nnmail-spool-file))
- (nconc
+ (nconc
(nnheader-directory-regular-files nnmail-spool-file)
procmails)
(cons nnmail-spool-file procmails)))
(t
procmails))))))
-;; Activate a backend only if it isn't already activated.
-;; If FORCE, re-read the active file even if the backend is
+;; Activate a backend only if it isn't already activated.
+;; If FORCE, re-read the active file even if the backend is
;; already activated.
(defun nnmail-activate (backend &optional force)
+ (nnheader-init-server-buffer)
(let (file timestamp file-time)
(if (or (not (symbol-value (intern (format "%s-group-alist" backend))))
force
(and (setq file (ignore-errors
- (symbol-value (intern (format "%s-active-file"
+ (symbol-value (intern (format "%s-active-file"
backend)))))
(setq file-time (nth 5 (file-attributes file)))
(or (not
(setq timestamp
(condition-case ()
(symbol-value (intern
- (format "%s-active-timestamp"
+ (format "%s-active-timestamp"
backend)))
(error 'none))))
(not (consp timestamp))
(> (nth 1 file-time) (nth 1 timestamp))))))
(save-excursion
(or (eq timestamp 'none)
- (set (intern (format "%s-active-timestamp" backend))
-;;; 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)))
-;;; dmoore@ucsd.edu 25.10.96
-;;; BACKEND-request-list already does this itself!
-;;; (set (intern (format "%s-group-alist" backend))
-;;; (nnmail-get-active))
- ))
+ (set (intern (format "%s-active-timestamp" backend))
+ file-time))
+ (funcall (intern (format "%s-request-list" backend)))))
t))
(defun nnmail-message-id ()
(buffer-name nnmail-cache-buffer)))
() ; The buffer is open.
(save-excursion
- (set-buffer
- (setq nnmail-cache-buffer
+ (set-buffer
+ (setq nnmail-cache-buffer
(get-buffer-create " *nnmail message-id cache*")))
(buffer-disable-undo (current-buffer))
(when (file-exists-p nnmail-message-id-cache-file)
nnmail-message-id-cache-file nil 'silent)
(set-buffer-modified-p nil)
(setq nnmail-cache-buffer nil)
- ;;(kill-buffer (current-buffer))
- )))
+ (kill-buffer (current-buffer)))))
(defun nnmail-cache-insert (id)
(when nnmail-treat-duplicates
+ (unless (gnus-buffer-live-p nnmail-cache-buffer)
+ (nnmail-cache-open))
(save-excursion
(set-buffer nnmail-cache-buffer)
(goto-char (point-max))
(goto-char (point-max))
(search-backward id nil t))))
+(defun nnmail-fetch-field (header)
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-head)
+ (message-fetch-field header))))
+
(defun nnmail-check-duplication (message-id func artnum-func)
(run-hooks 'nnmail-prepare-incoming-message-hook)
;; If this is a duplicate message, then we do not save it.
(setq group-art nil))
((eq action 'warn)
;; We insert a warning.
- (let ((case-fold-search t)
- (newid (nnmail-message-id)))
+ (let ((case-fold-search t))
(goto-char (point-min))
- (when (re-search-forward "^message-id[ \t]*:" nil t)
- (beginning-of-line)
- (insert "Original-"))
+ (re-search-forward "^message-id[ \t]*:" nil t)
(beginning-of-line)
- (insert
- "Message-ID: " newid "\n"
+ (insert
"Gnus-Warning: This is a duplicate of message " message-id "\n")
- (nnmail-cache-insert newid)
(funcall func (setq group-art
(nreverse (nnmail-article-group artnum-func))))))
(t
(defun nnmail-get-new-mail (method exit-func temp
&optional group spool-func)
"Read new incoming mail."
- ;; Nix out the previous split history.
- (unless group
- (setq nnmail-split-history nil))
(let* ((spools (nnmail-get-spool-files group))
(group-in group)
- incoming incomings spool)
+ nnmail-current-spool incoming incomings spool)
(when (and (nnmail-get-value "%s-get-new-mail" method)
nnmail-spool-file)
;; We first activate all the groups.
(nnheader-message 3 "%s: Reading incoming mail..." method)
(when (and (nnmail-move-inbox spool)
(file-exists-p nnmail-crash-box))
+ (setq nnmail-current-spool spool)
;; There is new mail. We first find out if all this mail
;; is supposed to go to some specific group.
(setq group (nnmail-get-split-group spool group-in))
;; We split the mail
- (nnmail-split-incoming
+ (nnmail-split-incoming
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.
+ ;; Check whether the inbox is to be moved to the special tmp dir.
(setq incoming
- (nnmail-make-complex-temp-name
- (expand-file-name
+ (nnmail-make-complex-temp-name
+ (expand-file-name
(if nnmail-tmp-directory
- (concat
+ (concat
(file-name-as-directory nnmail-tmp-directory)
(file-name-nondirectory
(concat (file-name-as-directory temp) "Incoming")))
(concat (file-name-as-directory temp) "Incoming")))))
+ (unless (file-exists-p (file-name-directory incoming))
+ (make-directory (file-name-directory incoming) t))
(rename-file nnmail-crash-box incoming t)
(push incoming incomings))))
- ;; If we did indeed read any incoming spools, we save all info.
+ ;; If we did indeed read any incoming spools, we save all info.
(when incomings
- (nnmail-save-active
+ (nnmail-save-active
(nnmail-get-value "%s-group-alist" method)
(nnmail-get-value "%s-active-file" method))
(when exit-func
(unless nnmail-read-passwd
(if (load "passwd" t)
(setq nnmail-read-passwd 'read-passwd)
- (autoload 'ange-ftp-read-passwd "ange-ftp")
+ (unless (fboundp 'ange-ftp-read-passwd)
+ (autoload 'ange-ftp-read-passwd "ange-ftp"))
(setq nnmail-read-passwd 'ange-ftp-read-passwd)))
(funcall nnmail-read-passwd prompt)))
(defun nnmail-write-region (start end filename &optional append visit lockname)
"Do a `write-region', and then set the file modes."
- (write-region start end filename append visit lockname)
- (set-file-modes filename nnmail-default-file-modes))
+ (let ((coding-system-for-write nnmail-file-coding-system)
+ (pathname-coding-system 'binary))
+ (write-region start end filename append visit lockname)
+ (set-file-modes filename nnmail-default-file-modes)))
;;;
;;; Status functions
", "))
(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)))
+ (pop history))
+ (setq nnmail-split-history (delq nil nnmail-split-history))))
+
(defun nnmail-new-mail-p (group)
"Say whether GROUP has new mail."
(let ((his nnmail-split-history)
his nil)))
found))
+(eval-and-compile
+ (autoload 'pop3-movemail "pop3"))
+
(defun nnmail-pop3-movemail (inbox crashbox)
"Function to move mail from INBOX on a pop3 server to file CRASHBOX."
- (require 'pop3)
(let ((pop3-maildrop
(substring inbox (match-end (string-match "^po:" inbox)))))
(pop3-movemail crashbox)))
(run-hooks 'nnmail-load-hook)
-
+
(provide 'nnmail)
;;; nnmail.el ends here