Mercurial > hg > xemacs-beta
comparison lisp/gnus/nnmail.el @ 98:0d2f883870bc r20-1b1
Import from CVS: tag r20-1b1
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:13:56 +0200 |
parents | 131b0175ea99 |
children | 4be1180a9e89 |
comparison
equal
deleted
inserted
replaced
97:498bf5da1c90 | 98:0d2f883870bc |
---|---|
1 ;;; nnmail.el --- mail support functions for the Gnus mail backends | 1 ;;; nnmail.el --- mail support functions for the Gnus mail backends |
2 ;; Copyright (C) 1995,96 Free Software Foundation, Inc. | 2 ;; Copyright (C) 1995,96,97 Free Software Foundation, Inc. |
3 | 3 |
4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no> | 4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no> |
5 ;; Keywords: news, mail | 5 ;; Keywords: news, mail |
6 | 6 |
7 ;; This file is part of GNU Emacs. | 7 ;; This file is part of GNU Emacs. |
27 | 27 |
28 (require 'nnheader) | 28 (require 'nnheader) |
29 (require 'timezone) | 29 (require 'timezone) |
30 (require 'message) | 30 (require 'message) |
31 (eval-when-compile (require 'cl)) | 31 (eval-when-compile (require 'cl)) |
32 | 32 (require 'custom) |
33 (defvar nnmail-split-methods | 33 |
34 (defgroup gnus-mail nil | |
35 "Mailreading.." | |
36 :group 'gnus) | |
37 | |
38 (defcustom nnmail-split-methods | |
34 '(("mail.misc" "")) | 39 '(("mail.misc" "")) |
35 "*Incoming mail will be split according to this variable. | 40 "Incoming mail will be split according to this variable. |
36 | 41 |
37 If you'd like, for instance, one mail group for mail from the | 42 If you'd like, for instance, one mail group for mail from the |
38 \"4ad-l\" mailing list, one group for junk mail and one for everything | 43 \"4ad-l\" mailing list, one group for junk mail and one for everything |
39 else, you could do something like this: | 44 else, you could do something like this: |
40 | 45 |
54 the argument. It should return a non-nil value if it thinks that the | 59 the argument. It should return a non-nil value if it thinks that the |
55 mail belongs in that group. | 60 mail belongs in that group. |
56 | 61 |
57 The last element should always have \"\" as the regexp. | 62 The last element should always have \"\" as the regexp. |
58 | 63 |
59 This variable can also have a function as its value.") | 64 This variable can also have a function as its value." |
65 :group 'gnus-mail | |
66 :type '(choice (repeat :tag "Alist" (group (string :tag "Name") regexp)) | |
67 (function-item nnmail-split-fancy) | |
68 (function :tag "Other"))) | |
60 | 69 |
61 ;; Suggested by Erik Selberg <speed@cs.washington.edu>. | 70 ;; Suggested by Erik Selberg <speed@cs.washington.edu>. |
62 (defvar nnmail-crosspost t | 71 (defcustom nnmail-crosspost t |
63 "*If non-nil, do crossposting if several split methods match the mail. | 72 "If non-nil, do crossposting if several split methods match the mail. |
64 If nil, the first match found will be used.") | 73 If nil, the first match found will be used." |
74 :group 'gnus-mail | |
75 :type 'boolean) | |
65 | 76 |
66 ;; Added by gord@enci.ucalgary.ca (Gordon Matzigkeit). | 77 ;; Added by gord@enci.ucalgary.ca (Gordon Matzigkeit). |
67 (defvar nnmail-keep-last-article nil | 78 (defcustom nnmail-keep-last-article nil |
68 "*If non-nil, nnmail will never delete the last expired article in a directory. | 79 "If non-nil, nnmail will never delete the last expired article in a directory. |
69 You may need to set this variable if other programs are putting | 80 You may need to set this variable if other programs are putting |
70 new mail into folder numbers that Gnus has marked as expired.") | 81 new mail into folder numbers that Gnus has marked as expired." |
71 | 82 :group 'gnus-mail |
72 (defvar nnmail-use-long-file-names nil | 83 :type 'boolean) |
73 "*If non-nil the mail backends will use long file and directory names. | 84 |
85 (defcustom nnmail-use-long-file-names nil | |
86 "If non-nil the mail backends will use long file and directory names. | |
74 If nil, groups like \"mail.misc\" will end up in directories like | 87 If nil, groups like \"mail.misc\" will end up in directories like |
75 \"mail/misc/\".") | 88 \"mail/misc/\"." |
76 | 89 :group 'gnus-mail |
77 (defvar nnmail-expiry-wait 7 | 90 :type 'boolean) |
91 | |
92 (defcustom nnmail-default-file-modes 384 | |
93 "Set the mode bits of all new mail files to this integer." | |
94 :group 'gnus-mail | |
95 :type 'integer) | |
96 | |
97 (defcustom nnmail-expiry-wait 7 | |
78 "*Expirable articles that are older than this will be expired. | 98 "*Expirable articles that are older than this will be expired. |
79 This variable can either be a number (which will be interpreted as a | 99 This variable can either be a number (which will be interpreted as a |
80 number of days) -- this doesn't have to be an integer. This variable | 100 number of days) -- this doesn't have to be an integer. This variable |
81 can also be `immediate' and `never'.") | 101 can also be `immediate' and `never'." |
82 | 102 :group 'gnus-mail |
83 (defvar nnmail-expiry-wait-function nil | 103 :type '(choice (const immediate) |
84 "*Variable that holds function to specify how old articles should be before they are expired. | 104 (integer :tag "days") |
105 (const never))) | |
106 | |
107 (defcustom nnmail-expiry-wait-function nil | |
108 "Variable that holds function to specify how old articles should be before they are expired. | |
85 The function will be called with the name of the group that the | 109 The function will be called with the name of the group that the |
86 expiry is to be performed in, and it should return an integer that | 110 expiry is to be performed in, and it should return an integer that |
87 says how many days an article can be stored before it is considered | 111 says how many days an article can be stored before it is considered |
88 \"old\". It can also return the values `never' and `immediate'. | 112 \"old\". It can also return the values `never' and `immediate'. |
89 | 113 |
90 Eg.: | 114 Eg.: |
91 | 115 |
92 (setq nnmail-expiry-wait-function | 116 \(setq nnmail-expiry-wait-function |
93 (lambda (newsgroup) | 117 (lambda (newsgroup) |
94 (cond ((string-match \"private\" newsgroup) 31) | 118 (cond ((string-match \"private\" newsgroup) 31) |
95 ((string-match \"junk\" newsgroup) 1) | 119 ((string-match \"junk\" newsgroup) 1) |
96 ((string-match \"important\" newsgroup) 'never) | 120 ((string-match \"important\" newsgroup) 'never) |
97 (t 7))))") | 121 (t 7))))" |
98 | 122 :group 'gnus-mail |
99 (defvar nnmail-spool-file | 123 :type '(choice (const :tag "nnmail-expiry-wait" nil) |
124 (function :format "%v" nnmail-))) | |
125 | |
126 (defcustom nnmail-spool-file | |
100 (or (getenv "MAIL") | 127 (or (getenv "MAIL") |
101 (concat "/usr/spool/mail/" (user-login-name))) | 128 (concat "/usr/spool/mail/" (user-login-name))) |
102 "Where the mail backends will look for incoming mail. | 129 "Where the mail backends will look for incoming mail. |
103 This variable is \"/usr/spool/mail/$user\" by default. | 130 This variable is \"/usr/spool/mail/$user\" by default. |
104 If this variable is nil, no mail backends will read incoming mail. | 131 If this variable is nil, no mail backends will read incoming mail. |
105 If this variable is a list, all files mentioned in this list will be | 132 If this variable is a list, all files mentioned in this list will be |
106 used as incoming mailboxes.") | 133 used as incoming mailboxes. |
107 | 134 If this variable is a directory (i. e., it's name ends with a \"/\"), |
108 (defvar nnmail-crash-box "~/.gnus-crash-box" | 135 treat all files in that directory as incoming spool files." |
109 "*File where Gnus will store mail while processing it.") | 136 :group 'gnus-mail |
110 | 137 :type 'file) |
111 (defvar nnmail-use-procmail nil | 138 |
139 (defcustom nnmail-crash-box "~/.gnus-crash-box" | |
140 "File where Gnus will store mail while processing it." | |
141 :group 'gnus-mail | |
142 :type 'file) | |
143 | |
144 (defcustom nnmail-use-procmail nil | |
112 "*If non-nil, the mail backends will look in `nnmail-procmail-directory' for spool files. | 145 "*If non-nil, the mail backends will look in `nnmail-procmail-directory' for spool files. |
113 The file(s) in `nnmail-spool-file' will also be read.") | 146 The file(s) in `nnmail-spool-file' will also be read." |
114 | 147 :group 'gnus-mail |
115 (defvar nnmail-procmail-directory "~/incoming/" | 148 :type 'boolean) |
149 | |
150 (defcustom nnmail-procmail-directory "~/incoming/" | |
116 "*When using procmail (and the like), incoming mail is put in this directory. | 151 "*When using procmail (and the like), incoming mail is put in this directory. |
117 The Gnus mail backends will read the mail from this directory.") | 152 The Gnus mail backends will read the mail from this directory." |
118 | 153 :group 'gnus-mail |
119 (defvar nnmail-procmail-suffix "\\.spool" | 154 :type 'directory) |
155 | |
156 (defcustom nnmail-procmail-suffix "\\.spool" | |
120 "*Suffix of files created by procmail (and the like). | 157 "*Suffix of files created by procmail (and the like). |
121 This variable might be a suffix-regexp to match the suffixes of | 158 This variable might be a suffix-regexp to match the suffixes of |
122 several files - eg. \".spool[0-9]*\".") | 159 several files - eg. \".spool[0-9]*\"." |
123 | 160 :group 'gnus-mail |
124 (defvar nnmail-resplit-incoming nil | 161 :type 'regexp) |
125 "*If non-nil, re-split incoming procmail sorted mail.") | 162 |
126 | 163 (defcustom nnmail-resplit-incoming nil |
127 (defvar nnmail-delete-file-function 'delete-file | 164 "*If non-nil, re-split incoming procmail sorted mail." |
128 "Function called to delete files in some mail backends.") | 165 :group 'gnus-mail |
129 | 166 :type 'boolean) |
130 (defvar nnmail-crosspost-link-function 'add-name-to-file | 167 |
168 (defcustom nnmail-delete-file-function 'delete-file | |
169 "Function called to delete files in some mail backends." | |
170 :group 'gnus-mail | |
171 :type 'function) | |
172 | |
173 (defcustom nnmail-crosspost-link-function 'add-name-to-file | |
131 "Function called to create a copy of a file. | 174 "Function called to create a copy of a file. |
132 This is `add-name-to-file' by default, which means that crossposts | 175 This is `add-name-to-file' by default, which means that crossposts |
133 will use hard links. If your file system doesn't allow hard | 176 will use hard links. If your file system doesn't allow hard |
134 links, you could set this variable to `copy-file' instead.") | 177 links, you could set this variable to `copy-file' instead." |
135 | 178 :group 'gnus-mail |
136 (defvar nnmail-movemail-program "movemail" | 179 :type '(radio (function-item add-name-to-file) |
180 (function-item copy-file) | |
181 (function :tag "Other"))) | |
182 | |
183 (defcustom nnmail-movemail-program "movemail" | |
137 "*A command to be executed to move mail from the inbox. | 184 "*A command to be executed to move mail from the inbox. |
138 The default is \"movemail\".") | 185 The default is \"movemail\". |
139 | 186 |
140 (defvar nnmail-pop-password-required nil | 187 This can also be a function. In that case, the function will be |
141 "*Non-nil if a password is required when reading mail using POP.") | 188 called with two parameters -- the name of the INBOX file, and the file |
142 | 189 to be moved to." |
143 (defvar nnmail-read-incoming-hook nil | 190 :group 'gnus-mail |
144 "*Hook that will be run after the incoming mail has been transferred. | 191 :type 'string) |
192 | |
193 (defcustom nnmail-pop-password-required nil | |
194 "*Non-nil if a password is required when reading mail using POP." | |
195 :group 'gnus-mail | |
196 :type 'boolean) | |
197 | |
198 (defcustom nnmail-read-incoming-hook | |
199 (if (eq system-type 'windows-nt) | |
200 '(nnheader-ms-strip-cr) | |
201 nil) | |
202 "Hook that will be run after the incoming mail has been transferred. | |
145 The incoming mail is moved from `nnmail-spool-file' (which normally is | 203 The incoming mail is moved from `nnmail-spool-file' (which normally is |
146 something like \"/usr/spool/mail/$user\") to the user's home | 204 something like \"/usr/spool/mail/$user\") to the user's home |
147 directory. This hook is called after the incoming mail box has been | 205 directory. This hook is called after the incoming mail box has been |
148 emptied, and can be used to call any mail box programs you have | 206 emptied, and can be used to call any mail box programs you have |
149 running (\"xwatch\", etc.) | 207 running (\"xwatch\", etc.) |
150 | 208 |
151 Eg. | 209 Eg. |
152 | 210 |
153 \(add-hook 'nnmail-read-incoming-hook | 211 \(add-hook 'nnmail-read-incoming-hook |
154 (lambda () | 212 (lambda () |
155 (start-process \"mailsend\" nil | 213 (start-process \"mailsend\" nil |
156 \"/local/bin/mailsend\" \"read\" \"mbox\"))) | 214 \"/local/bin/mailsend\" \"read\" \"mbox\"))) |
157 | 215 |
158 If you have xwatch running, this will alert it that mail has been | 216 If you have xwatch running, this will alert it that mail has been |
159 read. | 217 read. |
162 | 220 |
163 \(add-hook 'nnmail-read-incoming-hook | 221 \(add-hook 'nnmail-read-incoming-hook |
164 (lambda () | 222 (lambda () |
165 ;; Update the displayed time, since that will clear out | 223 ;; Update the displayed time, since that will clear out |
166 ;; the flag that says you have mail. | 224 ;; the flag that says you have mail. |
167 (if (eq (process-status \"display-time\") 'run) | 225 (when (eq (process-status \"display-time\") 'run) |
168 (display-time-filter display-time-process \"\"))))") | 226 (display-time-filter display-time-process \"\"))))" |
169 | 227 :group 'gnus-mail |
170 (when (eq system-type 'windows-nt) | 228 :type 'hook) |
171 (add-hook 'nnmail-prepare-incoming-hook 'nnheader-ms-strip-cr)) | |
172 | 229 |
173 ;; Suggested by Erik Selberg <speed@cs.washington.edu>. | 230 ;; Suggested by Erik Selberg <speed@cs.washington.edu>. |
174 (defvar nnmail-prepare-incoming-hook nil | 231 (defcustom nnmail-prepare-incoming-hook nil |
175 "*Hook called before treating incoming mail. | 232 "Hook called before treating incoming mail. |
176 The hook is run in a buffer with all the new, incoming mail.") | 233 The hook is run in a buffer with all the new, incoming mail." |
177 | 234 :group 'gnus-mail |
178 (defvar nnmail-pre-get-new-mail-hook nil | 235 :type 'hook) |
179 "Hook called just before starting to handle new incoming mail.") | 236 |
180 | 237 (defcustom nnmail-prepare-incoming-header-hook nil |
181 (defvar nnmail-post-get-new-mail-hook nil | 238 "Hook called narrowed to the headers of each message. |
182 "Hook called just after finishing handling new incoming mail.") | 239 This can be used to remove excessive spaces (and stuff like |
240 that) from the headers before splitting and saving the messages." | |
241 :group 'gnus-mail | |
242 :type 'hook) | |
243 | |
244 (defcustom nnmail-prepare-incoming-message-hook nil | |
245 "Hook called narrowed to each message." | |
246 :group 'gnus-mail | |
247 :type 'hook) | |
248 | |
249 (defcustom nnmail-list-identifiers nil | |
250 "Regexp that matches list identifiers to be removed. | |
251 This can also be a list of regexps." | |
252 :group 'gnus-mail | |
253 :type '(choice regexp | |
254 (repeat regexp))) | |
255 | |
256 (defcustom nnmail-pre-get-new-mail-hook nil | |
257 "Hook called just before starting to handle new incoming mail." | |
258 :group 'gnus-mail | |
259 :type 'hook) | |
260 | |
261 (defcustom nnmail-post-get-new-mail-hook nil | |
262 "Hook called just after finishing handling new incoming mail." | |
263 :group 'gnus-mail | |
264 :type 'hook) | |
265 | |
266 (defcustom nnmail-split-hook nil | |
267 "Hook called before deciding where to split an article. | |
268 The functions in this hook are free to modify the buffer | |
269 contents in any way they choose -- the buffer contents are | |
270 discarded after running the split process." | |
271 :group 'gnus-mail | |
272 :type 'hook) | |
183 | 273 |
184 ;; Suggested by Mejia Pablo J <pjm9806@usl.edu>. | 274 ;; Suggested by Mejia Pablo J <pjm9806@usl.edu>. |
185 (defvar nnmail-tmp-directory nil | 275 (defcustom nnmail-tmp-directory nil |
186 "*If non-nil, use this directory for temporary storage when reading incoming mail.") | 276 "*If non-nil, use this directory for temporary storage when reading incoming mail." |
187 | 277 :group 'gnus-mail |
188 (defvar nnmail-large-newsgroup 50 | 278 :type '(choice (const :tag "default" nil) |
279 (directory :format "%v"))) | |
280 | |
281 (defcustom nnmail-large-newsgroup 50 | |
189 "*The number of the articles which indicates a large newsgroup. | 282 "*The number of the articles which indicates a large newsgroup. |
190 If the number of the articles is greater than the value, verbose | 283 If the number of the articles is greater than the value, verbose |
191 messages will be shown to indicate the current status.") | 284 messages will be shown to indicate the current status." |
192 | 285 :group 'gnus-mail |
193 (defvar nnmail-split-fancy "mail.misc" | 286 :type 'integer) |
194 "*Incoming mail can be split according to this fancy variable. | 287 |
288 (defcustom nnmail-split-fancy "mail.misc" | |
289 "Incoming mail can be split according to this fancy variable. | |
195 To enable this, set `nnmail-split-methods' to `nnmail-split-fancy'. | 290 To enable this, set `nnmail-split-methods' to `nnmail-split-fancy'. |
196 | 291 |
197 The format is this variable is SPLIT, where SPLIT can be one of | 292 The format is this variable is SPLIT, where SPLIT can be one of |
198 the following: | 293 the following: |
199 | 294 |
200 GROUP: Mail will be stored in GROUP (a string). | 295 GROUP: Mail will be stored in GROUP (a string). |
201 | 296 |
202 \(FIELD VALUE SPLIT): If the message field FIELD (a regexp) contains | 297 \(FIELD VALUE SPLIT): If the message field FIELD (a regexp) contains |
203 VALUE (a regexp), store the messages as specified by SPLIT. | 298 VALUE (a regexp), store the messages as specified by SPLIT. |
204 | 299 |
205 \(| SPLIT...): Process each SPLIT expression until one of them matches. | 300 \(| SPLIT...): Process each SPLIT expression until one of them matches. |
206 A SPLIT expression is said to match if it will cause the mail | 301 A SPLIT expression is said to match if it will cause the mail |
207 message to be stored in one or more groups. | 302 message to be stored in one or more groups. |
208 | 303 |
209 \(& SPLIT...): Process each SPLIT expression. | 304 \(& SPLIT...): Process each SPLIT expression. |
210 | 305 |
306 \(: FUNCTION optional args): Call FUNCTION with the optional args, in | |
307 the buffer containing the message headers. The return value FUNCTION | |
308 should be a split, which is then recursively processed. | |
309 | |
211 FIELD must match a complete field name. VALUE must match a complete | 310 FIELD must match a complete field name. VALUE must match a complete |
212 word according to the `nnmail-split-fancy-syntax-table' syntax table. | 311 word according to the `nnmail-split-fancy-syntax-table' syntax table. |
213 You can use .* in the regexps to match partial field names or words. | 312 You can use .* in the regexps to match partial field names or words. |
214 | 313 |
215 FIELD and VALUE can also be lisp symbols, in that case they are expanded | 314 FIELD and VALUE can also be lisp symbols, in that case they are expanded |
216 as specified in `nnmail-split-abbrev-alist'. | 315 as specified in `nnmail-split-abbrev-alist'. |
217 | 316 |
317 GROUP can contain \\& and \\N which will substitute from matching | |
318 \\(\\) patterns in the previous VALUE. | |
319 | |
218 Example: | 320 Example: |
219 | 321 |
220 \(setq nnmail-split-methods 'nnmail-split-fancy | 322 \(setq nnmail-split-methods 'nnmail-split-fancy |
221 nnmail-split-fancy | 323 nnmail-split-fancy |
222 ;; Messages from the mailer deamon are not crossposted to any of | 324 ;; Messages from the mailer daemon are not crossposted to any of |
223 ;; the ordinary groups. Warnings are put in a separate group | 325 ;; the ordinary groups. Warnings are put in a separate group |
224 ;; from real errors. | 326 ;; from real errors. |
225 '(| (\"from\" mail (| (\"subject\" \"warn.*\" \"mail.warning\") | 327 '(| (\"from\" mail (| (\"subject\" \"warn.*\" \"mail.warning\") |
226 \"mail.misc\")) | 328 \"mail.misc\")) |
227 ;; Non-error messages are crossposted to all relevant | 329 ;; Non-error messages are crossposted to all relevant |
233 (any \"procmail@informatik\\\\.rwth-aachen\\\\.de\" \"procmail.list\") | 335 (any \"procmail@informatik\\\\.rwth-aachen\\\\.de\" \"procmail.list\") |
234 (any \"SmartList@informatik\\\\.rwth-aachen\\\\.de\" \"SmartList.list\") | 336 (any \"SmartList@informatik\\\\.rwth-aachen\\\\.de\" \"SmartList.list\") |
235 ;; People... | 337 ;; People... |
236 (any \"larsi@ifi\\\\.uio\\\\.no\" \"people.Lars Magne Ingebrigtsen\")) | 338 (any \"larsi@ifi\\\\.uio\\\\.no\" \"people.Lars Magne Ingebrigtsen\")) |
237 ;; Unmatched mail goes to the catch all group. | 339 ;; Unmatched mail goes to the catch all group. |
238 \"misc.misc\"))") | 340 \"misc.misc\"))" |
239 | 341 :group 'gnus-mail |
240 (defvar nnmail-split-abbrev-alist | 342 ;; Sigh! |
343 :type 'sexp) | |
344 | |
345 (defcustom nnmail-split-abbrev-alist | |
241 '((any . "from\\|to\\|cc\\|sender\\|apparently-to\\|resent-from\\|resent-to\\|resent-cc") | 346 '((any . "from\\|to\\|cc\\|sender\\|apparently-to\\|resent-from\\|resent-to\\|resent-cc") |
242 (mail . "mailer-daemon\\|postmaster")) | 347 (mail . "mailer-daemon\\|postmaster\\|uucp")) |
243 "*Alist of abbreviations allowed in `nnmail-split-fancy'.") | 348 "Alist of abbreviations allowed in `nnmail-split-fancy'." |
244 | 349 :group 'gnus-mail |
245 (defvar nnmail-delete-incoming t | 350 :type '(repeat (cons :format "%v" symbol regexp))) |
246 "*If non-nil, the mail backends will delete incoming files after splitting.") | 351 |
247 | 352 (defcustom nnmail-delete-incoming t |
248 (defvar nnmail-message-id-cache-length 1000 | 353 "*If non-nil, the mail backends will delete incoming files after |
354 splitting." | |
355 :group 'gnus-mail | |
356 :type 'boolean) | |
357 | |
358 (defcustom nnmail-message-id-cache-length 1000 | |
249 "*The approximate number of Message-IDs nnmail will keep in its cache. | 359 "*The approximate number of Message-IDs nnmail will keep in its cache. |
250 If this variable is nil, no checking on duplicate messages will be | 360 If this variable is nil, no checking on duplicate messages will be |
251 performed.") | 361 performed." |
252 | 362 :group 'gnus-mail |
253 (defvar nnmail-message-id-cache-file "~/.nnmail-cache" | 363 :type '(choice (const :tag "disable" nil) |
254 "*The file name of the nnmail Message-ID cache.") | 364 (integer :format "%v"))) |
255 | 365 |
256 (defvar nnmail-treat-duplicates 'warn | 366 (defcustom nnmail-message-id-cache-file "~/.nnmail-cache" |
367 "*The file name of the nnmail Message-ID cache." | |
368 :group 'gnus-mail | |
369 :type 'file) | |
370 | |
371 (defcustom nnmail-treat-duplicates 'warn | |
257 "*If non-nil, nnmail keep a cache of Message-IDs to discover mail duplicates. | 372 "*If non-nil, nnmail keep a cache of Message-IDs to discover mail duplicates. |
258 Three values are legal: nil, which means that nnmail is not to keep a | 373 Three values are legal: nil, which means that nnmail is not to keep a |
259 Message-ID cache; `warn', which means that nnmail should insert extra | 374 Message-ID cache; `warn', which means that nnmail should insert extra |
260 headers to warn the user about the duplication (this is the default); | 375 headers to warn the user about the duplication (this is the default); |
261 and `delete', which means that nnmail will delete duplicated mails. | 376 and `delete', which means that nnmail will delete duplicated mails. |
262 | 377 |
263 This variable can also be a function. It will be called from a buffer | 378 This variable can also be a function. It will be called from a buffer |
264 narrowed to the article in question with the Message-ID as a | 379 narrowed to the article in question with the Message-ID as a |
265 parameter. It should return nil, `warn' or `delete'.") | 380 parameter. It should return nil, `warn' or `delete'." |
381 :group 'gnus-mail | |
382 :type '(choice (const :tag "off" nil) | |
383 (const warn) | |
384 (const delete))) | |
266 | 385 |
267 ;;; Internal variables. | 386 ;;; Internal variables. |
387 | |
388 (defvar nnmail-split-history nil | |
389 "List of group/article elements that say where the previous split put messages.") | |
268 | 390 |
269 (defvar nnmail-pop-password nil | 391 (defvar nnmail-pop-password nil |
270 "*Password to use when reading mail from a POP server, if required.") | 392 "*Password to use when reading mail from a POP server, if required.") |
271 | 393 |
272 (defvar nnmail-split-fancy-syntax-table nil | 394 (defvar nnmail-split-fancy-syntax-table nil |
274 (unless (syntax-table-p nnmail-split-fancy-syntax-table) | 396 (unless (syntax-table-p nnmail-split-fancy-syntax-table) |
275 (setq nnmail-split-fancy-syntax-table | 397 (setq nnmail-split-fancy-syntax-table |
276 (copy-syntax-table (standard-syntax-table))) | 398 (copy-syntax-table (standard-syntax-table))) |
277 ;; support the %-hack | 399 ;; support the %-hack |
278 (modify-syntax-entry ?\% "." nnmail-split-fancy-syntax-table)) | 400 (modify-syntax-entry ?\% "." nnmail-split-fancy-syntax-table)) |
279 | |
280 | 401 |
281 (defvar nnmail-prepare-save-mail-hook nil | 402 (defvar nnmail-prepare-save-mail-hook nil |
282 "Hook called before saving mail.") | 403 "Hook called before saving mail.") |
283 | 404 |
284 (defvar nnmail-moved-inboxes nil | 405 (defvar nnmail-moved-inboxes nil |
315 (file-directory-p (concat dir group))) | 436 (file-directory-p (concat dir group))) |
316 (concat dir group "/") | 437 (concat dir group "/") |
317 ;; If not, we translate dots into slashes. | 438 ;; If not, we translate dots into slashes. |
318 (concat dir (nnheader-replace-chars-in-string group ?. ?/) "/"))) | 439 (concat dir (nnheader-replace-chars-in-string group ?. ?/) "/"))) |
319 (or file ""))) | 440 (or file ""))) |
320 | 441 |
321 (defun nnmail-date-to-time (date) | 442 (defun nnmail-date-to-time (date) |
322 "Convert DATE into time." | 443 "Convert DATE into time." |
323 (let* ((d1 (timezone-parse-date date)) | 444 (condition-case () |
324 (t1 (timezone-parse-time (aref d1 3)))) | 445 (let* ((d1 (timezone-parse-date date)) |
325 (apply 'encode-time | 446 (t1 (timezone-parse-time (aref d1 3)))) |
326 (mapcar (lambda (el) | 447 (apply 'encode-time |
327 (and el (string-to-number el))) | 448 (mapcar (lambda (el) |
328 (list | 449 (and el (string-to-number el))) |
329 (aref t1 2) (aref t1 1) (aref t1 0) | 450 (list |
330 (aref d1 2) (aref d1 1) (aref d1 0) | 451 (aref t1 2) (aref t1 1) (aref t1 0) |
331 (aref d1 4)))))) | 452 (aref d1 2) (aref d1 1) (aref d1 0) |
453 (number-to-string | |
454 (* 60 (timezone-zone-to-minute (aref d1 4)))))))) | |
455 ;; If we get an error, then we just return a 0 time. | |
456 (error (list 0 0)))) | |
332 | 457 |
333 (defun nnmail-time-less (t1 t2) | 458 (defun nnmail-time-less (t1 t2) |
334 "Say whether time T1 is less than time T2." | 459 "Say whether time T1 is less than time T2." |
335 (or (< (car t1) (car t2)) | 460 (or (< (car t1) (car t2)) |
336 (and (= (car t1) (car t2)) | 461 (and (= (car t1) (car t2)) |
338 | 463 |
339 (defun nnmail-days-to-time (days) | 464 (defun nnmail-days-to-time (days) |
340 "Convert DAYS into time." | 465 "Convert DAYS into time." |
341 (let* ((seconds (* 1.0 days 60 60 24)) | 466 (let* ((seconds (* 1.0 days 60 60 24)) |
342 (rest (expt 2 16)) | 467 (rest (expt 2 16)) |
343 (ms (condition-case nil (round (/ seconds rest)) | 468 (ms (condition-case nil (round (/ seconds rest)) |
344 (range-error (expt 2 16))))) | 469 (range-error (expt 2 16))))) |
345 (list ms (condition-case nil (round (- seconds (* ms rest))) | 470 (list ms (condition-case nil (round (- seconds (* ms rest))) |
346 (range-error (expt 2 16)))))) | 471 (range-error (expt 2 16)))))) |
347 | 472 |
348 (defun nnmail-time-since (time) | 473 (defun nnmail-time-since (time) |
349 "Return the time since TIME, which is either an internal time or a date." | 474 "Return the time since TIME, which is either an internal time or a date." |
350 (when (stringp time) | 475 (when (stringp time) |
351 ;; Convert date strings to internal time. | 476 ;; Convert date strings to internal time. |
352 (setq time (nnmail-date-to-time time))) | 477 (setq time (nnmail-date-to-time time))) |
353 (let* ((current (current-time)) | 478 (let* ((current (current-time)) |
354 (rest (if (< (nth 1 current) (nth 1 time)) (expt 2 16)))) | 479 (rest (when (< (nth 1 current) (nth 1 time)) |
480 (expt 2 16)))) | |
355 (list (- (+ (car current) (if rest -1 0)) (car time)) | 481 (list (- (+ (car current) (if rest -1 0)) (car time)) |
356 (- (+ (or rest 0) (nth 1 current)) (nth 1 time))))) | 482 (- (+ (or rest 0) (nth 1 current)) (nth 1 time))))) |
357 | 483 |
358 ;; Function rewritten from rmail.el. | 484 ;; Function rewritten from rmail.el. |
359 (defun nnmail-move-inbox (inbox) | 485 (defun nnmail-move-inbox (inbox) |
360 "Move INBOX to `nnmail-crash-box'." | 486 "Move INBOX to `nnmail-crash-box'." |
361 (let ((inbox (file-truename (expand-file-name inbox))) | 487 (if (not (file-writable-p nnmail-crash-box)) |
362 (tofile (file-truename (expand-file-name nnmail-crash-box))) | 488 (gnus-error 1 "Can't write to crash box %s. Not moving mail." |
363 movemail popmail errors password) | 489 nnmail-crash-box) |
364 ;; If getting from mail spool directory, | 490 ;; If the crash box exists and is empty, we delete it. |
365 ;; use movemail to move rather than just renaming, | 491 (when (and (file-exists-p nnmail-crash-box) |
366 ;; so as to interlock with the mailer. | 492 (zerop (nnheader-file-size (file-truename nnmail-crash-box)))) |
367 (unless (setq popmail (string-match "^po:" (file-name-nondirectory inbox))) | 493 (delete-file nnmail-crash-box)) |
368 (setq movemail t)) | 494 (let ((inbox (file-truename (expand-file-name inbox))) |
369 (when popmail | 495 (tofile (file-truename (expand-file-name nnmail-crash-box))) |
370 (setq inbox (file-name-nondirectory inbox))) | 496 movemail popmail errors) |
371 (when (and movemail | 497 (if (setq popmail (string-match |
372 ;; On some systems, /usr/spool/mail/foo is a directory | 498 "^po:" (file-name-nondirectory inbox))) |
373 ;; and the actual inbox is /usr/spool/mail/foo/foo. | 499 (setq inbox (file-name-nondirectory inbox)) |
374 (file-directory-p inbox)) | 500 (setq movemail t) |
375 (setq inbox (expand-file-name (user-login-name) inbox))) | 501 ;; On some systems, /usr/spool/mail/foo is a directory |
376 (if (member inbox nnmail-moved-inboxes) | 502 ;; and the actual inbox is /usr/spool/mail/foo/foo. |
377 nil | 503 (when (file-directory-p inbox) |
378 (if popmail | 504 (setq inbox (expand-file-name (user-login-name) inbox)))) |
379 (progn | 505 (if (member inbox nnmail-moved-inboxes) |
380 (setq nnmail-internal-password nnmail-pop-password) | 506 ;; We don't try to move an already moved inbox. |
381 (when (and nnmail-pop-password-required (not nnmail-pop-password)) | 507 nil |
382 (setq nnmail-internal-password | 508 (if popmail |
383 (nnmail-read-passwd | 509 (progn |
384 (format "Password for %s: " | 510 (when (and nnmail-pop-password |
385 (substring inbox (+ popmail 3)))))) | 511 (not nnmail-internal-password)) |
386 (message "Getting mail from post office ...")) | 512 (setq nnmail-internal-password nnmail-pop-password)) |
387 (when (or (and (file-exists-p tofile) | 513 (when (and nnmail-pop-password-required |
388 (/= 0 (nnheader-file-size tofile))) | 514 (not nnmail-internal-password)) |
389 (and (file-exists-p inbox) | 515 (setq nnmail-internal-password |
390 (/= 0 (nnheader-file-size inbox)))) | 516 (nnmail-read-passwd |
391 (message "Getting mail from %s..." inbox))) | 517 (format "Password for %s: " |
392 ;; Set TOFILE if have not already done so, and | 518 (substring inbox (+ popmail 3)))))) |
393 ;; rename or copy the file INBOX to TOFILE if and as appropriate. | 519 (message "Getting mail from post office ...")) |
394 (cond | 520 (when (or (and (file-exists-p tofile) |
395 ((file-exists-p tofile) | 521 (/= 0 (nnheader-file-size tofile))) |
396 ;; The crash box exists already. | 522 (and (file-exists-p inbox) |
397 t) | 523 (/= 0 (nnheader-file-size inbox)))) |
398 ((and (not popmail) | 524 (message "Getting mail from %s..." inbox))) |
399 (not (file-exists-p inbox))) | 525 ;; Set TOFILE if have not already done so, and |
400 ;; There is no inbox. | 526 ;; rename or copy the file INBOX to TOFILE if and as appropriate. |
401 (setq tofile nil)) | 527 (cond |
402 ((and (not movemail) (not popmail)) | 528 ((file-exists-p tofile) |
403 ;; Try copying. If that fails (perhaps no space), | 529 ;; The crash box exists already. |
404 ;; rename instead. | 530 t) |
405 (condition-case nil | 531 ((and (not popmail) |
406 (copy-file inbox tofile nil) | 532 (not (file-exists-p inbox))) |
407 (error | 533 ;; There is no inbox. |
408 ;; Third arg is t so we can replace existing file TOFILE. | 534 (setq tofile nil)) |
409 (rename-file inbox tofile t))) | 535 (t |
410 (push inbox nnmail-moved-inboxes) | 536 ;; If getting from mail spool directory, use movemail to move |
411 ;; Make the real inbox file empty. | 537 ;; rather than just renaming, so as to interlock with the |
412 ;; Leaving it deleted could cause lossage | 538 ;; mailer. |
413 ;; because mailers often won't create the file. | 539 (unwind-protect |
414 (condition-case () | 540 (save-excursion |
415 (write-region (point) (point) inbox) | 541 (setq errors (generate-new-buffer " *nnmail loss*")) |
416 (file-error nil))) | 542 (buffer-disable-undo errors) |
417 (t | 543 (let ((default-directory "/")) |
418 ;; Use movemail. | 544 (if (nnheader-functionp nnmail-movemail-program) |
419 (unwind-protect | 545 (funcall nnmail-movemail-program inbox tofile) |
420 (save-excursion | 546 (apply |
421 (setq errors (generate-new-buffer " *nnmail loss*")) | 547 'call-process |
422 (buffer-disable-undo errors) | 548 (append |
423 (let ((default-directory "/")) | 549 (list |
424 (apply | 550 (expand-file-name |
425 'call-process | 551 nnmail-movemail-program exec-directory) |
426 (append | 552 nil errors nil inbox tofile) |
427 (list | 553 (when nnmail-internal-password |
428 (expand-file-name nnmail-movemail-program exec-directory) | 554 (list nnmail-internal-password)))))) |
429 nil errors nil inbox tofile) | 555 (if (not (buffer-modified-p errors)) |
430 (when nnmail-internal-password | 556 ;; No output => movemail won |
431 (list nnmail-internal-password))))) | 557 (progn |
432 (if (not (buffer-modified-p errors)) | 558 (unless popmail |
433 ;; No output => movemail won | 559 (when (file-exists-p tofile) |
434 (push inbox nnmail-moved-inboxes) | 560 (set-file-modes tofile nnmail-default-file-modes))) |
435 (set-buffer errors) | 561 (push inbox nnmail-moved-inboxes)) |
436 (subst-char-in-region (point-min) (point-max) ?\n ?\ ) | 562 (set-buffer errors) |
437 (goto-char (point-max)) | 563 ;; There may be a warning about older revisions. We |
438 (skip-chars-backward " \t") | 564 ;; ignore those. |
439 (delete-region (point) (point-max)) | 565 (goto-char (point-min)) |
440 (goto-char (point-min)) | 566 (if (search-forward "older revision" nil t) |
441 (if (looking-at "movemail: ") | 567 (progn |
442 (delete-region (point-min) (match-end 0))) | 568 (unless popmail |
443 (error (concat "movemail: " (buffer-string))) | 569 (when (file-exists-p tofile) |
444 (setq tofile nil)))))) | 570 (set-file-modes tofile nnmail-default-file-modes))) |
445 (and errors | 571 (push inbox nnmail-moved-inboxes)) |
446 (buffer-name errors) | 572 ;; Probably a real error. |
447 (kill-buffer errors)) | 573 (subst-char-in-region (point-min) (point-max) ?\n ?\ ) |
448 tofile))) | 574 (goto-char (point-max)) |
575 (skip-chars-backward " \t") | |
576 (delete-region (point) (point-max)) | |
577 (goto-char (point-min)) | |
578 (when (looking-at "movemail: ") | |
579 (delete-region (point-min) (match-end 0))) | |
580 (unless (yes-or-no-p | |
581 (format "movemail: %s. Continue? " | |
582 (buffer-string))) | |
583 (error "%s" (buffer-string))) | |
584 (setq tofile nil))))))) | |
585 (message "Getting mail from %s...done" inbox) | |
586 (and errors | |
587 (buffer-name errors) | |
588 (kill-buffer errors)) | |
589 tofile)))) | |
449 | 590 |
450 (defun nnmail-get-active () | 591 (defun nnmail-get-active () |
451 "Returns an assoc of group names and active ranges. | 592 "Returns an assoc of group names and active ranges. |
452 nn*-request-list should have been called before calling this function." | 593 nn*-request-list should have been called before calling this function." |
453 (let (group-assoc) | 594 (let (group-assoc) |
465 group-assoc)) | 606 group-assoc)) |
466 | 607 |
467 (defun nnmail-save-active (group-assoc file-name) | 608 (defun nnmail-save-active (group-assoc file-name) |
468 "Save GROUP-ASSOC in ACTIVE-FILE." | 609 "Save GROUP-ASSOC in ACTIVE-FILE." |
469 (when file-name | 610 (when file-name |
470 (let (group) | 611 (nnheader-temp-write file-name |
471 (save-excursion | 612 (nnmail-generate-active group-assoc)))) |
472 (set-buffer (get-buffer-create " *nnmail active*")) | 613 |
473 (buffer-disable-undo (current-buffer)) | 614 (defun nnmail-generate-active (alist) |
474 (erase-buffer) | 615 "Generate an active file from group-alist ALIST." |
475 (while group-assoc | 616 (erase-buffer) |
476 (setq group (pop group-assoc)) | 617 (let (group) |
477 (insert (format "%s %d %d y\n" (car group) (cdadr group) | 618 (while (setq group (pop alist)) |
478 (caadr group)))) | 619 (insert (format "%s %d %d y\n" (car group) (cdadr group) |
479 (unless (file-exists-p (file-name-directory file-name)) | 620 (caadr group)))))) |
480 (make-directory (file-name-directory file-name) t)) | |
481 (write-region 1 (point-max) (expand-file-name file-name) nil 'nomesg) | |
482 (kill-buffer (current-buffer)))))) | |
483 | 621 |
484 (defun nnmail-get-split-group (file group) | 622 (defun nnmail-get-split-group (file group) |
623 "Find out whether this FILE is to be split into GROUP only. | |
624 If GROUP is non-nil and we are using procmail, return the group name | |
625 only when the file is the correct procmail file. When GROUP is nil, | |
626 return nil if FILE is a spool file or the procmail group for which it | |
627 is a spool. If not using procmail, return GROUP." | |
485 (if (or (eq nnmail-spool-file 'procmail) | 628 (if (or (eq nnmail-spool-file 'procmail) |
486 nnmail-use-procmail) | 629 nnmail-use-procmail) |
487 (cond (group group) | 630 (if (string-match (concat "^" (expand-file-name |
488 ((string-match (concat "^" (expand-file-name | 631 (file-name-as-directory |
489 (file-name-as-directory | 632 nnmail-procmail-directory)) |
490 nnmail-procmail-directory)) | 633 "\\([^/]*\\)" nnmail-procmail-suffix "$") |
491 "\\([^/]*\\)" nnmail-procmail-suffix "$") | 634 (expand-file-name file)) |
492 (expand-file-name file)) | 635 (let ((procmail-group (substring (expand-file-name file) |
493 (substring (expand-file-name file) | 636 (match-beginning 1) |
494 (match-beginning 1) (match-end 1))) | 637 (match-end 1)))) |
495 (t | 638 (if group |
496 group)) | 639 (if (string-equal group procmail-group) |
640 group | |
641 nil) | |
642 procmail-group)) | |
643 nil) | |
497 group)) | 644 group)) |
498 | 645 |
499 (defun nnmail-process-babyl-mail-format (func) | 646 (defun nnmail-process-babyl-mail-format (func artnum-func) |
500 (let ((case-fold-search t) | 647 (let ((case-fold-search t) |
501 start message-id content-length do-search end) | 648 start message-id content-length do-search end) |
649 (goto-char (point-min)) | |
502 (while (not (eobp)) | 650 (while (not (eobp)) |
503 (goto-char (point-min)) | |
504 (re-search-forward | 651 (re-search-forward |
505 "\n0, *unseen,+\n\\(\\*\\*\\* EOOH \\*\\*\\*\n\\)?" nil t) | 652 "\n0, *unseen,+\n\\(\\*\\*\\* EOOH \\*\\*\\*\n\\)?" nil t) |
506 (goto-char (match-end 0)) | 653 (goto-char (match-end 0)) |
507 (delete-region (match-beginning 0) (match-end 0)) | 654 (delete-region (match-beginning 0) (match-end 0)) |
508 (setq start (point)) | 655 (narrow-to-region |
509 ;; Skip all the headers in case there are more "From "s... | 656 (setq start (point)) |
510 (or (search-forward "\n\n" nil t) | 657 (progn |
511 (search-forward-regexp "^[^:]*\\( .*\\|\\)$" nil t) | 658 ;; Skip all the headers in case there are more "From "s... |
512 (search-forward "")) | 659 (or (search-forward "\n\n" nil t) |
660 (search-forward-regexp "^[^:]*\\( .*\\|\\)$" nil t) | |
661 (search-forward "")) | |
662 (point))) | |
663 ;; Unquote the ">From " line, if any. | |
664 (goto-char (point-min)) | |
665 (when (looking-at ">From ") | |
666 (replace-match "X-From-Line: ") ) | |
667 (run-hooks 'nnmail-prepare-incoming-header-hook) | |
668 (goto-char (point-max)) | |
513 ;; Find the Message-ID header. | 669 ;; Find the Message-ID header. |
514 (save-excursion | 670 (save-excursion |
515 (if (re-search-backward "^Message-ID:[ \t]*\\(<[^>]*>\\)" nil t) | 671 (if (re-search-backward "^Message-ID:[ \t]*\\(<[^>]*>\\)" nil t) |
516 (setq message-id (buffer-substring (match-beginning 1) | 672 (setq message-id (buffer-substring (match-beginning 1) |
517 (match-end 1))) | 673 (match-end 1))) |
535 ;; the backends ever use it, and we do not | 691 ;; the backends ever use it, and we do not |
536 ;; want to confuse other mailers by having | 692 ;; want to confuse other mailers by having |
537 ;; a (possibly) faulty header. | 693 ;; a (possibly) faulty header. |
538 (progn (insert "X-") t)))) | 694 (progn (insert "X-") t)))) |
539 (setq do-search t) | 695 (setq do-search t) |
696 (widen) | |
540 (if (or (= (+ (point) content-length) (point-max)) | 697 (if (or (= (+ (point) content-length) (point-max)) |
541 (save-excursion | 698 (save-excursion |
542 (goto-char (+ (point) content-length)) | 699 (goto-char (+ (point) content-length)) |
543 (looking-at ""))) | 700 (looking-at ""))) |
544 (progn | 701 (progn |
545 (goto-char (+ (point) content-length)) | 702 (goto-char (+ (point) content-length)) |
546 (setq do-search nil)) | 703 (setq do-search nil)) |
547 (setq do-search t))) | 704 (setq do-search t))) |
705 (widen) | |
548 ;; Go to the beginning of the next article - or to the end | 706 ;; Go to the beginning of the next article - or to the end |
549 ;; of the buffer. | 707 ;; of the buffer. |
550 (if do-search | 708 (when do-search |
551 (if (re-search-forward "^" nil t) | 709 (if (re-search-forward "^" nil t) |
552 (goto-char (match-beginning 0)) | 710 (goto-char (match-beginning 0)) |
553 (goto-char (1- (point-max))))) | 711 (goto-char (1- (point-max))))) |
554 (delete-char 1) ; delete ^_ | 712 (delete-char 1) ; delete ^_ |
555 (save-excursion | 713 (save-excursion |
556 (save-restriction | 714 (save-restriction |
557 (narrow-to-region start (point)) | 715 (narrow-to-region start (point)) |
558 (goto-char (point-min)) | 716 (goto-char (point-min)) |
559 (nnmail-check-duplication message-id func) | 717 (nnmail-check-duplication message-id func artnum-func) |
560 (setq end (point-max)))) | 718 (setq end (point-max)))) |
561 (goto-char end)))) | 719 (goto-char end)))) |
562 | 720 |
563 (defun nnmail-search-unix-mail-delim () | 721 (defun nnmail-search-unix-mail-delim () |
564 "Put point at the beginning of the next message." | 722 "Put point at the beginning of the next Unix mbox message." |
565 (let ((case-fold-search t) | 723 ;; Algorithm used to find the the next article in the |
566 (delim (concat "^" message-unix-mail-delimiter)) | 724 ;; brain-dead Unix mbox format: |
725 ;; | |
726 ;; 1) Search for "^From ". | |
727 ;; 2) If we find it, then see whether the previous | |
728 ;; line is blank and the next line looks like a header. | |
729 ;; Then it's possible that this is a mail delim, and we use it. | |
730 (let ((case-fold-search nil) | |
567 found) | 731 found) |
568 (while (not found) | 732 (while (not found) |
569 (if (re-search-forward delim nil t) | 733 (if (not (re-search-forward "^From " nil t)) |
570 (when (or (looking-at "[^\n :]+ *:") | 734 (setq found 'no) |
571 (looking-at delim) | 735 (save-excursion |
572 (looking-at (concat ">" message-unix-mail-delimiter))) | 736 (beginning-of-line) |
573 (forward-line -1) | 737 (when (and (or (bobp) |
574 (setq found 'yes)) | 738 (save-excursion |
575 (setq found 'no))) | 739 (forward-line -1) |
740 (= (following-char) ?\n))) | |
741 (save-excursion | |
742 (forward-line 1) | |
743 (while (looking-at ">From ") | |
744 (forward-line 1)) | |
745 (looking-at "[^ \t:]+[ \t]*:"))) | |
746 (setq found 'yes))))) | |
747 (beginning-of-line) | |
576 (eq found 'yes))) | 748 (eq found 'yes))) |
577 | 749 |
578 (defun nnmail-process-unix-mail-format (func) | 750 (defun nnmail-search-unix-mail-delim-backward () |
751 "Put point at the beginning of the current Unix mbox message." | |
752 ;; Algorithm used to find the the next article in the | |
753 ;; brain-dead Unix mbox format: | |
754 ;; | |
755 ;; 1) Search for "^From ". | |
756 ;; 2) If we find it, then see whether the previous | |
757 ;; line is blank and the next line looks like a header. | |
758 ;; Then it's possible that this is a mail delim, and we use it. | |
759 (let ((case-fold-search nil) | |
760 found) | |
761 (while (not found) | |
762 (if (not (re-search-backward "^From " nil t)) | |
763 (setq found 'no) | |
764 (save-excursion | |
765 (beginning-of-line) | |
766 (when (and (or (bobp) | |
767 (save-excursion | |
768 (forward-line -1) | |
769 (= (following-char) ?\n))) | |
770 (save-excursion | |
771 (forward-line 1) | |
772 (while (looking-at ">From ") | |
773 (forward-line 1)) | |
774 (looking-at "[^ \t:]+[ \t]*:"))) | |
775 (setq found 'yes))))) | |
776 (beginning-of-line) | |
777 (eq found 'yes))) | |
778 | |
779 (defun nnmail-process-unix-mail-format (func artnum-func) | |
579 (let ((case-fold-search t) | 780 (let ((case-fold-search t) |
580 (delim (concat "^" message-unix-mail-delimiter)) | |
581 start message-id content-length end skip head-end) | 781 start message-id content-length end skip head-end) |
582 (goto-char (point-min)) | 782 (goto-char (point-min)) |
583 (if (not (and (re-search-forward delim nil t) | 783 (if (not (and (re-search-forward "^From " nil t) |
584 (goto-char (match-beginning 0)))) | 784 (goto-char (match-beginning 0)))) |
585 ;; Possibly wrong format? | 785 ;; Possibly wrong format? |
586 (error "Error, unknown mail format! (Possibly corrupted.)") | 786 (error "Error, unknown mail format! (Possibly corrupted.)") |
587 ;; Carry on until the bitter end. | 787 ;; Carry on until the bitter end. |
588 (while (not (eobp)) | 788 (while (not (eobp)) |
619 ;; We destroy the header, since none of the backends ever | 819 ;; We destroy the header, since none of the backends ever |
620 ;; use it, and we do not want to confuse other mailers by | 820 ;; use it, and we do not want to confuse other mailers by |
621 ;; having a (possibly) faulty header. | 821 ;; having a (possibly) faulty header. |
622 (beginning-of-line) | 822 (beginning-of-line) |
623 (insert "X-")) | 823 (insert "X-")) |
824 (run-hooks 'nnmail-prepare-incoming-header-hook) | |
624 ;; Find the end of this article. | 825 ;; Find the end of this article. |
625 (goto-char (point-max)) | 826 (goto-char (point-max)) |
626 (widen) | 827 (widen) |
627 (setq head-end (point)) | 828 (setq head-end (point)) |
628 ;; We try the Content-Length value. The idea: skip over the header | 829 ;; We try the Content-Length value. The idea: skip over the header |
636 (setq skip (+ (point) content-length)) | 837 (setq skip (+ (point) content-length)) |
637 (goto-char skip) | 838 (goto-char skip) |
638 (cond ((or (= skip (point-max)) | 839 (cond ((or (= skip (point-max)) |
639 (= (1+ skip) (point-max))) | 840 (= (1+ skip) (point-max))) |
640 (setq end (point-max))) | 841 (setq end (point-max))) |
641 ((looking-at delim) | 842 ((looking-at "From ") |
642 (setq end skip)) | 843 (setq end skip)) |
643 ((looking-at | 844 ((looking-at "[ \t]*\n\\(From \\)") |
644 (concat "[ \t]*\n\\(" delim "\\)")) | |
645 (setq end (match-beginning 1))) | 845 (setq end (match-beginning 1))) |
646 (t (setq end nil)))) | 846 (t (setq end nil)))) |
647 (if end | 847 (if end |
648 (goto-char end) | 848 (goto-char end) |
649 ;; No Content-Length, so we find the beginning of the next | 849 ;; No Content-Length, so we find the beginning of the next |
654 ;; Allow the backend to save the article. | 854 ;; Allow the backend to save the article. |
655 (save-excursion | 855 (save-excursion |
656 (save-restriction | 856 (save-restriction |
657 (narrow-to-region start (point)) | 857 (narrow-to-region start (point)) |
658 (goto-char (point-min)) | 858 (goto-char (point-min)) |
659 (nnmail-check-duplication message-id func) | 859 (nnmail-check-duplication message-id func artnum-func) |
660 (setq end (point-max)))) | 860 (setq end (point-max)))) |
661 (goto-char end))))) | 861 (goto-char end))))) |
662 | 862 |
663 (defun nnmail-process-mmdf-mail-format (func) | 863 (defun nnmail-process-mmdf-mail-format (func artnum-func) |
664 (let ((delim "^\^A\^A\^A\^A$") | 864 (let ((delim "^\^A\^A\^A\^A$") |
665 (case-fold-search t) | 865 (case-fold-search t) |
666 start message-id end) | 866 start message-id end) |
667 (goto-char (point-min)) | 867 (goto-char (point-min)) |
668 (if (not (and (re-search-forward delim nil t) | 868 (if (not (and (re-search-forward delim nil t) |
692 (when (re-search-backward "^Message-ID:" nil t) | 892 (when (re-search-backward "^Message-ID:" nil t) |
693 (beginning-of-line) | 893 (beginning-of-line) |
694 (insert "Original-"))) | 894 (insert "Original-"))) |
695 (forward-line 1) | 895 (forward-line 1) |
696 (insert "Message-ID: " (setq message-id (nnmail-message-id)) "\n")) | 896 (insert "Message-ID: " (setq message-id (nnmail-message-id)) "\n")) |
897 (run-hooks 'nnmail-prepare-incoming-header-hook) | |
697 ;; Find the end of this article. | 898 ;; Find the end of this article. |
698 (goto-char (point-max)) | 899 (goto-char (point-max)) |
699 (widen) | 900 (widen) |
700 (if (re-search-forward delim nil t) | 901 (if (re-search-forward delim nil t) |
701 (beginning-of-line) | 902 (beginning-of-line) |
703 ;; Allow the backend to save the article. | 904 ;; Allow the backend to save the article. |
704 (save-excursion | 905 (save-excursion |
705 (save-restriction | 906 (save-restriction |
706 (narrow-to-region start (point)) | 907 (narrow-to-region start (point)) |
707 (goto-char (point-min)) | 908 (goto-char (point-min)) |
708 (nnmail-check-duplication message-id func) | 909 (nnmail-check-duplication message-id func artnum-func) |
709 (setq end (point-max)))) | 910 (setq end (point-max)))) |
710 (goto-char end) | 911 (goto-char end) |
711 (forward-line 2))))) | 912 (forward-line 2))))) |
712 | 913 |
713 (defun nnmail-split-incoming (incoming func &optional exit-func group) | 914 (defun nnmail-split-incoming (incoming func &optional exit-func |
915 group artnum-func) | |
714 "Go through the entire INCOMING file and pick out each individual mail. | 916 "Go through the entire INCOMING file and pick out each individual mail. |
715 FUNC will be called with the buffer narrowed to each mail." | 917 FUNC will be called with the buffer narrowed to each mail." |
716 (let (;; If this is a group-specific split, we bind the split | 918 (let (;; If this is a group-specific split, we bind the split |
717 ;; methods to just this group. | 919 ;; methods to just this group. |
718 (nnmail-split-methods (if (and group | 920 (nnmail-split-methods (if (and group |
724 (save-excursion | 926 (save-excursion |
725 ;; Insert the incoming file. | 927 ;; Insert the incoming file. |
726 (set-buffer (get-buffer-create " *nnmail incoming*")) | 928 (set-buffer (get-buffer-create " *nnmail incoming*")) |
727 (buffer-disable-undo (current-buffer)) | 929 (buffer-disable-undo (current-buffer)) |
728 (erase-buffer) | 930 (erase-buffer) |
729 (nnheader-insert-file-contents-literally incoming) | 931 (nnheader-insert-file-contents incoming) |
730 (unless (zerop (buffer-size)) | 932 (unless (zerop (buffer-size)) |
731 (goto-char (point-min)) | 933 (goto-char (point-min)) |
732 (save-excursion (run-hooks 'nnmail-prepare-incoming-hook)) | 934 (save-excursion (run-hooks 'nnmail-prepare-incoming-hook)) |
733 ;; Handle both babyl, MMDF and unix mail formats, since movemail will | 935 ;; Handle both babyl, MMDF and unix mail formats, since movemail will |
734 ;; use the former when fetching from a mailbox, the latter when | 936 ;; use the former when fetching from a mailbox, the latter when |
735 ;; fetches from a file. | 937 ;; fetches from a file. |
736 (cond ((or (looking-at "\^L") | 938 (cond ((or (looking-at "\^L") |
737 (looking-at "BABYL OPTIONS:")) | 939 (looking-at "BABYL OPTIONS:")) |
738 (nnmail-process-babyl-mail-format func)) | 940 (nnmail-process-babyl-mail-format func artnum-func)) |
739 ((looking-at "\^A\^A\^A\^A") | 941 ((looking-at "\^A\^A\^A\^A") |
740 (nnmail-process-mmdf-mail-format func)) | 942 (nnmail-process-mmdf-mail-format func artnum-func)) |
741 (t | 943 (t |
742 (nnmail-process-unix-mail-format func)))) | 944 (nnmail-process-unix-mail-format func artnum-func)))) |
743 (if exit-func (funcall exit-func)) | 945 (when exit-func |
946 (funcall exit-func)) | |
744 (kill-buffer (current-buffer))))) | 947 (kill-buffer (current-buffer))))) |
745 | 948 |
746 ;; Mail crossposts suggested by Brian Edmonds <edmonds@cs.ubc.ca>. | 949 ;; Mail crossposts suggested by Brian Edmonds <edmonds@cs.ubc.ca>. |
747 (defun nnmail-article-group (func) | 950 (defun nnmail-article-group (func) |
748 "Look at the headers and return an alist of groups that match. | 951 "Look at the headers and return an alist of groups that match. |
767 (insert-buffer-substring obuf beg end) | 970 (insert-buffer-substring obuf beg end) |
768 ;; Fold continuation lines. | 971 ;; Fold continuation lines. |
769 (goto-char (point-min)) | 972 (goto-char (point-min)) |
770 (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t) | 973 (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t) |
771 (replace-match " " t t)) | 974 (replace-match " " t t)) |
975 ;; Allow washing. | |
976 (run-hooks 'nnmail-split-hook) | |
772 (if (and (symbolp nnmail-split-methods) | 977 (if (and (symbolp nnmail-split-methods) |
773 (fboundp nnmail-split-methods)) | 978 (fboundp nnmail-split-methods)) |
774 ;; `nnmail-split-methods' is a function, so we just call | 979 (let ((split |
775 ;; this function here and use the result. | |
776 (setq group-art | |
777 (mapcar | |
778 (lambda (group) (cons group (funcall func group))) | |
779 (condition-case nil | 980 (condition-case nil |
780 (or (funcall nnmail-split-methods) | 981 (or (funcall nnmail-split-methods) |
781 '("bogus")) | 982 '("bogus")) |
782 (error | 983 (error |
783 (message | 984 (message |
784 "Error in `nnmail-split-methods'; using `bogus' mail group") | 985 "Error in `nnmail-split-methods'; using `bogus' mail group") |
785 (sit-for 1) | 986 (sit-for 1) |
786 '("bogus"))))) | 987 '("bogus"))))) |
988 (unless (equal split '(junk)) | |
989 ;; `nnmail-split-methods' is a function, so we just call | |
990 ;; this function here and use the result. | |
991 (setq group-art | |
992 (mapcar | |
993 (lambda (group) (cons group (funcall func group))) | |
994 split)))) | |
787 ;; Go through the split methods to find a match. | 995 ;; Go through the split methods to find a match. |
788 (while (and methods (or nnmail-crosspost (not group-art))) | 996 (while (and methods (or nnmail-crosspost (not group-art))) |
789 (goto-char (point-max)) | 997 (goto-char (point-max)) |
790 (setq method (pop methods)) | 998 (setq method (pop methods)) |
791 (if (or methods | 999 (if (or methods |
792 (not (equal "" (nth 1 method)))) | 1000 (not (equal "" (nth 1 method)))) |
793 (when (and | 1001 (when (and |
794 (condition-case () | 1002 (ignore-errors |
795 (if (stringp (nth 1 method)) | 1003 (if (stringp (nth 1 method)) |
796 (re-search-backward (cadr method) nil t) | 1004 (re-search-backward (cadr method) nil t) |
797 ;; Function to say whether this is a match. | 1005 ;; Function to say whether this is a match. |
798 (funcall (nth 1 method) (car method))) | 1006 (funcall (nth 1 method) (car method)))) |
799 (error nil)) | |
800 ;; Don't enter the article into the same | 1007 ;; Don't enter the article into the same |
801 ;; group twice. | 1008 ;; group twice. |
802 (not (assoc (car method) group-art))) | 1009 (not (assoc (car method) group-art))) |
803 (push (cons (car method) (funcall func (car method))) | 1010 (push (cons (car method) (funcall func (car method))) |
804 group-art)) | 1011 group-art)) |
805 ;; This is the final group, which is used as a | 1012 ;; This is the final group, which is used as a |
806 ;; catch-all. | 1013 ;; catch-all. |
807 (unless group-art | 1014 (unless group-art |
808 (setq group-art | 1015 (setq group-art |
809 (list (cons (car method) | 1016 (list (cons (car method) |
810 (funcall func (car method))))))))) | 1017 (funcall func (car method))))))))) |
811 group-art)))) | 1018 ;; See whether the split methods returned `junk'. |
1019 (if (equal group-art '(junk)) | |
1020 nil | |
1021 (nreverse (delq 'junk group-art))))))) | |
812 | 1022 |
813 (defun nnmail-insert-lines () | 1023 (defun nnmail-insert-lines () |
814 "Insert how many lines there are in the body of the mail. | 1024 "Insert how many lines there are in the body of the mail. |
815 Return the number of characters in the body." | 1025 Return the number of characters in the body." |
816 (let (lines chars) | 1026 (let (lines chars) |
817 (save-excursion | 1027 (save-excursion |
818 (goto-char (point-min)) | 1028 (goto-char (point-min)) |
819 (when (search-forward "\n\n" nil t) | 1029 (when (search-forward "\n\n" nil t) |
820 (setq chars (- (point-max) (point))) | 1030 (setq chars (- (point-max) (point))) |
821 (setq lines (count-lines (point) (point-max))) | 1031 (setq lines (count-lines (point) (point-max))) |
822 (forward-char -1) | 1032 (forward-char -1) |
823 (save-excursion | 1033 (save-excursion |
824 (when (re-search-backward "^Lines: " nil t) | 1034 (when (re-search-backward "^Lines: " nil t) |
829 | 1039 |
830 (defun nnmail-insert-xref (group-alist) | 1040 (defun nnmail-insert-xref (group-alist) |
831 "Insert an Xref line based on the (group . article) alist." | 1041 "Insert an Xref line based on the (group . article) alist." |
832 (save-excursion | 1042 (save-excursion |
833 (goto-char (point-min)) | 1043 (goto-char (point-min)) |
834 (when (search-forward "\n\n" nil t) | 1044 (when (search-forward "\n\n" nil t) |
835 (forward-char -1) | 1045 (forward-char -1) |
836 (when (re-search-backward "^Xref: " nil t) | 1046 (when (re-search-backward "^Xref: " nil t) |
837 (delete-region (match-beginning 0) | 1047 (delete-region (match-beginning 0) |
838 (progn (forward-line 1) (point)))) | 1048 (progn (forward-line 1) (point)))) |
839 (insert (format "Xref: %s" (system-name))) | 1049 (insert (format "Xref: %s" (system-name))) |
840 (while group-alist | 1050 (while group-alist |
841 (insert (format " %s:%d" (caar group-alist) (cdar group-alist))) | 1051 (insert (format " %s:%d" (caar group-alist) (cdar group-alist))) |
842 (setq group-alist (cdr group-alist))) | 1052 (setq group-alist (cdr group-alist))) |
843 (insert "\n")))) | 1053 (insert "\n")))) |
1054 | |
1055 ;;; Message washing functions | |
1056 | |
1057 (defun nnmail-remove-leading-whitespace () | |
1058 "Remove excessive whitespace from all headers." | |
1059 (goto-char (point-min)) | |
1060 (while (re-search-forward "^\\([^ :]+: \\) +" nil t) | |
1061 (replace-match "\\1" t))) | |
1062 | |
1063 (defun nnmail-remove-list-identifiers () | |
1064 "Remove list identifiers from Subject headers." | |
1065 (let ((regexp (if (stringp nnmail-list-identifiers) nnmail-list-identifiers | |
1066 (mapconcat 'identity nnmail-list-identifiers "\\|")))) | |
1067 (when regexp | |
1068 (goto-char (point-min)) | |
1069 (when (re-search-forward | |
1070 (concat "^Subject: +\\(Re: +\\)?\\(" regexp "\\) *") | |
1071 nil t) | |
1072 (delete-region (match-beginning 2) (match-end 0)))))) | |
1073 | |
1074 (defun nnmail-remove-tabs () | |
1075 "Translate TAB characters into SPACE characters." | |
1076 (subst-char-in-region (point-min) (point-max) ?\t ? t)) | |
1077 | |
1078 ;;; Utility functions | |
844 | 1079 |
845 ;; Written by byer@mv.us.adobe.com (Scott Byer). | 1080 ;; Written by byer@mv.us.adobe.com (Scott Byer). |
846 (defun nnmail-make-complex-temp-name (prefix) | 1081 (defun nnmail-make-complex-temp-name (prefix) |
847 (let ((newname (make-temp-name prefix)) | 1082 (let ((newname (make-temp-name prefix)) |
848 (newprefix prefix)) | 1083 (newprefix prefix)) |
866 (defvar nnmail-split-cache nil) | 1101 (defvar nnmail-split-cache nil) |
867 ;; Alist of split expressions their equivalent regexps. | 1102 ;; Alist of split expressions their equivalent regexps. |
868 | 1103 |
869 (defun nnmail-split-it (split) | 1104 (defun nnmail-split-it (split) |
870 ;; Return a list of groups matching SPLIT. | 1105 ;; Return a list of groups matching SPLIT. |
871 (cond ((stringp split) | 1106 (cond |
872 ;; A group. | 1107 ;; nil split |
873 (list split)) | 1108 ((null split) |
874 ((eq (car split) '&) | 1109 nil) |
875 (apply 'nconc (mapcar 'nnmail-split-it (cdr split)))) | 1110 |
876 ((eq (car split) '|) | 1111 ;; A group name. Do the \& and \N subs into the string. |
877 (let (done) | 1112 ((stringp split) |
878 (while (and (not done) (cdr split)) | 1113 (list (nnmail-expand-newtext split))) |
879 (setq split (cdr split) | 1114 |
880 done (nnmail-split-it (car split)))) | 1115 ;; Junk the message. |
881 done)) | 1116 ((eq split 'junk) |
882 ((assq split nnmail-split-cache) | 1117 (list 'junk)) |
883 ;; A compiled match expression. | 1118 |
884 (goto-char (point-max)) | 1119 ;; Builtin & operation. |
885 (if (re-search-backward (cdr (assq split nnmail-split-cache)) nil t) | 1120 ((eq (car split) '&) |
886 (nnmail-split-it (nth 2 split)))) | 1121 (apply 'nconc (mapcar 'nnmail-split-it (cdr split)))) |
887 (t | 1122 |
888 ;; An uncompiled match. | 1123 ;; Builtin | operation. |
889 (let* ((field (nth 0 split)) | 1124 ((eq (car split) '|) |
890 (value (nth 1 split)) | 1125 (let (done) |
891 (regexp (concat "^\\(" | 1126 (while (and (not done) (cdr split)) |
892 (if (symbolp field) | 1127 (setq split (cdr split) |
893 (cdr (assq field | 1128 done (nnmail-split-it (car split)))) |
894 nnmail-split-abbrev-alist)) | 1129 done)) |
895 field) | 1130 |
896 "\\):.*\\<\\(" | 1131 ;; Builtin : operation. |
897 (if (symbolp value) | 1132 ((eq (car split) ':) |
898 (cdr (assq value | 1133 (nnmail-split-it (eval (cdr split)))) |
899 nnmail-split-abbrev-alist)) | 1134 |
900 value) | 1135 ;; Check the cache for the regexp for this split. |
901 "\\)\\>"))) | 1136 ;; FIX FIX FIX could avoid calling assq twice here |
902 (setq nnmail-split-cache | 1137 ((assq split nnmail-split-cache) |
903 (cons (cons split regexp) nnmail-split-cache)) | 1138 (goto-char (point-max)) |
904 (goto-char (point-max)) | 1139 ;; FIX FIX FIX problem with re-search-backward is that if you have |
905 (if (re-search-backward regexp nil t) | 1140 ;; a split: (from "foo-\\(bar\\|baz\\)@gnus.org "mail.foo.\\1") |
906 (nnmail-split-it (nth 2 split))))))) | 1141 ;; and someone mails a message with 'To: foo-bar@gnus.org' and |
1142 ;; 'CC: foo-baz@gnus.org', we'll pick 'mail.foo.baz' as the group | |
1143 ;; if the cc line is a later header, even though the other choice | |
1144 ;; is probably better. Also, this routine won't do a crosspost | |
1145 ;; when there are two different matches. | |
1146 ;; I guess you could just make this more determined, and it could | |
1147 ;; look for still more matches prior to this one, and recurse | |
1148 ;; on each of the multiple matches hit. Of course, then you'd | |
1149 ;; want to make sure that nnmail-article-group or nnmail-split-fancy | |
1150 ;; removed duplicates, since there might be more of those. | |
1151 ;; I guess we could also remove duplicates in the & split case, since | |
1152 ;; that's the only thing that can introduce them. | |
1153 (when (re-search-backward (cdr (assq split nnmail-split-cache)) nil t) | |
1154 ;; Someone might want to do a \N sub on this match, so get the | |
1155 ;; correct match positions. | |
1156 (goto-char (match-end 0)) | |
1157 (let ((value (nth 1 split))) | |
1158 (re-search-backward (if (symbolp value) | |
1159 (cdr (assq value nnmail-split-abbrev-alist)) | |
1160 value) | |
1161 (match-end 1))) | |
1162 (nnmail-split-it (nth 2 split)))) | |
1163 | |
1164 ;; Not in cache, compute a regexp for the field/value pair. | |
1165 (t | |
1166 (let* ((field (nth 0 split)) | |
1167 (value (nth 1 split)) | |
1168 (regexp (concat "^\\(\\(" | |
1169 (if (symbolp field) | |
1170 (cdr (assq field nnmail-split-abbrev-alist)) | |
1171 field) | |
1172 "\\):.*\\)\\<\\(" | |
1173 (if (symbolp value) | |
1174 (cdr (assq value nnmail-split-abbrev-alist)) | |
1175 value) | |
1176 "\\)\\>"))) | |
1177 (push (cons split regexp) nnmail-split-cache) | |
1178 ;; Now that it's in the cache, just call nnmail-split-it again | |
1179 ;; on the same split, which will find it immediately in the cache. | |
1180 (nnmail-split-it split))))) | |
1181 | |
1182 (defun nnmail-expand-newtext (newtext) | |
1183 (let ((len (length newtext)) | |
1184 (pos 0) | |
1185 c expanded beg N did-expand) | |
1186 (while (< pos len) | |
1187 (setq beg pos) | |
1188 (while (and (< pos len) | |
1189 (not (= (aref newtext pos) ?\\))) | |
1190 (setq pos (1+ pos))) | |
1191 (unless (= beg pos) | |
1192 (push (substring newtext beg pos) expanded)) | |
1193 (when (< pos len) | |
1194 ;; we hit a \, expand it. | |
1195 (setq did-expand t) | |
1196 (setq pos (1+ pos)) | |
1197 (setq c (aref newtext pos)) | |
1198 (if (not (or (= c ?\&) | |
1199 (and (>= c ?1) | |
1200 (<= c ?9)))) | |
1201 ;; \ followed by some character we don't expand | |
1202 (push (char-to-string c) expanded) | |
1203 ;; \& or \N | |
1204 (if (= c ?\&) | |
1205 (setq N 0) | |
1206 (setq N (- c ?0))) | |
1207 (when (match-beginning N) | |
1208 (push (buffer-substring (match-beginning N) (match-end N)) | |
1209 expanded)))) | |
1210 (setq pos (1+ pos))) | |
1211 (if did-expand | |
1212 (apply 'concat (nreverse expanded)) | |
1213 newtext))) | |
907 | 1214 |
908 ;; Get a list of spool files to read. | 1215 ;; Get a list of spool files to read. |
909 (defun nnmail-get-spool-files (&optional group) | 1216 (defun nnmail-get-spool-files (&optional group) |
910 (if (null nnmail-spool-file) | 1217 (if (null nnmail-spool-file) |
911 ;; No spool file whatsoever. | 1218 ;; No spool file whatsoever. |
917 (or (eq nnmail-spool-file 'procmail) | 1224 (or (eq nnmail-spool-file 'procmail) |
918 nnmail-use-procmail) | 1225 nnmail-use-procmail) |
919 (directory-files | 1226 (directory-files |
920 nnmail-procmail-directory | 1227 nnmail-procmail-directory |
921 t (concat (if group (concat "^" group) "") | 1228 t (concat (if group (concat "^" group) "") |
922 nnmail-procmail-suffix "$") t))) | 1229 nnmail-procmail-suffix "$")))) |
923 (p procmails) | 1230 (p procmails) |
924 (crash (when (and (file-exists-p nnmail-crash-box) | 1231 (crash (when (and (file-exists-p nnmail-crash-box) |
925 (> (nnheader-file-size | 1232 (> (nnheader-file-size |
926 (file-truename nnmail-crash-box)) 0)) | 1233 (file-truename nnmail-crash-box)) |
1234 0)) | |
927 (list nnmail-crash-box)))) | 1235 (list nnmail-crash-box)))) |
928 ;; Remove any directories that inadvertantly match the procmail | 1236 ;; Remove any directories that inadvertently match the procmail |
929 ;; suffix, which might happen if the suffix is "". | 1237 ;; suffix, which might happen if the suffix is "". |
930 (while p | 1238 (while p |
931 (when (file-directory-p (car p)) | 1239 (when (file-directory-p (car p)) |
932 (setq procmails (delete (car p) procmails))) | 1240 (setq procmails (delete (car p) procmails))) |
933 (setq p (cdr p))) | 1241 (setq p (cdr p))) |
941 procmails) | 1249 procmails) |
942 ((and group | 1250 ((and group |
943 (eq nnmail-spool-file 'procmail)) | 1251 (eq nnmail-spool-file 'procmail)) |
944 nil) | 1252 nil) |
945 ((listp nnmail-spool-file) | 1253 ((listp nnmail-spool-file) |
946 (append nnmail-spool-file procmails)) | 1254 (nconc |
1255 (apply | |
1256 'nconc | |
1257 (mapcar | |
1258 (lambda (file) | |
1259 (if (and (not (string-match "^po:" file)) | |
1260 (file-directory-p file)) | |
1261 (nnheader-directory-regular-files file) | |
1262 (list file))) | |
1263 nnmail-spool-file)) | |
1264 procmails)) | |
947 ((stringp nnmail-spool-file) | 1265 ((stringp nnmail-spool-file) |
948 (cons nnmail-spool-file procmails)) | 1266 (if (and (not (string-match "^po:" nnmail-spool-file)) |
1267 (file-directory-p nnmail-spool-file)) | |
1268 (nconc | |
1269 (nnheader-directory-regular-files nnmail-spool-file) | |
1270 procmails) | |
1271 (cons nnmail-spool-file procmails))) | |
949 ((eq nnmail-spool-file 'pop) | 1272 ((eq nnmail-spool-file 'pop) |
950 (cons (format "po:%s" (user-login-name)) procmails)) | 1273 (cons (format "po:%s" (user-login-name)) procmails)) |
951 (t | 1274 (t |
952 procmails)))))) | 1275 procmails)))))) |
953 | 1276 |
956 ;; already activated. | 1279 ;; already activated. |
957 (defun nnmail-activate (backend &optional force) | 1280 (defun nnmail-activate (backend &optional force) |
958 (let (file timestamp file-time) | 1281 (let (file timestamp file-time) |
959 (if (or (not (symbol-value (intern (format "%s-group-alist" backend)))) | 1282 (if (or (not (symbol-value (intern (format "%s-group-alist" backend)))) |
960 force | 1283 force |
961 (and (setq file (condition-case () | 1284 (and (setq file (ignore-errors |
962 (symbol-value (intern (format "%s-active-file" | 1285 (symbol-value (intern (format "%s-active-file" |
963 backend))) | 1286 backend))))) |
964 (error nil))) | |
965 (setq file-time (nth 5 (file-attributes file))) | 1287 (setq file-time (nth 5 (file-attributes file))) |
966 (or (not | 1288 (or (not |
967 (setq timestamp | 1289 (setq timestamp |
968 (condition-case () | 1290 (condition-case () |
969 (symbol-value (intern | 1291 (symbol-value (intern |
976 (and (= (nth 0 file-time) (nth 0 timestamp)) | 1298 (and (= (nth 0 file-time) (nth 0 timestamp)) |
977 (> (nth 1 file-time) (nth 1 timestamp)))))) | 1299 (> (nth 1 file-time) (nth 1 timestamp)))))) |
978 (save-excursion | 1300 (save-excursion |
979 (or (eq timestamp 'none) | 1301 (or (eq timestamp 'none) |
980 (set (intern (format "%s-active-timestamp" backend)) | 1302 (set (intern (format "%s-active-timestamp" backend)) |
981 (current-time))) | 1303 ;;; dmoore@ucsd.edu 25.10.96 |
1304 ;;; it's not always the case that current-time | |
1305 ;;; does correspond to changes in the file's time. So just compare | |
1306 ;;; the file's new time against its own previous time. | |
1307 ;;; (current-time) | |
1308 file-time | |
1309 )) | |
982 (funcall (intern (format "%s-request-list" backend))) | 1310 (funcall (intern (format "%s-request-list" backend))) |
983 (set (intern (format "%s-group-alist" backend)) | 1311 ;;; dmoore@ucsd.edu 25.10.96 |
984 (nnmail-get-active)))) | 1312 ;;; BACKEND-request-list already does this itself! |
1313 ;;; (set (intern (format "%s-group-alist" backend)) | |
1314 ;;; (nnmail-get-active)) | |
1315 )) | |
985 t)) | 1316 t)) |
986 | 1317 |
987 (defun nnmail-message-id () | 1318 (defun nnmail-message-id () |
988 (concat "<" (message-unique-id) "@totally-fudged-out-message-id>")) | 1319 (concat "<" (message-unique-id) "@totally-fudged-out-message-id>")) |
989 | 1320 |
1001 (save-excursion | 1332 (save-excursion |
1002 (set-buffer | 1333 (set-buffer |
1003 (setq nnmail-cache-buffer | 1334 (setq nnmail-cache-buffer |
1004 (get-buffer-create " *nnmail message-id cache*"))) | 1335 (get-buffer-create " *nnmail message-id cache*"))) |
1005 (buffer-disable-undo (current-buffer)) | 1336 (buffer-disable-undo (current-buffer)) |
1006 (and (file-exists-p nnmail-message-id-cache-file) | 1337 (when (file-exists-p nnmail-message-id-cache-file) |
1007 (insert-file-contents nnmail-message-id-cache-file)) | 1338 (nnheader-insert-file-contents nnmail-message-id-cache-file)) |
1008 (set-buffer-modified-p nil) | 1339 (set-buffer-modified-p nil) |
1009 (current-buffer)))) | 1340 (current-buffer)))) |
1010 | 1341 |
1011 (defun nnmail-cache-close () | 1342 (defun nnmail-cache-close () |
1012 (when (and nnmail-cache-buffer | 1343 (when (and nnmail-cache-buffer |
1015 (buffer-modified-p nnmail-cache-buffer)) | 1346 (buffer-modified-p nnmail-cache-buffer)) |
1016 (save-excursion | 1347 (save-excursion |
1017 (set-buffer nnmail-cache-buffer) | 1348 (set-buffer nnmail-cache-buffer) |
1018 ;; Weed out the excess number of Message-IDs. | 1349 ;; Weed out the excess number of Message-IDs. |
1019 (goto-char (point-max)) | 1350 (goto-char (point-max)) |
1020 (and (search-backward "\n" nil t nnmail-message-id-cache-length) | 1351 (when (search-backward "\n" nil t nnmail-message-id-cache-length) |
1021 (progn | 1352 (progn |
1022 (beginning-of-line) | 1353 (beginning-of-line) |
1023 (delete-region (point-min) (point)))) | 1354 (delete-region (point-min) (point)))) |
1024 ;; Save the buffer. | 1355 ;; Save the buffer. |
1025 (or (file-exists-p (file-name-directory nnmail-message-id-cache-file)) | 1356 (or (file-exists-p (file-name-directory nnmail-message-id-cache-file)) |
1026 (make-directory (file-name-directory nnmail-message-id-cache-file) | 1357 (make-directory (file-name-directory nnmail-message-id-cache-file) |
1027 t)) | 1358 t)) |
1028 (write-region (point-min) (point-max) | 1359 (nnmail-write-region (point-min) (point-max) |
1029 nnmail-message-id-cache-file nil 'silent) | 1360 nnmail-message-id-cache-file nil 'silent) |
1030 (set-buffer-modified-p nil) | 1361 (set-buffer-modified-p nil) |
1031 (setq nnmail-cache-buffer nil) | 1362 (setq nnmail-cache-buffer nil) |
1032 ;;(kill-buffer (current-buffer)) | 1363 ;;(kill-buffer (current-buffer)) |
1033 ))) | 1364 ))) |
1034 | 1365 |
1044 (save-excursion | 1375 (save-excursion |
1045 (set-buffer nnmail-cache-buffer) | 1376 (set-buffer nnmail-cache-buffer) |
1046 (goto-char (point-max)) | 1377 (goto-char (point-max)) |
1047 (search-backward id nil t)))) | 1378 (search-backward id nil t)))) |
1048 | 1379 |
1049 (defun nnmail-check-duplication (message-id func) | 1380 (defun nnmail-check-duplication (message-id func artnum-func) |
1381 (run-hooks 'nnmail-prepare-incoming-message-hook) | |
1050 ;; If this is a duplicate message, then we do not save it. | 1382 ;; If this is a duplicate message, then we do not save it. |
1051 (let* ((duplication (nnmail-cache-id-exists-p message-id)) | 1383 (let* ((duplication (nnmail-cache-id-exists-p message-id)) |
1384 (case-fold-search t) | |
1052 (action (when duplication | 1385 (action (when duplication |
1053 (cond | 1386 (cond |
1054 ((memq nnmail-treat-duplicates '(warn delete)) | 1387 ((memq nnmail-treat-duplicates '(warn delete)) |
1055 nnmail-treat-duplicates) | 1388 nnmail-treat-duplicates) |
1056 ((nnheader-functionp nnmail-treat-duplicates) | 1389 ((nnheader-functionp nnmail-treat-duplicates) |
1057 (funcall nnmail-treat-duplicates message-id)) | 1390 (funcall nnmail-treat-duplicates message-id)) |
1058 (t | 1391 (t |
1059 nnmail-treat-duplicates))))) | 1392 nnmail-treat-duplicates)))) |
1393 group-art) | |
1394 ;; Let the backend save the article (or not). | |
1060 (cond | 1395 (cond |
1061 ((not duplication) | 1396 ((not duplication) |
1062 (nnmail-cache-insert message-id) | 1397 (nnmail-cache-insert message-id) |
1063 (funcall func)) | 1398 (funcall func (setq group-art |
1399 (nreverse (nnmail-article-group artnum-func))))) | |
1064 ((eq action 'delete) | 1400 ((eq action 'delete) |
1065 (delete-region (point-min) (point-max))) | 1401 (setq group-art nil)) |
1066 ((eq action 'warn) | 1402 ((eq action 'warn) |
1067 ;; We insert a warning. | 1403 ;; We insert a warning. |
1068 (let ((case-fold-search t) | 1404 (let ((case-fold-search t) |
1069 (newid (nnmail-message-id))) | 1405 (newid (nnmail-message-id))) |
1070 (goto-char (point-min)) | 1406 (goto-char (point-min)) |
1074 (beginning-of-line) | 1410 (beginning-of-line) |
1075 (insert | 1411 (insert |
1076 "Message-ID: " newid "\n" | 1412 "Message-ID: " newid "\n" |
1077 "Gnus-Warning: This is a duplicate of message " message-id "\n") | 1413 "Gnus-Warning: This is a duplicate of message " message-id "\n") |
1078 (nnmail-cache-insert newid) | 1414 (nnmail-cache-insert newid) |
1079 (funcall func))) | 1415 (funcall func (setq group-art |
1416 (nreverse (nnmail-article-group artnum-func)))))) | |
1080 (t | 1417 (t |
1081 (funcall func))))) | 1418 (funcall func (setq group-art |
1419 (nreverse (nnmail-article-group artnum-func)))))) | |
1420 ;; Add the group-art list to the history list. | |
1421 (if group-art | |
1422 (push group-art nnmail-split-history) | |
1423 (delete-region (point-min) (point-max))))) | |
1082 | 1424 |
1083 ;;; Get new mail. | 1425 ;;; Get new mail. |
1084 | 1426 |
1085 (defun nnmail-get-value (&rest args) | 1427 (defun nnmail-get-value (&rest args) |
1086 (let ((sym (intern (apply 'format args)))) | 1428 (let ((sym (intern (apply 'format args)))) |
1088 (symbol-value sym)))) | 1430 (symbol-value sym)))) |
1089 | 1431 |
1090 (defun nnmail-get-new-mail (method exit-func temp | 1432 (defun nnmail-get-new-mail (method exit-func temp |
1091 &optional group spool-func) | 1433 &optional group spool-func) |
1092 "Read new incoming mail." | 1434 "Read new incoming mail." |
1435 ;; Nix out the previous split history. | |
1436 (unless group | |
1437 (setq nnmail-split-history nil)) | |
1093 (let* ((spools (nnmail-get-spool-files group)) | 1438 (let* ((spools (nnmail-get-spool-files group)) |
1094 (group-in group) | 1439 (group-in group) |
1095 incoming incomings spool) | 1440 incoming incomings spool) |
1096 (when (and (nnmail-get-value "%s-get-new-mail" method) | 1441 (when (and (nnmail-get-value "%s-get-new-mail" method) |
1097 nnmail-spool-file) | 1442 nnmail-spool-file) |
1105 ;; mail from each. | 1450 ;; mail from each. |
1106 (while spools | 1451 (while spools |
1107 (setq spool (pop spools)) | 1452 (setq spool (pop spools)) |
1108 ;; We read each spool file if either the spool is a POP-mail | 1453 ;; We read each spool file if either the spool is a POP-mail |
1109 ;; spool, or the file exists. We can't check for the | 1454 ;; spool, or the file exists. We can't check for the |
1110 ;; existance of POPped mail. | 1455 ;; existence of POPped mail. |
1111 (when (or (string-match "^po:" spool) | 1456 (when (or (string-match "^po:" spool) |
1112 (and (file-exists-p spool) | 1457 (and (file-exists-p (file-truename spool)) |
1113 (> (nnheader-file-size (file-truename spool)) 0))) | 1458 (> (nnheader-file-size (file-truename spool)) 0))) |
1114 (nnheader-message 3 "%s: Reading incoming mail..." method) | 1459 (nnheader-message 3 "%s: Reading incoming mail..." method) |
1115 (when (and (nnmail-move-inbox spool) | 1460 (when (and (nnmail-move-inbox spool) |
1116 (file-exists-p nnmail-crash-box)) | 1461 (file-exists-p nnmail-crash-box)) |
1117 ;; There is new mail. We first find out if all this mail | 1462 ;; There is new mail. We first find out if all this mail |
1118 ;; is supposed to go to some specific group. | 1463 ;; is supposed to go to some specific group. |
1119 (setq group (nnmail-get-split-group spool group-in)) | 1464 (setq group (nnmail-get-split-group spool group-in)) |
1120 ;; We split the mail | 1465 ;; We split the mail |
1121 (nnmail-split-incoming | 1466 (nnmail-split-incoming |
1122 nnmail-crash-box (intern (format "%s-save-mail" method)) | 1467 nnmail-crash-box (intern (format "%s-save-mail" method)) |
1123 spool-func group) | 1468 spool-func group (intern (format "%s-active-number" method))) |
1124 ;; Check whether the inbox is to be moved to the special tmp dir. | 1469 ;; Check whether the inbox is to be moved to the special tmp dir. |
1125 (setq incoming | 1470 (setq incoming |
1126 (nnmail-make-complex-temp-name | 1471 (nnmail-make-complex-temp-name |
1127 (expand-file-name | 1472 (expand-file-name |
1128 (if nnmail-tmp-directory | 1473 (if nnmail-tmp-directory |
1175 (setq days (nnmail-days-to-time days)) | 1520 (setq days (nnmail-days-to-time days)) |
1176 ;; Compare the time with the current time. | 1521 ;; Compare the time with the current time. |
1177 (nnmail-time-less days (nnmail-time-since time))))))) | 1522 (nnmail-time-less days (nnmail-time-since time))))))) |
1178 | 1523 |
1179 (defvar nnmail-read-passwd nil) | 1524 (defvar nnmail-read-passwd nil) |
1180 (defun nnmail-read-passwd (prompt) | 1525 (defun nnmail-read-passwd (prompt &rest args) |
1181 (unless nnmail-read-passwd | 1526 "Read a password using PROMPT. |
1182 (if (load "passwd" t) | 1527 If ARGS, PROMPT is used as an argument to `format'." |
1183 (setq nnmail-read-passwd 'read-passwd) | 1528 (let ((prompt |
1184 (autoload 'ange-ftp-read-passwd "ange-ftp") | 1529 (if args |
1185 (setq nnmail-read-passwd 'ange-ftp-read-passwd))) | 1530 (apply 'format prompt args) |
1186 (funcall nnmail-read-passwd prompt)) | 1531 prompt))) |
1532 (unless nnmail-read-passwd | |
1533 (if (load "passwd" t) | |
1534 (setq nnmail-read-passwd 'read-passwd) | |
1535 (autoload 'ange-ftp-read-passwd "ange-ftp") | |
1536 (setq nnmail-read-passwd 'ange-ftp-read-passwd))) | |
1537 (funcall nnmail-read-passwd prompt))) | |
1187 | 1538 |
1188 (defun nnmail-check-syntax () | 1539 (defun nnmail-check-syntax () |
1189 "Check (and modify) the syntax of the message in the current buffer." | 1540 "Check (and modify) the syntax of the message in the current buffer." |
1190 (save-restriction | 1541 (save-restriction |
1191 (message-narrow-to-head) | 1542 (message-narrow-to-head) |
1192 (let ((case-fold-search t)) | 1543 (let ((case-fold-search t)) |
1193 (unless (re-search-forward "^Message-Id:" nil t) | 1544 (unless (re-search-forward "^Message-ID:" nil t) |
1194 (insert "Message-ID: " (nnmail-message-id) "\n"))))) | 1545 (insert "Message-ID: " (nnmail-message-id) "\n"))))) |
1195 | 1546 |
1547 (defun nnmail-write-region (start end filename &optional append visit lockname) | |
1548 "Do a `write-region', and then set the file modes." | |
1549 (write-region start end filename append visit lockname) | |
1550 (set-file-modes filename nnmail-default-file-modes)) | |
1551 | |
1552 ;;; | |
1553 ;;; Status functions | |
1554 ;;; | |
1555 | |
1556 (defun nnmail-replace-status (name value) | |
1557 "Make status NAME and VALUE part of the current status line." | |
1558 (save-restriction | |
1559 (message-narrow-to-head) | |
1560 (let ((status (nnmail-decode-status))) | |
1561 (setq status (delq (member name status) status)) | |
1562 (when value | |
1563 (push (cons name value) status)) | |
1564 (message-remove-header "status") | |
1565 (goto-char (point-max)) | |
1566 (insert "Status: " (nnmail-encode-status status) "\n")))) | |
1567 | |
1568 (defun nnmail-decode-status () | |
1569 "Return a status-value alist from STATUS." | |
1570 (goto-char (point-min)) | |
1571 (when (re-search-forward "^Status: " nil t) | |
1572 (let (name value status) | |
1573 (save-restriction | |
1574 ;; Narrow to the status. | |
1575 (narrow-to-region | |
1576 (point) | |
1577 (if (re-search-forward "^[^ \t]" nil t) | |
1578 (1- (point)) | |
1579 (point-max))) | |
1580 ;; Go through all elements and add them to the list. | |
1581 (goto-char (point-min)) | |
1582 (while (re-search-forward "[^ \t=]+" nil t) | |
1583 (setq name (match-string 0)) | |
1584 (if (not (= (following-char) ?=)) | |
1585 ;; Implied "yes". | |
1586 (setq value "yes") | |
1587 (forward-char 1) | |
1588 (if (not (= (following-char) ?\")) | |
1589 (if (not (looking-at "[^ \t]")) | |
1590 ;; Implied "no". | |
1591 (setq value "no") | |
1592 ;; Unquoted value. | |
1593 (setq value (match-string 0)) | |
1594 (goto-char (match-end 0))) | |
1595 ;; Quoted value. | |
1596 (setq value (read (current-buffer))))) | |
1597 (push (cons name value) status))) | |
1598 status))) | |
1599 | |
1600 (defun nnmail-encode-status (status) | |
1601 "Return a status string from STATUS." | |
1602 (mapconcat | |
1603 (lambda (elem) | |
1604 (concat | |
1605 (car elem) "=" | |
1606 (if (string-match "[ \t]" (cdr elem)) | |
1607 (prin1-to-string (cdr elem)) | |
1608 (cdr elem)))) | |
1609 status " ")) | |
1610 | |
1611 (defun nnmail-split-history () | |
1612 "Generate an overview of where the last mail split put articles." | |
1613 (interactive) | |
1614 (unless nnmail-split-history | |
1615 (error "No current split history")) | |
1616 (with-output-to-temp-buffer "*nnmail split history*" | |
1617 (let ((history nnmail-split-history) | |
1618 elem) | |
1619 (while (setq elem (pop history)) | |
1620 (princ (mapconcat (lambda (ga) | |
1621 (concat (car ga) ":" (int-to-string (cdr ga)))) | |
1622 elem | |
1623 ", ")) | |
1624 (princ "\n"))))) | |
1625 | |
1626 (defun nnmail-new-mail-p (group) | |
1627 "Say whether GROUP has new mail." | |
1628 (let ((his nnmail-split-history) | |
1629 found) | |
1630 (while his | |
1631 (when (assoc group (pop his)) | |
1632 (setq found t | |
1633 his nil))) | |
1634 found)) | |
1635 | |
1196 (run-hooks 'nnmail-load-hook) | 1636 (run-hooks 'nnmail-load-hook) |
1197 | 1637 |
1198 (provide 'nnmail) | 1638 (provide 'nnmail) |
1199 | 1639 |
1200 ;;; nnmail.el ends here | 1640 ;;; nnmail.el ends here |