X-Git-Url: http://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fnnmail.el;h=8d6c342e76edccd34ed926d974266c0fa0bbd537;hb=1866e8307d0f95ac3fa5fefaa6dba11852474ac7;hp=896e88338ba71bb80b85aee89f8536cd71bc0db9;hpb=ddfdb23b95b4dbb6eedd9111fe7e0df1a8e60257;p=gnus diff --git a/lisp/nnmail.el b/lisp/nnmail.el index 896e88338..8d6c342e7 100644 --- a/lisp/nnmail.el +++ b/lisp/nnmail.el @@ -27,7 +27,6 @@ (require 'nnheader) (require 'timezone) -(require 'sendmail) (require 'message) (eval-when-compile (require 'cl)) @@ -75,6 +74,9 @@ new mail into folder numbers that Gnus has marked as expired.") 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 @@ -136,7 +138,14 @@ 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. @@ -165,17 +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 . (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 . (defvar nnmail-tmp-directory nil "*If non-nil, use this directory for temporary storage when reading incoming mail.") @@ -233,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'.") @@ -261,9 +291,16 @@ parameter. It should return nil, `warn' or `delete'.") ;;; Internal variables. -(defvar nnmail-split-fancy-syntax-table - (copy-syntax-table (standard-syntax-table)) +(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.") @@ -271,6 +308,8 @@ parameter. It should return nil, `warn' or `delete'.") (defvar nnmail-moved-inboxes nil "List of inboxes that have been moved.") +(defvar nnmail-internal-password nil) + (defconst nnmail-version "nnmail 1.0" @@ -285,9 +324,11 @@ parameter. It should return nil, `warn' or `delete'.") "Insert FILE in server buffer safely." (set-buffer nntp-server-buffer) (erase-buffer) - (condition-case () - (progn (nnheader-insert-file-contents-literally 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." @@ -338,14 +379,12 @@ parameter. It should return nil, `warn' or `delete'.") (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) + (let ((inbox (file-truename (expand-file-name inbox))) + (tofile (file-truename (expand-file-name nnmail-crash-box))) + movemail popmail errors password) ;; If getting from mail spool directory, ;; use movemail to move rather than just renaming, ;; so as to interlock with the mailer. @@ -361,11 +400,18 @@ parameter. It should return nil, `warn' or `delete'.") (if (member inbox nnmail-moved-inboxes) nil (if popmail - (message "Getting mail from post office ...") + (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 (nth 7 (file-attributes tofile)))) + (/= 0 (nnheader-file-size tofile))) (and (file-exists-p inbox) - (/= 0 (nth 7 (file-attributes 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. @@ -380,12 +426,12 @@ parameter. It should return nil, `warn' or `delete'.") ((and (not movemail) (not popmail)) ;; Try copying. If that fails (perhaps no space), ;; rename instead. - (when (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)) + (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. @@ -399,26 +445,44 @@ parameter. It should return nil, `warn' or `delete'.") (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 (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 - (push tofile nnmail-moved-inboxes) + (progn + (or popmail + (set-file-modes inbox nnmail-default-file-modes)) + (push inbox nnmail-moved-inboxes)) (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)) + ;; There may be a warning about older revisions. We + ;; ignore those. (goto-char (point-min)) - (if (looking-at "movemail: ") + (if (search-forward "older revision" nil t) + (progn + (or popmail + (set-file-modes inbox 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))) - (beep t) - (message (concat "movemail: " - (buffer-substring (point-min) - (point-max)))) - (sit-for 3) - (setq tofile nil)))))) + (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)) @@ -455,25 +519,35 @@ nn*-request-list should have been called before calling this function." (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) + (nnmail-write-region + 1 (point-max) (expand-file-name file-name) nil 'nomesg) (kill-buffer (current-buffer)))))) (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)) @@ -482,11 +556,16 @@ nn*-request-list should have been called before calling this function." " \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 " ")) + (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 " ")) + (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) @@ -524,16 +603,16 @@ nn*-request-list should have been called before calling this function." (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 "^" nil t) - (goto-char (match-beginning 0)) - (goto-char (1- (point-max))))) + (when do-search + (if (re-search-forward "^" 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)))) @@ -552,7 +631,7 @@ nn*-request-list should have been called before calling this function." (setq found 'no))) (eq found 'yes))) -(defun nnmail-process-unix-mail-format (func) +(defun nnmail-process-unix-mail-format (func artnum-func) (let ((case-fold-search t) (delim (concat "^" message-unix-mail-delimiter)) start message-id content-length end skip head-end) @@ -598,6 +677,7 @@ nn*-request-list should have been called before calling this function." ;; having a (possibly) faulty header. (beginning-of-line) (insert "X-")) + (run-hooks 'nnmail-prepare-incoming-header-hook) ;; Find the end of this article. (goto-char (point-max)) (widen) @@ -633,11 +713,11 @@ nn*-request-list should have been called before calling this function." (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))))) -(defun nnmail-process-mmdf-mail-format (func) +(defun nnmail-process-mmdf-mail-format (func artnum-func) (let ((delim "^\^A\^A\^A\^A$") (case-fold-search t) start message-id end) @@ -671,6 +751,7 @@ nn*-request-list should have been called before calling this function." (insert "Original-"))) (forward-line 1) (insert "Message-ID: " (setq message-id (nnmail-message-id)) "\n")) + (run-hooks 'nnmail-prepare-incoming-header-hook) ;; Find the end of this article. (goto-char (point-max)) (widen) @@ -682,12 +763,13 @@ nn*-request-list should have been called before calling this function." (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) (forward-line 2))))) -(defun nnmail-split-incoming (incoming func &optional exit-func group) +(defun nnmail-split-incoming (incoming func &optional exit-func + group artnum-func) "Go through the entire INCOMING file and pick out each individual mail. FUNC will be called with the buffer narrowed to each mail." (let (;; If this is a group-specific split, we bind the split @@ -712,11 +794,11 @@ FUNC will be called with the buffer narrowed to each mail." ;; fetches from a file. (cond ((or (looking-at "\^L") (looking-at "BABYL OPTIONS:")) - (nnmail-process-babyl-mail-format func)) + (nnmail-process-babyl-mail-format func artnum-func)) ((looking-at "\^A\^A\^A\^A") - (nnmail-process-mmdf-mail-format func)) + (nnmail-process-mmdf-mail-format func artnum-func)) (t - (nnmail-process-unix-mail-format func)))) + (nnmail-process-unix-mail-format func artnum-func)))) (if exit-func (funcall exit-func)) (kill-buffer (current-buffer))))) @@ -746,6 +828,8 @@ 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)) + ;; Allow washing. + (run-hooks 'nnmail-split-hook) (if (and (symbolp nnmail-split-methods) (fboundp nnmail-split-methods)) ;; `nnmail-split-methods' is a function, so we just call @@ -785,7 +869,10 @@ FUNC will be called with the group name to determine the article number." (setq group-art (list (cons (car method) (funcall func (car method))))))))) - group-art)))) + ;; See whether the split methods returned `junk'. + (if (equal group-art '(junk)) + nil + (nreverse (delq 'junk group-art))))))) (defun nnmail-insert-lines () "Insert how many lines there are in the body of the mail. @@ -819,6 +906,31 @@ Return the number of characters in the body." (setq group-alist (cdr group-alist))) (insert "\n")))) +;;; Message washing functions + +(defun nnmail-remove-leading-whitespace () + "Remove excessive whitespace from all headers." + (goto-char (point-min)) + (while (re-search-forward "^\\([^ :]+: \\) +" nil t) + (replace-match "\\1" t t))) + +(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 "\\|")))) + (when regexp + (goto-char (point-min)) + (when (re-search-forward + (concat "Subject: +\\(Re: +\\)?\\(" regexp "\\) *") + nil t) + (delete-region (match-beginning 2) (match-end 0)))))) + +(defun nnmail-remove-tabs () + "Translate TAB characters into SPACE characters." + (subst-char-in-region (point-min) (point-max) ?\t ? t)) + +;;; Utility functions + ;; Written by byer@mv.us.adobe.com (Scott Byer). (defun nnmail-make-complex-temp-name (prefix) (let ((newname (make-temp-name prefix)) @@ -845,42 +957,44 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (defun nnmail-split-it (split) ;; Return a list of groups matching SPLIT. - (cond ((stringp split) - ;; A group. - (list split)) - ((eq (car split) '&) - (apply 'nconc (mapcar 'nnmail-split-it (cdr split)))) - ((eq (car split) '|) - (let (done) - (while (and (not done) (cdr split)) - (setq split (cdr split) - done (nnmail-split-it (car split)))) - done)) - ((assq split nnmail-split-cache) - ;; A compiled match expression. - (goto-char (point-max)) - (if (re-search-backward (cdr (assq split nnmail-split-cache)) nil t) - (nnmail-split-it (nth 2 split)))) - (t - ;; An uncompiled match. - (let* ((field (nth 0 split)) - (value (nth 1 split)) - (regexp (concat "^\\(" - (if (symbolp field) - (cdr (assq field - nnmail-split-abbrev-alist)) - field) - "\\):.*\\<\\(" - (if (symbolp value) - (cdr (assq value - nnmail-split-abbrev-alist)) - value) - "\\>\\)"))) - (setq nnmail-split-cache - (cons (cons split regexp) nnmail-split-cache)) - (goto-char (point-max)) - (if (re-search-backward regexp nil t) - (nnmail-split-it (nth 2 split))))))) + (cond + ((stringp split) + ;; A group. + (list split)) + ((eq split 'junk) + ;; Junk this. + (list 'junk)) + ((eq (car split) '&) + (apply 'nconc (mapcar 'nnmail-split-it (cdr split)))) + ((eq (car split) '|) + (let (done) + (while (and (not done) (cdr split)) + (setq split (cdr split) + done (nnmail-split-it (car split)))) + done)) + ((assq split nnmail-split-cache) + ;; A compiled match expression. + (goto-char (point-max)) + (if (re-search-backward (cdr (assq split nnmail-split-cache)) nil t) + (nnmail-split-it (nth 2 split)))) + (t + ;; An uncompiled match. + (let* ((field (nth 0 split)) + (value (nth 1 split)) + (regexp (concat "^\\(" + (if (symbolp field) + (cdr (assq field nnmail-split-abbrev-alist)) + field) + "\\):.*\\<\\(" + (if (symbolp value) + (cdr (assq value nnmail-split-abbrev-alist)) + value) + "\\)\\>"))) + (setq nnmail-split-cache + (cons (cons split regexp) nnmail-split-cache)) + (goto-char (point-max)) + (if (re-search-backward regexp nil t) + (nnmail-split-it (nth 2 split))))))) ;; Get a list of spool files to read. (defun nnmail-get-spool-files (&optional group) @@ -896,11 +1010,11 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (directory-files nnmail-procmail-directory t (concat (if group (concat "^" group) "") - nnmail-procmail-suffix "$") t))) + nnmail-procmail-suffix "$")))) (p procmails) (crash (when (and (file-exists-p nnmail-crash-box) - (> (nth 7 (file-attributes - (file-truename nnmail-crash-box))) 0)) + (> (nnheader-file-size + (file-truename nnmail-crash-box)) 0)) (list nnmail-crash-box)))) ;; Remove any directories that inadvertantly match the procmail ;; suffix, which might happen if the suffix is "". @@ -913,8 +1027,12 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." crash (cond ((and group (or (eq nnmail-spool-file 'procmail) - nnmail-use-procmail)) + nnmail-use-procmail) + procmails) procmails) + ((and group + (eq nnmail-spool-file 'procmail)) + nil) ((listp nnmail-spool-file) (append nnmail-spool-file procmails)) ((stringp nnmail-spool-file) @@ -998,8 +1116,8 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (or (file-exists-p (file-name-directory nnmail-message-id-cache-file)) (make-directory (file-name-directory nnmail-message-id-cache-file) t)) - (write-region (point-min) (point-max) - nnmail-message-id-cache-file nil 'silent) + (nnmail-write-region (point-min) (point-max) + nnmail-message-id-cache-file nil 'silent) (set-buffer-modified-p nil) (setq nnmail-cache-buffer nil) ;;(kill-buffer (current-buffer)) @@ -1019,7 +1137,8 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (goto-char (point-max)) (search-backward id nil t)))) -(defun nnmail-check-duplication (message-id func) +(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. (let* ((duplication (nnmail-cache-id-exists-p message-id)) (action (when duplication @@ -1029,11 +1148,14 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." ((nnheader-functionp nnmail-treat-duplicates) (funcall nnmail-treat-duplicates message-id)) (t - nnmail-treat-duplicates))))) + nnmail-treat-duplicates)))) + (group-art (nreverse (nnmail-article-group artnum-func)))) (cond + ((null group-art) + (delete-region (point-min) (point-max))) ((not duplication) (nnmail-cache-insert message-id) - (funcall func)) + (funcall func group-art)) ((eq action 'delete) (delete-region (point-min) (point-max))) ((eq action 'warn) @@ -1049,9 +1171,9 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." "Message-ID: " newid "\n" "Gnus-Warning: This is a duplicate of message " message-id "\n") (nnmail-cache-insert newid) - (funcall func))) + (funcall func group-art))) (t - (funcall func))))) + (funcall func group-art))))) ;;; Get new mail. @@ -1083,7 +1205,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." ;; existance of POPped mail. (when (or (string-match "^po:" spool) (and (file-exists-p spool) - (> (nth 7 (file-attributes (file-truename spool))) 0))) + (> (nnheader-file-size (file-truename spool)) 0))) (nnheader-message 3 "%s: Reading incoming mail..." method) (when (and (nnmail-move-inbox spool) (file-exists-p nnmail-crash-box)) @@ -1093,7 +1215,7 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." ;; We split the mail (nnmail-split-incoming nnmail-crash-box (intern (format "%s-save-mail" method)) - spool-func group) + spool-func group (intern (format "%s-active-number" method))) ;; Check whether the inbox is to be moved to the special tmp dir. (setq incoming (nnmail-make-complex-temp-name @@ -1148,6 +1270,89 @@ See the documentation for the variable `nnmail-split-fancy' for documentation." (setq days (nnmail-days-to-time days)) ;; Compare the time with the current time. (nnmail-time-less days (nnmail-time-since time))))))) + +(defvar nnmail-read-passwd nil) +(defun nnmail-read-passwd (prompt) + (unless nnmail-read-passwd + (if (load "passwd" t) + (setq nnmail-read-passwd 'read-passwd) + (autoload 'ange-ftp-read-passwd "ange-ftp") + (setq nnmail-read-passwd 'ange-ftp-read-passwd))) + (funcall nnmail-read-passwd prompt)) + +(defun nnmail-check-syntax () + "Check (and modify) the syntax of the message in the current buffer." + (save-restriction + (message-narrow-to-head) + (let ((case-fold-search t)) + (unless (re-search-forward "^Message-Id:" nil t) + (insert "Message-ID: " (nnmail-message-id) "\n"))))) + +(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)) + +;;; +;;; Status functions +;;; + +(defun nnmail-replace-status (name value) + "Make status NAME and VALUE part of the current status line." + (save-restriction + (message-narrow-to-head) + (let ((status (nnmail-decode-status))) + (setq status (delq (member name status) status)) + (when value + (push (cons name value) status)) + (message-remove-header "status") + (goto-char (point-max)) + (insert "Status: " (nnmail-encode-status status) "\n")))) + +(defun nnmail-decode-status () + "Return a status-value alist from STATUS." + (goto-char (point-min)) + (when (re-search-forward "^Status: " nil t) + (let (name value status) + (save-restriction + ;; Narrow to the status. + (narrow-to-region + (point) + (if (re-search-forward "^[^ \t]" nil t) + (1- (point)) + (point-max))) + ;; Go through all elements and add them to the list. + (goto-char (point-min)) + (while (re-search-forward "[^ \t=]+" nil t) + (setq name (match-string 0)) + (if (not (= (following-char) ?=)) + ;; Implied "yes". + (setq value "yes") + (forward-char 1) + (if (not (= (following-char) ?\")) + (if (not (looking-at "[^ \t]")) + ;; Implied "no". + (setq value "no") + ;; Unquoted value. + (setq value (match-string 0)) + (goto-char (match-end 0))) + ;; Quoted value. + (setq value (read (current-buffer))))) + (push (cons name value) status))) + status))) + +(defun nnmail-encode-status (status) + "Return a status string from STATUS." + (mapconcat + (lambda (elem) + (concat + (car elem) "=" + (if (string-match "[ \t]" (cdr elem)) + (prin1-to-string (cdr elem)) + (cdr elem)))) + status " ")) + +(run-hooks 'nnmail-load-hook) (provide 'nnmail)