(require 'nnheader)
(require 'timezone)
-(require 'sendmail)
(require 'message)
(eval-when-compile (require 'cl))
If nil, groups like \"mail.misc\" will end up in directories like
\"mail/misc/\".")
+(defvar nnmail-default-file-modes 384
+ "Set the mode bits of all new mail files to this integer.")
+
(defvar nnmail-expiry-wait 7
"*Expirable articles that are older than this will be expired.
This variable can either be a number (which will be interpreted as a
(lambda (newsgroup)
(cond ((string-match \"private\" newsgroup) 31)
((string-match \"junk\" newsgroup) 1)
- ((string-match \"important\" 'never))
+ ((string-match \"important\" newsgroup) 'never)
(t 7))))")
(defvar nnmail-spool-file
(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.)
Eg.
\(add-hook 'nnmail-read-incoming-hook
- (lambda ()
+ (lambda ()
(start-process \"mailsend\" nil
\"/local/bin/mailsend\" \"read\" \"mbox\")))
(lambda ()
;; Update the displayed time, since that will clear out
;; the flag that says you have mail.
- (if (eq (process-status \"display-time\") 'run)
- (display-time-filter display-time-process \"\"))))")
+ (when (eq (process-status \"display-time\") 'run)
+ (display-time-filter display-time-process \"\"))))")
+
+(when (eq system-type 'windows-nt)
+ (add-hook 'nnmail-prepare-incoming-hook 'nnheader-ms-strip-cr))
;; 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.")
\(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\")
\"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'.")
;;; Internal variables.
-(defvar nnmail-split-fancy-syntax-table
- (copy-syntax-table (standard-syntax-table))
+(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
(defconst nnmail-version "nnmail 1.0"
"Insert FILE in server buffer safely."
(set-buffer nntp-server-buffer)
(erase-buffer)
- (condition-case ()
- (progn (nnheader-insert-raw-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-group-pathname (group dir &optional file)
"Make pathname for GROUP."
"Convert DAYS into time."
(let* ((seconds (* 1.0 days 60 60 24))
(rest (expt 2 16))
- (ms (condition-case nil (round (/ seconds rest))
+ (ms (condition-case nil (round (/ seconds rest))
(range-error (expt 2 16)))))
(list ms (condition-case nil (round (- seconds (* ms rest)))
(range-error (expt 2 16))))))
;; Convert date strings to internal time.
(setq time (nnmail-date-to-time time)))
(let* ((current (current-time))
- (rest (if (< (nth 1 current) (nth 1 time)) (expt 2 16))))
+ (rest (when (< (nth 1 current) (nth 1 time))
+ (expt 2 16))))
(list (- (+ (car current) (if rest -1 0)) (car time))
(- (+ (or rest 0) (nth 1 current)) (nth 1 time)))))
-;; Function taken from rmail.el.
+;; Function rewritten from rmail.el.
(defun nnmail-move-inbox (inbox)
"Move INBOX to `nnmail-crash-box'."
- (let ((inbox (file-truename
- (expand-file-name (substitute-in-file-name inbox))))
- (tofile (file-truename (expand-file-name
- (substitute-in-file-name nnmail-crash-box))))
- movemail popmail errors)
- ;; If getting from mail spool directory,
- ;; use movemail to move rather than just renaming,
- ;; so as to interlock with the mailer.
- (unless (setq popmail (string-match "^po:" (file-name-nondirectory inbox)))
- (setq movemail t))
- (when popmail
- (setq inbox (file-name-nondirectory inbox)))
- (when (and movemail
- ;; On some systems, /usr/spool/mail/foo is a directory
- ;; and the actual inbox is /usr/spool/mail/foo/foo.
- (file-directory-p inbox))
- (setq inbox (expand-file-name (user-login-name) inbox)))
- (if popmail
- (message "Getting mail from post office ...")
- (when (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)))
- ;; Set TOFILE if have not already done so, and
- ;; rename or copy the file INBOX to TOFILE if and as appropriate.
- (cond ((file-exists-p tofile)
- ;; The crash box exists already.
- t)
- ((and (not popmail)
- (not (file-exists-p inbox)))
- ;; There is no inbox.
- (setq tofile nil))
- ((and (not movemail) (not popmail))
- ;; Try copying. If that fails (perhaps no space),
- ;; rename instead.
- (condition-case nil
- (copy-file inbox tofile nil)
- (error
- ;; Third arg is t so we can replace existing file TOFILE.
- (rename-file inbox tofile t)))
- ;; Make the real inbox file empty.
- ;; Leaving it deleted could cause lossage
- ;; because mailers often won't create the file.
- (condition-case ()
- (write-region (point) (point) inbox)
- (file-error nil)))
- (t
- (unwind-protect
- (save-excursion
- (setq errors (generate-new-buffer " *nnmail loss*"))
- (buffer-disable-undo errors)
- (let ((default-directory "/"))
- (call-process
- (expand-file-name nnmail-movemail-program exec-directory)
- nil errors nil inbox tofile))
- (if (not (buffer-modified-p errors))
- ;; No output => movemail won
- nil
- (set-buffer errors)
- (subst-char-in-region (point-min) (point-max) ?\n ?\ )
- (goto-char (point-max))
- (skip-chars-backward " \t")
- (delete-region (point) (point-max))
- (goto-char (point-min))
- (if (looking-at "movemail: ")
- (delete-region (point-min) (match-end 0)))
- (beep t)
- (message (concat "movemail: "
- (buffer-substring (point-min)
- (point-max))))
- (sit-for 3)
- (setq tofile nil))))))
- (and errors
- (buffer-name errors)
- (kill-buffer errors))
- tofile))
+ (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))
+ (when popmail
+ (setq inbox (file-name-nondirectory inbox)))
+ (when (and movemail
+ ;; On some systems, /usr/spool/mail/foo is a directory
+ ;; and the actual inbox is /usr/spool/mail/foo/foo.
+ (file-directory-p inbox))
+ (setq inbox (expand-file-name (user-login-name) inbox)))
+ (if (member inbox nnmail-moved-inboxes)
+ nil
+ (if popmail
+ (progn
+ (setq nnmail-internal-password nnmail-pop-password)
+ (when (and nnmail-pop-password-required (not nnmail-pop-password))
+ (setq nnmail-internal-password
+ (nnmail-read-passwd
+ (format "Password for %s: "
+ (substring inbox (+ popmail 3))))))
+ (message "Getting mail from 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)))
+ ;; Set TOFILE if have not already done so, and
+ ;; rename or copy the file INBOX to TOFILE if and as appropriate.
+ (cond
+ ((file-exists-p tofile)
+ ;; The crash box exists already.
+ t)
+ ((and (not popmail)
+ (not (file-exists-p inbox)))
+ ;; There is no inbox.
+ (setq tofile nil))
+ ((and (not movemail) (not popmail))
+ ;; Try copying. If that fails (perhaps no space),
+ ;; rename instead.
+ (condition-case nil
+ (copy-file inbox tofile nil)
+ (error
+ ;; Third arg is t so we can replace existing file TOFILE.
+ (rename-file inbox tofile t)))
+ (push inbox nnmail-moved-inboxes)
+ ;; Make the real inbox file empty.
+ ;; Leaving it deleted could cause lossage
+ ;; because mailers often won't create the file.
+ (condition-case ()
+ (write-region (point) (point) inbox)
+ (file-error nil)))
+ (t
+ ;; Use movemail.
+ (unwind-protect
+ (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))
+ ;; No output => movemail won
+ (progn
+ (or popmail
+ (set-file-modes tofile nnmail-default-file-modes))
+ (push inbox nnmail-moved-inboxes))
+ (set-buffer errors)
+ ;; There may be a warning about older revisions. We
+ ;; ignore those.
+ (goto-char (point-min))
+ (if (search-forward "older revision" nil t)
+ (progn
+ (or popmail
+ (set-file-modes tofile nnmail-default-file-modes))
+ (push inbox nnmail-moved-inboxes))
+ ;; Probably a real error.
+ (subst-char-in-region (point-min) (point-max) ?\n ?\ )
+ (goto-char (point-max))
+ (skip-chars-backward " \t")
+ (delete-region (point) (point-max))
+ (goto-char (point-min))
+ (when (looking-at "movemail: ")
+ (delete-region (point-min) (match-end 0)))
+ (unless (yes-or-no-p
+ (format "movemail: %s. Continue? "
+ (buffer-string)))
+ (error "%s" (buffer-string)))
+ (setq tofile nil)))))))
+ (and errors
+ (buffer-name errors)
+ (kill-buffer errors))
+ tofile))))
(defun nnmail-get-active ()
"Returns an assoc of group names and active ranges.
(defun nnmail-save-active (group-assoc file-name)
"Save GROUP-ASSOC in ACTIVE-FILE."
(when file-name
- (let (group)
- (save-excursion
- (set-buffer (get-buffer-create " *nnmail active*"))
- (buffer-disable-undo (current-buffer))
- (erase-buffer)
- (while group-assoc
- (setq group (pop group-assoc))
- (insert (format "%s %d %d y\n" (car group) (cdadr group)
- (caadr group))))
- (unless (file-exists-p (file-name-directory file-name))
- (make-directory (file-name-directory file-name) t))
- (write-region 1 (point-max) (expand-file-name file-name) nil 'nomesg)
- (kill-buffer (current-buffer))))))
+ (nnheader-temp-write file-name
+ (nnmail-generate-active group-assoc))))
+
+(defun nnmail-generate-active (alist)
+ "Generate an active file from group-alist ALIST."
+ (erase-buffer)
+ (let (group)
+ (while (setq group (pop alist))
+ (insert (format "%s %d %d y\n" (car group) (cdadr group)
+ (caadr group))))))
(defun nnmail-get-split-group (file group)
+ "Find out whether this FILE is to be split into GROUP only.
+If GROUP is non-nil and we are using procmail, return the group name
+only when the file is the correct procmail file. When GROUP is nil,
+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)
- (cond (group group)
- ((string-match (concat "^" (expand-file-name
- (file-name-as-directory
- nnmail-procmail-directory))
- "\\([^/]*\\)" nnmail-procmail-suffix "$")
- (expand-file-name file))
- (substring (expand-file-name file)
- (match-beginning 1) (match-end 1)))
- (t
- group))
+ (if (string-match (concat "^" (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)
+ (match-end 1))))
+ (if group
+ (if (string-equal group procmail-group)
+ group
+ nil)
+ procmail-group))
+ nil)
group))
-(defun nnmail-process-babyl-mail-format (func)
+(defun nnmail-process-babyl-mail-format (func artnum-func)
(let ((case-fold-search t)
start message-id content-length do-search end)
(while (not (eobp))
"\f\n0, *unseen,+\n\\(\\*\\*\\* EOOH \\*\\*\\*\n\\)?" nil t)
(goto-char (match-end 0))
(delete-region (match-beginning 0) (match-end 0))
- (setq start (point))
- ;; Skip all the headers in case there are more "From "s...
- (or (search-forward "\n\n" nil t)
- (search-forward-regexp "^[^:]*\\( .*\\|\\)$" nil t)
- (search-forward "\1f\f"))
+ (narrow-to-region
+ (setq start (point))
+ (progn
+ ;; Skip all the headers in case there are more "From "s...
+ (or (search-forward "\n\n" nil t)
+ (search-forward-regexp "^[^:]*\\( .*\\|\\)$" nil t)
+ (search-forward "\1f\f"))
+ (point)))
+ (run-hooks 'nnmail-prepare-incoming-header-hook)
+ (widen)
;; Find the Message-ID header.
(save-excursion
(if (re-search-backward "^Message-ID:[ \t]*\\(<[^>]*>\\)" nil t)
(setq do-search t)))
;; Go to the beginning of the next article - or to the end
;; of the buffer.
- (if do-search
- (if (re-search-forward "^\1f" nil t)
- (goto-char (match-beginning 0))
- (goto-char (1- (point-max)))))
+ (when do-search
+ (if (re-search-forward "^\1f" nil t)
+ (goto-char (match-beginning 0))
+ (goto-char (1- (point-max)))))
(delete-char 1) ; delete ^_
(save-excursion
(save-restriction
(narrow-to-region start (point))
(goto-char (point-min))
- (nnmail-check-duplication message-id func)
+ (nnmail-check-duplication message-id func artnum-func)
(setq end (point-max))))
(goto-char end))))