comparison lisp/gnus/nnimap.el @ 88155:d7ddb3e565de

sync with trunk
author Henrik Enberg <henrik.enberg@telia.com>
date Mon, 16 Jan 2006 00:03:54 +0000
parents 0d8b17d428b5
children
comparison
equal deleted inserted replaced
88154:8ce476d3ba36 88155:d7ddb3e565de
1 ;;; nnimap.el --- imap backend for Gnus 1 ;;; nnimap.el --- imap backend for Gnus
2 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. 2
3 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 ;; 2005 Free Software Foundation, Inc.
3 5
4 ;; Author: Simon Josefsson <jas@pdc.kth.se> 6 ;; Author: Simon Josefsson <jas@pdc.kth.se>
5 ;; Jim Radford <radford@robby.caltech.edu> 7 ;; Jim Radford <radford@robby.caltech.edu>
6 ;; Keywords: mail 8 ;; Keywords: mail
7 9
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details. 20 ;; GNU General Public License for more details.
19 21
20 ;; You should have received a copy of the GNU General Public License 22 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the 23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 ;; Boston, MA 02111-1307, USA. 25 ;; Boston, MA 02110-1301, USA.
24 26
25 ;;; Commentary: 27 ;;; Commentary:
26 28
27 ;; Todo, major things: 29 ;; Todo, major things:
28 ;; 30 ;;
53 ;; o ACAP stuff (perhaps a different project, would be nice to ACAPify 55 ;; o ACAP stuff (perhaps a different project, would be nice to ACAPify
54 ;; .newsrc.eld) 56 ;; .newsrc.eld)
55 ;; o What about Gnus's article editing, can we support it? NO! 57 ;; o What about Gnus's article editing, can we support it? NO!
56 ;; o Use \Draft to support the draft group?? 58 ;; o Use \Draft to support the draft group??
57 ;; o Duplicate suppression 59 ;; o Duplicate suppression
60 ;; o Rewrite UID SEARCH UID X as UID FETCH X (UID) for those with slow servers
58 61
59 ;;; Code: 62 ;;; Code:
60 63
61 (eval-and-compile 64 (require 'imap)
62 (require 'cl)
63 (require 'imap))
64
65 (require 'nnoo) 65 (require 'nnoo)
66 (require 'nnmail) 66 (require 'nnmail)
67 (require 'nnheader) 67 (require 'nnheader)
68 (require 'mm-util) 68 (require 'mm-util)
69 (require 'gnus) 69 (require 'gnus)
70 (require 'gnus-range) 70 (require 'gnus-range)
71 (require 'gnus-start) 71 (require 'gnus-start)
72 (require 'gnus-int) 72 (require 'gnus-int)
73 73
74 (eval-when-compile (require 'cl))
75
74 (nnoo-declare nnimap) 76 (nnoo-declare nnimap)
75 77
76 (defconst nnimap-version "nnimap 0.131") 78 (defconst nnimap-version "nnimap 1.0")
79
80 (defgroup nnimap nil
81 "Reading IMAP mail with Gnus."
82 :group 'gnus)
77 83
78 (defvoo nnimap-address nil 84 (defvoo nnimap-address nil
79 "Address of physical IMAP server. If nil, use the virtual server's name.") 85 "Address of physical IMAP server. If nil, use the virtual server's name.")
80 86
81 (defvoo nnimap-server-port nil 87 (defvoo nnimap-server-port nil
82 "Port number on physical IMAP server. 88 "Port number on physical IMAP server.
83 If nil, defaults to 993 for SSL connections and 143 otherwise.") 89 If nil, defaults to 993 for TLS/SSL connections and 143 otherwise.")
84 90
85 ;; Splitting variables 91 ;; Splitting variables
86 92
87 (defvar nnimap-split-crosspost t 93 (defcustom nnimap-split-crosspost t
88 "If non-nil, do crossposting if several split methods match the mail. 94 "If non-nil, do crossposting if several split methods match the mail.
89 If nil, the first match found will be used.") 95 If nil, the first match found will be used."
90 96 :group 'nnimap
91 (defvar nnimap-split-inbox nil 97 :type 'boolean)
92 "*Name of mailbox to split mail from. 98
99 (defcustom nnimap-split-inbox nil
100 "Name of mailbox to split mail from.
93 101
94 Mail is read from this mailbox and split according to rules in 102 Mail is read from this mailbox and split according to rules in
95 `nnimap-split-rules'. 103 `nnimap-split-rule'.
96 104
97 This can be a string or a list of strings.") 105 This can be a string or a list of strings."
98 106 :group 'nnimap
99 (defvar nnimap-split-rule nil 107 :type '(choice (string)
100 "*Mail will be split according to these rules. 108 (repeat string)))
109
110 (define-widget 'nnimap-strict-function 'function
111 "This widget only matches values that are functionp.
112
113 Warning: This means that a value that is the symbol of a not yet
114 loaded function will not match. Use with care."
115 :match 'nnimap-strict-function-match)
116
117 (defun nnimap-strict-function-match (widget value)
118 "Ignoring WIDGET, match if VALUE is a function."
119 (functionp value))
120
121 (defcustom nnimap-split-rule nil
122 "Mail will be split according to these rules.
101 123
102 Mail is read from mailbox(es) specified in `nnimap-split-inbox'. 124 Mail is read from mailbox(es) specified in `nnimap-split-inbox'.
103 125
104 If you'd like, for instance, one mail group for mail from the 126 If you'd like, for instance, one mail group for mail from the
105 \"gnus-imap\" mailing list, one group for junk mail and leave 127 \"gnus-imap\" mailing list, one group for junk mail and leave
107 this: 129 this:
108 130
109 \(setq nnimap-split-rule '((\"INBOX.gnus-imap\" \"From:.*gnus-imap\") 131 \(setq nnimap-split-rule '((\"INBOX.gnus-imap\" \"From:.*gnus-imap\")
110 (\"INBOX.junk\" \"Subject:.*buy\"))) 132 (\"INBOX.junk\" \"Subject:.*buy\")))
111 133
112 As you can see, `nnimap-split-rule' is a list of lists, where the first 134 As you can see, `nnimap-split-rule' is a list of lists, where the
113 element in each \"rule\" is the name of the IMAP mailbox, and the 135 first element in each \"rule\" is the name of the IMAP mailbox (or the
114 second is a regexp that nnimap will try to match on the header to find 136 symbol `junk' if you want to remove the mail), and the second is a
115 a fit. 137 regexp that nnimap will try to match on the header to find a fit.
116 138
117 The second element can also be a function. In that case, it will be 139 The second element can also be a function. In that case, it will be
118 called narrowed to the headers with the first element of the rule as 140 called narrowed to the headers with the first element of the rule as
119 the argument. It should return a non-nil value if it thinks that the 141 the argument. It should return a non-nil value if it thinks that the
120 mail belongs in that group. 142 mail belongs in that group.
127 even different split rules in different inboxes on the same server, 149 even different split rules in different inboxes on the same server,
128 the syntax of this variable have been extended along the lines of: 150 the syntax of this variable have been extended along the lines of:
129 151
130 \(setq nnimap-split-rule 152 \(setq nnimap-split-rule
131 '((\"my1server\" (\".*\" ((\"ding\" \"ding@gnus.org\") 153 '((\"my1server\" (\".*\" ((\"ding\" \"ding@gnus.org\")
132 (\"junk\" \"From:.*Simon\"))) 154 (\"junk\" \"From:.*Simon\")))
133 (\"my2server\" (\"INBOX\" nnimap-split-fancy)) 155 (\"my2server\" (\"INBOX\" nnimap-split-fancy))
134 (\"my[34]server\" (\".*\" ((\"private\" \"To:.*Simon\") 156 (\"my[34]server\" (\".*\" ((\"private\" \"To:.*Simon\")
135 (\"junk\" my-junk-func))))) 157 (\"junk\" my-junk-func)))))
136 158
137 The virtual server name is in fact a regexp, so that the same rules 159 The virtual server name is in fact a regexp, so that the same rules
138 may apply to several servers. In the example, the servers 160 may apply to several servers. In the example, the servers
139 \"my3server\" and \"my4server\" both use the same rules. Similarly, 161 \"my3server\" and \"my4server\" both use the same rules. Similarly,
140 the inbox string is also a regexp. The actual splitting rules are as 162 the inbox string is also a regexp. The actual splitting rules are as
141 before, either a function, or a list with group/regexp or 163 before, either a function, or a list with group/regexp or
142 group/function elements.") 164 group/function elements."
143 165 :group 'nnimap
144 (defvar nnimap-split-predicate "UNSEEN UNDELETED" 166 :type '(choice :tag "Rule type"
167 (repeat :menu-tag "Single-server"
168 :tag "Single-server list"
169 (list (string :tag "Mailbox")
170 (choice :tag "Predicate"
171 (regexp :tag "A regexp")
172 (nnimap-strict-function :tag "A function"))))
173 (choice :menu-tag "A function"
174 :tag "A function"
175 (function-item nnimap-split-fancy)
176 (function-item nnmail-split-fancy)
177 (nnimap-strict-function :tag "User-defined function"))
178 (repeat :menu-tag "Multi-server (extended)"
179 :tag "Multi-server list"
180 (list (regexp :tag "Server regexp")
181 (list (regexp :tag "Incoming Mailbox regexp")
182 (repeat :tag "Rules for matching server(s) and mailbox(es)"
183 (list (string :tag "Destination mailbox")
184 (choice :tag "Predicate"
185 (regexp :tag "A Regexp")
186 (nnimap-strict-function :tag "A Function")))))))))
187
188 (defcustom nnimap-split-predicate "UNSEEN UNDELETED"
145 "The predicate used to find articles to split. 189 "The predicate used to find articles to split.
146 If you use another IMAP client to peek on articles but always would 190 If you use another IMAP client to peek on articles but always would
147 like nnimap to split them once it's started, you could change this to 191 like nnimap to split them once it's started, you could change this to
148 \"UNDELETED\". Other available predicates are available in 192 \"UNDELETED\". Other available predicates are available in
149 RFC2060 section 6.4.4.") 193 RFC2060 section 6.4.4."
150 194 :group 'nnimap
151 (defvar nnimap-split-fancy nil 195 :type 'string)
152 "Like `nnmail-split-fancy', which see.") 196
197 (defcustom nnimap-split-fancy nil
198 "Like the variable `nnmail-split-fancy'."
199 :group 'nnimap
200 :type 'sexp)
201
202 (defvar nnimap-split-download-body-default nil
203 "Internal variable with default value for `nnimap-split-download-body'.")
204
205 (defcustom nnimap-split-download-body 'default
206 "Whether to download entire articles during splitting.
207 This is generally not required, and will slow things down considerably.
208 You may need it if you want to use an advanced splitting function that
209 analyzes the body before splitting the article.
210 If this variable is nil, bodies will not be downloaded; if this
211 variable is the symbol `default' the default behaviour is
212 used (which currently is nil, unless you use a statistical
213 spam.el test); if this variable is another non-nil value bodies
214 will be downloaded."
215 :version "22.1"
216 :group 'nnimap
217 :type '(choice (const :tag "Let system decide" deault)
218 boolean))
219
220 ;; Performance / bug workaround variables
221
222 (defcustom nnimap-close-asynchronous t
223 "Close mailboxes asynchronously in `nnimap-close-group'.
224 This means that errors caught by nnimap when closing the mailbox will
225 not prevent Gnus from updating the group status, which may be harmful.
226 However, it increases speed."
227 :version "22.1"
228 :type 'boolean
229 :group 'nnimap)
230
231 (defcustom nnimap-dont-close t
232 "Never close mailboxes.
233 This increases the speed of closing mailboxes (quiting group) but may
234 decrease the speed of selecting another mailbox later. Re-selecting
235 the same mailbox will be faster though."
236 :version "22.1"
237 :type 'boolean
238 :group 'nnimap)
239
240 (defcustom nnimap-retrieve-groups-asynchronous t
241 "Send asynchronous STATUS commands for each mailbox before checking mail.
242 If you have mailboxes that rarely receives mail, this speeds up new
243 mail checking. It works by first sending STATUS commands for each
244 mailbox, and then only checking groups which has a modified UIDNEXT
245 more carefully for new mail.
246
247 In summary, the default is O((1-p)*k+p*n) and changing it to nil makes
248 it O(n). If p is small, then the default is probably faster."
249 :version "22.1"
250 :type 'boolean
251 :group 'nnimap)
252
253 (defvoo nnimap-need-unselect-to-notice-new-mail nil
254 "Unselect mailboxes before looking for new mail in them.
255 Some servers seem to need this under some circumstances.")
153 256
154 ;; Authorization / Privacy variables 257 ;; Authorization / Privacy variables
155 258
156 (defvoo nnimap-auth-method nil 259 (defvoo nnimap-auth-method nil
157 "Obsolete.") 260 "Obsolete.")
162 The default, nil, will try to use the \"best\" method the server can 265 The default, nil, will try to use the \"best\" method the server can
163 handle. 266 handle.
164 267
165 Change this if 268 Change this if
166 269
167 1) you want to connect with SSL. The SSL integration with IMAP is 270 1) you want to connect with TLS/SSL. The TLS/SSL integration
168 brain-dead so you'll have to tell it specifically. 271 with IMAP is suboptimal so you'll have to tell it
272 specifically.
169 273
170 2) your server is more capable than your environment -- i.e. your 274 2) your server is more capable than your environment -- i.e. your
171 server accept Kerberos login's but you haven't installed the 275 server accept Kerberos login's but you haven't installed the
172 `imtest' program or your machine isn't configured for Kerberos. 276 `imtest' program or your machine isn't configured for Kerberos.
173 277
174 Possible choices: kerberos4, ssl, network") 278 Possible choices: gssapi, kerberos4, starttls, tls, ssl, network, shell.
279 See also `imap-streams' and `imap-stream-alist'.")
175 280
176 (defvoo nnimap-authenticator nil 281 (defvoo nnimap-authenticator nil
177 "How nnimap authenticate itself to the server. 282 "How nnimap authenticate itself to the server.
178 283
179 The default, nil, will try to use the \"best\" method the server can 284 The default, nil, will try to use the \"best\" method the server can
183 if your server is more capable than your environment -- i.e. you 288 if your server is more capable than your environment -- i.e. you
184 connect to a server that accept Kerberos login's but you haven't 289 connect to a server that accept Kerberos login's but you haven't
185 installed the `imtest' program or your machine isn't configured for 290 installed the `imtest' program or your machine isn't configured for
186 Kerberos. 291 Kerberos.
187 292
188 Possible choices: kerberos4, cram-md5, login, anonymous.") 293 Possible choices: gssapi, kerberos4, digest-md5, cram-md5, login, anonymous.
294 See also `imap-authenticators' and `imap-authenticator-alist'")
189 295
190 (defvoo nnimap-directory (nnheader-concat gnus-directory "overview/") 296 (defvoo nnimap-directory (nnheader-concat gnus-directory "overview/")
191 "Directory to keep NOV cache files for nnimap groups. 297 "Directory to keep NOV cache files for nnimap groups.
192 See also `nnimap-nov-file-name'.") 298 See also `nnimap-nov-file-name'.")
193 299
200 `nnmail-use-long-file-names' is nil") 306 `nnmail-use-long-file-names' is nil")
201 307
202 (defvoo nnimap-nov-file-name-suffix ".novcache" 308 (defvoo nnimap-nov-file-name-suffix ".novcache"
203 "Suffix for NOV cache base filename.") 309 "Suffix for NOV cache base filename.")
204 310
205 (defvoo nnimap-nov-is-evil nil 311 (defvoo nnimap-nov-is-evil gnus-agent
206 "If non-nil, nnimap will never generate or use a local nov database for this backend. 312 "If non-nil, never generate or use a local nov database for this backend.
207 Using nov databases will speed up header fetching considerably. 313 Using nov databases should speed up header fetching considerably.
314 However, it will invoke a UID SEARCH UID command on the server, and
315 some servers implement this command inefficiently by opening each and
316 every message in the group, thus making it quite slow.
208 Unlike other backends, you do not need to take special care if you 317 Unlike other backends, you do not need to take special care if you
209 flip this variable.") 318 flip this variable.")
319
320 (defvoo nnimap-search-uids-not-since-is-evil nil
321 "If non-nil, avoid \"UID SEARCH UID ... NOT SINCE\" queries when expiring.
322 Instead, use \"UID SEARCH SINCE\" to prune the list of expirable
323 articles within Gnus. This seems to be faster on Courier in some cases.")
210 324
211 (defvoo nnimap-expunge-on-close 'always ; 'ask, 'never 325 (defvoo nnimap-expunge-on-close 'always ; 'ask, 'never
212 "Whether to expunge a group when it is closed. 326 "Whether to expunge a group when it is closed.
213 When a IMAP group with articles marked for deletion is closed, this 327 When a IMAP group with articles marked for deletion is closed, this
214 variable determine if nnimap should actually remove the articles or 328 variable determine if nnimap should actually remove the articles or
235 349
236 There are two wildcards * and %. * matches everything, % matches 350 There are two wildcards * and %. * matches everything, % matches
237 everything in the current hierarchy.") 351 everything in the current hierarchy.")
238 352
239 (defvoo nnimap-news-groups nil 353 (defvoo nnimap-news-groups nil
240 "IMAP support a news-like mode, also known as bulletin board mode, where replies is sent via IMAP instead of SMTP. 354 "IMAP support a news-like mode, also known as bulletin board mode,
355 where replies is sent via IMAP instead of SMTP.
241 356
242 This variable should contain a regexp matching groups where you wish 357 This variable should contain a regexp matching groups where you wish
243 replies to be stored to the mailbox directly. 358 replies to be stored to the mailbox directly.
244 359
245 Example: 360 Example:
249 364
250 Note that there is nothing technically different between mail-like and 365 Note that there is nothing technically different between mail-like and
251 news-like mailboxes. If you wish to have a group with todo items or 366 news-like mailboxes. If you wish to have a group with todo items or
252 similar which you wouldn't want to set up a mailing list for, you can 367 similar which you wouldn't want to set up a mailing list for, you can
253 use this to make replies go directly to the group.") 368 use this to make replies go directly to the group.")
369
370 (defvoo nnimap-expunge-search-string "UID %s NOT SINCE %s"
371 "IMAP search command to use for articles that are to be expired.
372 The first %s is replaced by a UID set of articles to search on,
373 and the second %s is replaced by a date criterium.
374
375 One useful (and perhaps the only useful) value to change this to would
376 be `UID %s NOT SENTSINCE %s' to make nnimap use the Date: header
377 instead of the internal date of messages. See section 6.4.4 of RFC
378 2060 for more information on valid strings.")
379
380 (defvoo nnimap-importantize-dormant t
381 "If non-nil, mark \"dormant\" articles as \"ticked\" for other IMAP clients.
382 Note that within Gnus, dormant articles will still (only) be
383 marked as ticked. This is to make \"dormant\" articles stand out,
384 just like \"ticked\" articles, in other IMAP clients.")
254 385
255 (defvoo nnimap-server-address nil 386 (defvoo nnimap-server-address nil
256 "Obsolete. Use `nnimap-address'.") 387 "Obsolete. Use `nnimap-address'.")
257 388
258 (defcustom nnimap-authinfo-file "~/.authinfo" 389 (defcustom nnimap-authinfo-file "~/.authinfo"
268 (cons :format "%v" 399 (cons :format "%v"
269 (const :format "" "login") 400 (const :format "" "login")
270 (string :format "Login: %v")) 401 (string :format "Login: %v"))
271 (cons :format "%v" 402 (cons :format "%v"
272 (const :format "" "password") 403 (const :format "" "password")
273 (string :format "Password: %v"))))))) 404 (string :format "Password: %v"))))))
405 :group 'nnimap)
274 406
275 (defcustom nnimap-prune-cache t 407 (defcustom nnimap-prune-cache t
276 "If non-nil, nnimap check whether articles still exist on server before using data stored in NOV cache." 408 "If non-nil, nnimap check whether articles still exist on server before using data stored in NOV cache."
277 :type 'boolean) 409 :type 'boolean
410 :group 'nnimap)
278 411
279 (defvar nnimap-request-list-method 'imap-mailbox-list 412 (defvar nnimap-request-list-method 'imap-mailbox-list
280 "Method to use to request a list of all folders from the server. 413 "Method to use to request a list of all folders from the server.
281 If this is 'imap-mailbox-lsub, then use a server-side subscription list to 414 If this is 'imap-mailbox-lsub, then use a server-side subscription list to
282 restrict visible folders.") 415 restrict visible folders.")
283 416
417 (defcustom nnimap-debug nil
418 "If non-nil, random debug spews are placed in *nnimap-debug* buffer.
419 Note that username, passwords and other privacy sensitive
420 information (such as e-mail) may be stored in the *nnimap-debug*
421 buffer. It is not written to disk, however. Do not enable this
422 variable unless you are comfortable with that."
423 :group 'nnimap
424 :type 'boolean)
425
284 ;; Internal variables: 426 ;; Internal variables:
285 427
286 (defvar nnimap-debug nil 428 (defvar nnimap-debug-buffer "*nnimap-debug*")
287 "Name of buffer to record debugging info. 429 (defvar nnimap-mailbox-info (gnus-make-hashtable 997))
288 For example: (setq nnimap-debug \"*nnimap-debug*\")")
289 (defvar nnimap-current-move-server nil) 430 (defvar nnimap-current-move-server nil)
290 (defvar nnimap-current-move-group nil) 431 (defvar nnimap-current-move-group nil)
291 (defvar nnimap-current-move-article nil) 432 (defvar nnimap-current-move-article nil)
292 (defvar nnimap-length) 433 (defvar nnimap-length)
293 (defvar nnimap-progress-chars '(?| ?/ ?- ?\\)) 434 (defvar nnimap-progress-chars '(?| ?/ ?- ?\\))
294 (defvar nnimap-progress-how-often 20) 435 (defvar nnimap-progress-how-often 20)
295 (defvar nnimap-counter) 436 (defvar nnimap-counter)
296 (defvar nnimap-callback-callback-function nil
297 "Gnus callback the nnimap asynchronous callback should call.")
298 (defvar nnimap-callback-buffer nil
299 "Which buffer the asynchronous article prefetch callback should work in.")
300 (defvar nnimap-server-buffer-alist nil) ;; Map server name to buffers. 437 (defvar nnimap-server-buffer-alist nil) ;; Map server name to buffers.
301 (defvar nnimap-current-server nil) ;; Current server 438 (defvar nnimap-current-server nil) ;; Current server
302 (defvar nnimap-server-buffer nil) ;; Current servers' buffer 439 (defvar nnimap-server-buffer nil) ;; Current servers' buffer
303 440
304 441
305 442
306 (nnoo-define-basics nnimap) 443 (nnoo-define-basics nnimap)
307 444
323 group (gnus-server-to-method 460 group (gnus-server-to-method
324 (format "nnimap:%s" server)))) 461 (format "nnimap:%s" server))))
325 (new-uidvalidity (imap-mailbox-get 'uidvalidity)) 462 (new-uidvalidity (imap-mailbox-get 'uidvalidity))
326 (old-uidvalidity (gnus-group-get-parameter gnusgroup 'uidvalidity)) 463 (old-uidvalidity (gnus-group-get-parameter gnusgroup 'uidvalidity))
327 (dir (file-name-as-directory (expand-file-name nnimap-directory))) 464 (dir (file-name-as-directory (expand-file-name nnimap-directory)))
328 (nameuid (nnheader-translate-file-chars 465 (nameuid (nnheader-translate-file-chars
329 (concat nnimap-nov-file-name 466 (concat nnimap-nov-file-name
330 (if (equal server "") 467 (if (equal server "")
331 "unnamed" 468 "unnamed"
332 server) "." group "." old-uidvalidity 469 server) "." group "." old-uidvalidity
333 nnimap-nov-file-name-suffix) t)) 470 nnimap-nov-file-name-suffix) t))
334 (file (if (or nnmail-use-long-file-names 471 (file (if (or nnmail-use-long-file-names
335 (file-exists-p (expand-file-name nameuid dir))) 472 (file-exists-p (expand-file-name nameuid dir)))
336 (expand-file-name nameuid dir) 473 (expand-file-name nameuid dir)
337 (expand-file-name 474 (expand-file-name
338 (mm-encode-coding-string 475 (mm-encode-coding-string
339 (nnheader-replace-chars-in-string nameuid ?. ?/) 476 (nnheader-replace-chars-in-string nameuid ?. ?/)
349 t))) 486 t)))
350 487
351 (defun nnimap-before-find-minmax-bugworkaround () 488 (defun nnimap-before-find-minmax-bugworkaround ()
352 "Function called before iterating through mailboxes with 489 "Function called before iterating through mailboxes with
353 `nnimap-find-minmax-uid'." 490 `nnimap-find-minmax-uid'."
354 ;; XXX this is for UoW imapd problem, it doesn't notice new mail in 491 (when nnimap-need-unselect-to-notice-new-mail
355 ;; currently selected mailbox without a re-select/examine. 492 ;; XXX this is for UoW imapd problem, it doesn't notice new mail in
356 (or (null (imap-current-mailbox nnimap-server-buffer)) 493 ;; currently selected mailbox without a re-select/examine.
357 (imap-mailbox-unselect nnimap-server-buffer))) 494 (or (null (imap-current-mailbox nnimap-server-buffer))
495 (imap-mailbox-unselect nnimap-server-buffer))))
358 496
359 (defun nnimap-find-minmax-uid (group &optional examine) 497 (defun nnimap-find-minmax-uid (group &optional examine)
360 "Find lowest and highest active article number in GROUP. 498 "Find lowest and highest active article number in GROUP.
361 If EXAMINE is non-nil the group is selected read-only." 499 If EXAMINE is non-nil the group is selected read-only."
362 (with-current-buffer nnimap-server-buffer 500 (with-current-buffer nnimap-server-buffer
363 (when (imap-mailbox-select group examine) 501 (when (or (string= group (imap-current-mailbox))
502 (imap-mailbox-select group examine))
364 (let (minuid maxuid) 503 (let (minuid maxuid)
365 (when (> (imap-mailbox-get 'exists) 0) 504 (when (> (imap-mailbox-get 'exists) 0)
366 (imap-fetch "1,*" "UID" nil 'nouidfetch) 505 (imap-fetch "1,*" "UID" nil 'nouidfetch)
367 (imap-message-map (lambda (uid Uid) 506 (imap-message-map (lambda (uid Uid)
368 (setq minuid (if minuid (min minuid uid) uid) 507 (setq minuid (if minuid (min minuid uid) uid)
378 imap-current-mailbox 517 imap-current-mailbox
379 (if (imap-mailbox-select group) 518 (if (imap-mailbox-select group)
380 (if (or (nnimap-verify-uidvalidity 519 (if (or (nnimap-verify-uidvalidity
381 group (or server nnimap-current-server)) 520 group (or server nnimap-current-server))
382 (zerop (imap-mailbox-get 'exists group)) 521 (zerop (imap-mailbox-get 'exists group))
522 t ;; for OGnus to see if ignoring uidvalidity
523 ;; changes has any bad effects.
383 (yes-or-no-p 524 (yes-or-no-p
384 (format 525 (format
385 "nnimap: Group %s is not uidvalid. Continue? " group))) 526 "nnimap: Group %s is not uidvalid. Continue? " group)))
386 imap-current-mailbox 527 imap-current-mailbox
387 (imap-mailbox-unselect) 528 (imap-mailbox-unselect)
423 chars (imap-message-get imap-current-message 'RFC822.SIZE))) 564 chars (imap-message-get imap-current-message 'RFC822.SIZE)))
424 (nnheader-insert-nov 565 (nnheader-insert-nov
425 (with-temp-buffer 566 (with-temp-buffer
426 (buffer-disable-undo) 567 (buffer-disable-undo)
427 (insert headers) 568 (insert headers)
428 (nnheader-ms-strip-cr) 569 (let ((head (nnheader-parse-naked-head)))
429 (nnheader-fold-continuation-lines)
430 (subst-char-in-region (point-min) (point-max) ?\t ? )
431 (let ((head (nnheader-parse-head 'naked)))
432 (mail-header-set-number head uid) 570 (mail-header-set-number head uid)
433 (mail-header-set-chars head chars) 571 (mail-header-set-chars head chars)
434 (mail-header-set-lines head lines) 572 (mail-header-set-lines head lines)
435 (mail-header-set-xref 573 (mail-header-set-xref
436 head (format "%s %s:%d" (system-name) mbx uid)) 574 head (format "%s %s:%d" (system-name) mbx uid))
451 1) 589 1)
452 (1- (car articles))))) 590 (1- (car articles)))))
453 articles))))) 591 articles)))))
454 (mapcar (lambda (msgid) 592 (mapcar (lambda (msgid)
455 (imap-search 593 (imap-search
456 (format "HEADER Message-Id %s" msgid))) 594 (format "HEADER Message-Id \"%s\"" msgid)))
457 articles)))) 595 articles))))
458 596
459 (defun nnimap-group-overview-filename (group server) 597 (defun nnimap-group-overview-filename (group server)
460 "Make file name for GROUP on SERVER." 598 "Make file name for GROUP on SERVER."
461 (let* ((dir (file-name-as-directory (expand-file-name nnimap-directory))) 599 (let* ((dir (file-name-as-directory (expand-file-name nnimap-directory)))
462 (uidvalidity (gnus-group-get-parameter 600 (uidvalidity (gnus-group-get-parameter
463 (gnus-group-prefixed-name 601 (gnus-group-prefixed-name
464 group (gnus-server-to-method 602 group (gnus-server-to-method
465 (format "nnimap:%s" server))) 603 (format "nnimap:%s" server)))
466 'uidvalidity)) 604 'uidvalidity))
467 (name (nnheader-translate-file-chars 605 (name (nnheader-translate-file-chars
468 (concat nnimap-nov-file-name 606 (concat nnimap-nov-file-name
469 (if (equal server "") 607 (if (equal server "")
470 "unnamed" 608 "unnamed"
471 server) "." group nnimap-nov-file-name-suffix) t)) 609 server) "." group nnimap-nov-file-name-suffix) t))
472 (nameuid (nnheader-translate-file-chars 610 (nameuid (nnheader-translate-file-chars
473 (concat nnimap-nov-file-name 611 (concat nnimap-nov-file-name
474 (if (equal server "") 612 (if (equal server "")
475 "unnamed" 613 "unnamed"
476 server) "." group "." uidvalidity 614 server) "." group "." uidvalidity
477 nnimap-nov-file-name-suffix) t)) 615 nnimap-nov-file-name-suffix) t))
478 (oldfile (if (or nnmail-use-long-file-names 616 (oldfile (if (or nnmail-use-long-file-names
479 (file-exists-p (expand-file-name name dir))) 617 (file-exists-p (expand-file-name name dir)))
480 (expand-file-name name dir) 618 (expand-file-name name dir)
481 (expand-file-name 619 (expand-file-name
482 (mm-encode-coding-string 620 (mm-encode-coding-string
483 (nnheader-replace-chars-in-string name ?. ?/) 621 (nnheader-replace-chars-in-string name ?. ?/)
484 nnmail-pathname-coding-system) 622 nnmail-pathname-coding-system)
485 dir))) 623 dir)))
486 (newfile (if (or nnmail-use-long-file-names 624 (newfile (if (or nnmail-use-long-file-names
487 (file-exists-p (expand-file-name nameuid dir))) 625 (file-exists-p (expand-file-name nameuid dir)))
488 (expand-file-name nameuid dir) 626 (expand-file-name nameuid dir)
489 (expand-file-name 627 (expand-file-name
490 (mm-encode-coding-string 628 (mm-encode-coding-string
491 (nnheader-replace-chars-in-string nameuid ?. ?/) 629 (nnheader-replace-chars-in-string nameuid ?. ?/)
492 nnmail-pathname-coding-system) 630 nnmail-pathname-coding-system)
493 dir)))) 631 dir))))
494 (when (and (file-exists-p oldfile) (not (file-exists-p newfile))) 632 (when (and (file-exists-p oldfile) (not (file-exists-p newfile)))
495 (message "nnimap: Upgrading novcache filename...") 633 (message "nnimap: Upgrading novcache filename...")
496 (sit-for 1) 634 (sit-for 1)
497 (gnus-make-directory (file-name-directory newfile)) 635 (gnus-make-directory (file-name-directory newfile))
498 (unless (ignore-errors (rename-file oldfile newfile) t) 636 (unless (ignore-errors (rename-file oldfile newfile) t)
531 (copy-sequence 669 (copy-sequence
532 nnmail-extra-headers)))) 670 nnmail-extra-headers))))
533 (if (imap-capability 'IMAP4rev1) 671 (if (imap-capability 'IMAP4rev1)
534 (format "BODY.PEEK[HEADER.FIELDS %s])" headers) 672 (format "BODY.PEEK[HEADER.FIELDS %s])" headers)
535 (format "RFC822.HEADER.LINES %s)" headers))))) 673 (format "RFC822.HEADER.LINES %s)" headers)))))
674 (with-current-buffer nntp-server-buffer
675 (sort-numeric-fields 1 (point-min) (point-max)))
536 (and (numberp nnmail-large-newsgroup) 676 (and (numberp nnmail-large-newsgroup)
537 (> nnimap-length nnmail-large-newsgroup) 677 (> nnimap-length nnmail-large-newsgroup)
538 (nnheader-message 6 "nnimap: Retrieving headers...done"))))) 678 (nnheader-message 6 "nnimap: Retrieving headers...done")))))
539 679
540 (defun nnimap-use-nov-p (group server) 680 (defun nnimap-dont-use-nov-p (group server)
541 (or gnus-nov-is-evil nnimap-nov-is-evil 681 (or gnus-nov-is-evil nnimap-nov-is-evil
542 (unless (and (gnus-make-directory 682 (unless (and (gnus-make-directory
543 (file-name-directory 683 (file-name-directory
544 (nnimap-group-overview-filename group server))) 684 (nnimap-group-overview-filename group server)))
545 (file-writable-p 685 (file-writable-p
549 689
550 (deffoo nnimap-retrieve-headers (articles &optional group server fetch-old) 690 (deffoo nnimap-retrieve-headers (articles &optional group server fetch-old)
551 (when (nnimap-possibly-change-group group server) 691 (when (nnimap-possibly-change-group group server)
552 (with-current-buffer nntp-server-buffer 692 (with-current-buffer nntp-server-buffer
553 (erase-buffer) 693 (erase-buffer)
554 (if (nnimap-use-nov-p group server) 694 (if (nnimap-dont-use-nov-p group server)
555 (nnimap-retrieve-headers-from-server 695 (nnimap-retrieve-headers-from-server
556 (gnus-compress-sequence articles) group server) 696 (gnus-compress-sequence articles) group server)
557 (let (uids cached low high) 697 (let (uids cached low high)
558 (when (setq uids (nnimap-retrieve-which-headers articles fetch-old) 698 (when (setq uids (nnimap-retrieve-which-headers articles fetch-old)
559 low (car uids) 699 low (car uids)
572 (cons (1+ (cdr cached)) high) group server)) 712 (cons (1+ (cdr cached)) high) group server))
573 (when nnimap-prune-cache 713 (when nnimap-prune-cache
574 ;; remove nov's for articles which has expired on server 714 ;; remove nov's for articles which has expired on server
575 (goto-char (point-min)) 715 (goto-char (point-min))
576 (dolist (uid (gnus-set-difference articles uids)) 716 (dolist (uid (gnus-set-difference articles uids))
577 (when (re-search-forward (format "^%d\t" uid) nil t) 717 (when (re-search-forward (format "^%d\t" uid) nil t)
578 (gnus-delete-line))))) 718 (gnus-delete-line)))))
579 ;; nothing cached, fetch whole range from server 719 ;; nothing cached, fetch whole range from server
580 (nnimap-retrieve-headers-from-server 720 (nnimap-retrieve-headers-from-server
581 (cons low high) group server)) 721 (cons low high) group server))
582 (when (buffer-modified-p) 722 (when (buffer-modified-p)
583 (nnmail-write-region 723 (nnmail-write-region
592 (nnheader-report 'nnimap "Can't open connection to server %s" server) 732 (nnheader-report 'nnimap "Can't open connection to server %s" server)
593 (unless (or (imap-capability 'IMAP4 nnimap-server-buffer) 733 (unless (or (imap-capability 'IMAP4 nnimap-server-buffer)
594 (imap-capability 'IMAP4rev1 nnimap-server-buffer)) 734 (imap-capability 'IMAP4rev1 nnimap-server-buffer))
595 (imap-close nnimap-server-buffer) 735 (imap-close nnimap-server-buffer)
596 (nnheader-report 'nnimap "Server %s is not IMAP4 compliant" server)) 736 (nnheader-report 'nnimap "Server %s is not IMAP4 compliant" server))
597 (let* ((list (gnus-parse-netrc nnimap-authinfo-file)) 737 (let* ((list (progn (gnus-message 7 "Parsing authinfo file `%s'."
738 nnimap-authinfo-file)
739 (gnus-parse-netrc nnimap-authinfo-file)))
598 (port (if nnimap-server-port 740 (port (if nnimap-server-port
599 (int-to-string nnimap-server-port) 741 (int-to-string nnimap-server-port)
600 "imap")) 742 "imap"))
601 (alist (gnus-netrc-machine list (or nnimap-server-address 743 (alist (or (gnus-netrc-machine list server port "imap")
602 nnimap-address server) 744 (gnus-netrc-machine list server port "imaps")
603 port "imap")) 745 (gnus-netrc-machine list
746 (or nnimap-server-address
747 nnimap-address)
748 port "imap")
749 (gnus-netrc-machine list
750 (or nnimap-server-address
751 nnimap-address)
752 port "imaps")))
604 (user (gnus-netrc-get alist "login")) 753 (user (gnus-netrc-get alist "login"))
605 (passwd (gnus-netrc-get alist "password"))) 754 (passwd (gnus-netrc-get alist "password")))
606 (if (imap-authenticate user passwd nnimap-server-buffer) 755 (if (imap-authenticate user passwd nnimap-server-buffer)
607 (prog1 756 (prog1
608 (push (list server nnimap-server-buffer) 757 (push (list server nnimap-server-buffer)
624 (if (assq 'nnimap-server-address defs) 773 (if (assq 'nnimap-server-address defs)
625 (push (list 'nnimap-address 774 (push (list 'nnimap-address
626 (cadr (assq 'nnimap-server-address defs))) defs) 775 (cadr (assq 'nnimap-server-address defs))) defs)
627 (push (list 'nnimap-address server) defs))) 776 (push (list 'nnimap-address server) defs)))
628 (nnoo-change-server 'nnimap server defs) 777 (nnoo-change-server 'nnimap server defs)
778 (or nnimap-server-buffer
779 (setq nnimap-server-buffer (cadr (assq 'nnimap-server-buffer defs))))
629 (with-current-buffer (get-buffer-create nnimap-server-buffer) 780 (with-current-buffer (get-buffer-create nnimap-server-buffer)
630 (nnoo-change-server 'nnimap server defs)) 781 (nnoo-change-server 'nnimap server defs))
631 (or (and nnimap-server-buffer 782 (or (and nnimap-server-buffer
632 (imap-opened nnimap-server-buffer)) 783 (imap-opened nnimap-server-buffer)
784 (if (with-current-buffer nnimap-server-buffer
785 (memq imap-state '(auth select examine)))
786 t
787 (imap-close nnimap-server-buffer)
788 (nnimap-open-connection server)))
633 (nnimap-open-connection server)))) 789 (nnimap-open-connection server))))
634 790
635 (deffoo nnimap-server-opened (&optional server) 791 (deffoo nnimap-server-opened (&optional server)
636 "Whether SERVER is opened. 792 "Whether SERVER is opened.
637 If SERVER is the current virtual server, and the connection to the 793 If SERVER is the current virtual server, and the connection to the
669 "This function returns the last error message from server." 825 "This function returns the last error message from server."
670 (when (nnimap-possibly-change-server server) 826 (when (nnimap-possibly-change-server server)
671 (nnoo-status-message 'nnimap server))) 827 (nnoo-status-message 'nnimap server)))
672 828
673 (defun nnimap-demule (string) 829 (defun nnimap-demule (string)
830 ;; BEWARE: we used to use string-as-multibyte here which is braindead
831 ;; because it will turn accidental emacs-mule-valid byte sequences
832 ;; into multibyte chars. --Stef
833 ;; Reverted, braindead got 7.5 out of 10 on imdb, so it can't be
834 ;; that bad. --Simon
674 (funcall (if (and (fboundp 'string-as-multibyte) 835 (funcall (if (and (fboundp 'string-as-multibyte)
675 (subrp (symbol-function 'string-as-multibyte))) 836 (subrp (symbol-function 'string-as-multibyte)))
676 'string-as-multibyte 837 'string-as-multibyte
677 'identity) 838 'identity)
678 (or string ""))) 839 (or string "")))
679 840
680 (defun nnimap-callback () 841 (defun nnimap-make-callback (article gnus-callback buffer)
681 (remove-hook 'imap-fetch-data-hook 'nnimap-callback) 842 "Return a callback function."
682 (with-current-buffer nnimap-callback-buffer 843 `(lambda ()
683 (insert 844 (nnimap-callback ,article ,gnus-callback ,buffer)))
684 (with-current-buffer nnimap-server-buffer 845
685 (nnimap-demule 846 (defun nnimap-callback (article gnus-callback buffer)
686 (if (imap-capability 'IMAP4rev1) 847 (when (eq article (imap-current-message))
687 ;; xxx don't just use car? alist doesn't contain 848 (remove-hook 'imap-fetch-data-hook
688 ;; anything else now, but it might... 849 (nnimap-make-callback article gnus-callback buffer))
689 (nth 2 (car (imap-message-get (imap-current-message) 'BODYDETAIL))) 850 (with-current-buffer buffer
690 (imap-message-get (imap-current-message) 'RFC822))))) 851 (insert
691 (nnheader-ms-strip-cr) 852 (with-current-buffer nnimap-server-buffer
692 (funcall nnimap-callback-callback-function t))) 853 (nnimap-demule
854 (if (imap-capability 'IMAP4rev1)
855 ;; xxx don't just use car? alist doesn't contain
856 ;; anything else now, but it might...
857 (nth 2 (car (imap-message-get article 'BODYDETAIL)))
858 (imap-message-get article 'RFC822)))))
859 (nnheader-ms-strip-cr)
860 (funcall gnus-callback t))))
693 861
694 (defun nnimap-request-article-part (article part prop &optional 862 (defun nnimap-request-article-part (article part prop &optional
695 group server to-buffer detail) 863 group server to-buffer detail)
696 (when (nnimap-possibly-change-group group server) 864 (when (nnimap-possibly-change-group group server)
697 (let ((article (if (stringp article) 865 (let ((article (if (stringp article)
698 (car-safe (imap-search 866 (car-safe (imap-search
699 (format "HEADER Message-Id %s" article) 867 (format "HEADER Message-Id \"%s\"" article)
700 nnimap-server-buffer)) 868 nnimap-server-buffer))
701 article))) 869 article)))
702 (when article 870 (when article
703 (gnus-message 10 "nnimap: Fetching (part of) article %d..." article) 871 (gnus-message 10 "nnimap: Fetching (part of) article %d from %s..."
872 article (or group imap-current-mailbox
873 gnus-newsgroup-name))
704 (if (not nnheader-callback-function) 874 (if (not nnheader-callback-function)
705 (with-current-buffer (or to-buffer nntp-server-buffer) 875 (with-current-buffer (or to-buffer nntp-server-buffer)
706 (erase-buffer) 876 (erase-buffer)
707 (let ((data (imap-fetch article part prop nil 877 (let ((data (imap-fetch article part prop nil
708 nnimap-server-buffer))) 878 nnimap-server-buffer)))
709 (insert (nnimap-demule (if detail 879 (insert (nnimap-demule (if detail
710 (nth 2 (car data)) 880 (nth 2 (car data))
711 data)))) 881 data))))
712 (nnheader-ms-strip-cr) 882 (nnheader-ms-strip-cr)
713 (gnus-message 10 "nnimap: Fetching (part of) article %d...done" 883 (gnus-message
714 article) 884 10 "nnimap: Fetching (part of) article %d from %s...done"
885 article (or group imap-current-mailbox gnus-newsgroup-name))
715 (if (bobp) 886 (if (bobp)
716 (nnheader-report 'nnimap "No such article: %s" 887 (nnheader-report 'nnimap "No such article %d in %s: %s"
888 article (or group imap-current-mailbox
889 gnus-newsgroup-name)
717 (imap-error-text nnimap-server-buffer)) 890 (imap-error-text nnimap-server-buffer))
718 (cons group article))) 891 (cons group article)))
719 (add-hook 'imap-fetch-data-hook 'nnimap-callback) 892 (add-hook 'imap-fetch-data-hook
720 (setq nnimap-callback-callback-function nnheader-callback-function 893 (nnimap-make-callback article
721 nnimap-callback-buffer nntp-server-buffer) 894 nnheader-callback-function
895 nntp-server-buffer))
722 (imap-fetch-asynch article part nil nnimap-server-buffer) 896 (imap-fetch-asynch article part nil nnimap-server-buffer)
723 (cons group article)))))) 897 (cons group article))))))
724 898
725 (deffoo nnimap-asynchronous-p () 899 (deffoo nnimap-asynchronous-p ()
726 t) 900 t)
764 (max 1 (or (nth 1 info) 1)) 938 (max 1 (or (nth 1 info) 1))
765 (or (nth 2 info) 0) group) 939 (or (nth 2 info) 0) group)
766 (nnheader-report 'nnimap "Group %s selected" group) 940 (nnheader-report 'nnimap "Group %s selected" group)
767 t))))) 941 t)))))
768 942
943 (defun nnimap-update-unseen (group &optional server)
944 "Update the unseen count in `nnimap-mailbox-info'."
945 (gnus-sethash
946 (gnus-group-prefixed-name group server)
947 (let ((old (gnus-gethash-safe (gnus-group-prefixed-name group server)
948 nnimap-mailbox-info)))
949 (list (nth 0 old) (nth 1 old)
950 (imap-mailbox-status group 'unseen nnimap-server-buffer)
951 (nth 3 old)))
952 nnimap-mailbox-info))
953
769 (defun nnimap-close-group (group &optional server) 954 (defun nnimap-close-group (group &optional server)
770 (with-current-buffer nnimap-server-buffer 955 (with-current-buffer nnimap-server-buffer
771 (when (and (imap-opened) 956 (when (and (imap-opened)
772 (nnimap-possibly-change-group group server)) 957 (nnimap-possibly-change-group group server))
958 (nnimap-update-unseen group server)
773 (case nnimap-expunge-on-close 959 (case nnimap-expunge-on-close
774 ('always (imap-mailbox-expunge) 960 (always (progn
775 (imap-mailbox-close)) 961 (imap-mailbox-expunge nnimap-close-asynchronous)
776 ('ask (if (and (imap-search "DELETED") 962 (unless nnimap-dont-close
777 (gnus-y-or-n-p (format 963 (imap-mailbox-close nnimap-close-asynchronous))))
778 "Expunge articles in group `%s'? " 964 (ask (if (and (imap-search "DELETED")
779 imap-current-mailbox))) 965 (gnus-y-or-n-p (format "Expunge articles in group `%s'? "
780 (progn (imap-mailbox-expunge) 966 imap-current-mailbox)))
781 (imap-mailbox-close)) 967 (progn
782 (imap-mailbox-unselect))) 968 (imap-mailbox-expunge nnimap-close-asynchronous)
969 (unless nnimap-dont-close
970 (imap-mailbox-close nnimap-close-asynchronous)))
971 (imap-mailbox-unselect)))
783 (t (imap-mailbox-unselect))) 972 (t (imap-mailbox-unselect)))
784 (not imap-current-mailbox)))) 973 (not imap-current-mailbox))))
785 974
786 (defun nnimap-pattern-to-list-arguments (pattern) 975 (defun nnimap-pattern-to-list-arguments (pattern)
787 (mapcar (lambda (p) 976 (mapcar (lambda (p)
804 (cdr pattern) (car pattern))) 993 (cdr pattern) (car pattern)))
805 (or (member "\\NoSelect" (imap-mailbox-get 'list-flags mbx)) 994 (or (member "\\NoSelect" (imap-mailbox-get 'list-flags mbx))
806 (let ((info (nnimap-find-minmax-uid mbx 'examine))) 995 (let ((info (nnimap-find-minmax-uid mbx 'examine)))
807 (when info 996 (when info
808 (with-current-buffer nntp-server-buffer 997 (with-current-buffer nntp-server-buffer
809 (insert (format "\"%s\" %d %d y\n" 998 (insert (format "\"%s\" %d %d y\n"
810 mbx (or (nth 2 info) 0) 999 mbx (or (nth 2 info) 0)
811 (max 1 (or (nth 1 info) 1))))))))))) 1000 (max 1 (or (nth 1 info) 1)))))))))))
812 (gnus-message 5 "nnimap: Generating active list%s...done" 1001 (gnus-message 5 "nnimap: Generating active list%s...done"
813 (if (> (length server) 0) (concat " for " server) "")) 1002 (if (> (length server) 0) (concat " for " server) ""))
814 t)) 1003 t))
815 1004
816 (deffoo nnimap-request-post (&optional server) 1005 (deffoo nnimap-request-post (&optional server)
817 (let ((success t)) 1006 (let ((success t))
818 (dolist (mbx (message-unquote-tokens 1007 (dolist (mbx (message-unquote-tokens
819 (message-tokenize-header 1008 (message-tokenize-header
820 (message-fetch-field "Newsgroups") ", ")) success) 1009 (message-fetch-field "Newsgroups") ", ")) success)
821 (let ((to-newsgroup (gnus-group-prefixed-name mbx gnus-command-method))) 1010 (let ((to-newsgroup (gnus-group-prefixed-name mbx gnus-command-method)))
822 (or (gnus-active to-newsgroup) 1011 (or (gnus-active to-newsgroup)
823 (gnus-activate-group to-newsgroup) 1012 (gnus-activate-group to-newsgroup)
824 (if (gnus-y-or-n-p (format "No such group: %s. Create it? " 1013 (if (gnus-y-or-n-p (format "No such group: %s. Create it? "
825 to-newsgroup)) 1014 to-newsgroup))
832 (unless (nnimap-request-accept-article mbx (nth 1 gnus-command-method)) 1021 (unless (nnimap-request-accept-article mbx (nth 1 gnus-command-method))
833 (setq success nil)))))) 1022 (setq success nil))))))
834 1023
835 ;; Optional backend functions 1024 ;; Optional backend functions
836 1025
1026 (defun nnimap-string-lessp-numerical (s1 s2)
1027 "Return t if first arg string is less than second in numerical order."
1028 (cond ((string= s1 s2)
1029 nil)
1030 ((> (length s1) (length s2))
1031 nil)
1032 ((< (length s1) (length s2))
1033 t)
1034 ((< (string-to-number (substring s1 0 1))
1035 (string-to-number (substring s2 0 1)))
1036 t)
1037 ((> (string-to-number (substring s1 0 1))
1038 (string-to-number (substring s2 0 1)))
1039 nil)
1040 (t
1041 (nnimap-string-lessp-numerical (substring s1 1) (substring s2 1)))))
1042
837 (deffoo nnimap-retrieve-groups (groups &optional server) 1043 (deffoo nnimap-retrieve-groups (groups &optional server)
838 (when (nnimap-possibly-change-server server) 1044 (when (nnimap-possibly-change-server server)
839 (gnus-message 5 "nnimap: Checking mailboxes...") 1045 (gnus-message 5 "nnimap: Checking mailboxes...")
840 (with-current-buffer nntp-server-buffer 1046 (with-current-buffer nntp-server-buffer
841 (erase-buffer) 1047 (erase-buffer)
842 (nnimap-before-find-minmax-bugworkaround) 1048 (nnimap-before-find-minmax-bugworkaround)
843 (dolist (group groups) 1049 (let (asyncgroups slowgroups)
844 (gnus-message 7 "nnimap: Checking mailbox %s" group) 1050 (if (null nnimap-retrieve-groups-asynchronous)
845 (or (member "\\NoSelect" 1051 (setq slowgroups groups)
846 (imap-mailbox-get 'list-flags group nnimap-server-buffer)) 1052 (dolist (group groups)
847 (let ((info (nnimap-find-minmax-uid group 'examine))) 1053 (gnus-message 9 "nnimap: Quickly checking mailbox %s" group)
848 (insert (format "\"%s\" %d %d y\n" group 1054 (add-to-list (if (gnus-gethash-safe
849 (or (nth 2 info) 0) 1055 (gnus-group-prefixed-name group server)
850 (max 1 (or (nth 1 info) 1)))))))) 1056 nnimap-mailbox-info)
1057 'asyncgroups
1058 'slowgroups)
1059 (list group (imap-mailbox-status-asynch
1060 group '(uidvalidity uidnext unseen)
1061 nnimap-server-buffer))))
1062 (dolist (asyncgroup asyncgroups)
1063 (let ((group (nth 0 asyncgroup))
1064 (tag (nth 1 asyncgroup))
1065 new old)
1066 (when (imap-ok-p (imap-wait-for-tag tag nnimap-server-buffer))
1067 (if (or (not (string=
1068 (nth 0 (gnus-gethash (gnus-group-prefixed-name
1069 group server)
1070 nnimap-mailbox-info))
1071 (imap-mailbox-get 'uidvalidity group
1072 nnimap-server-buffer)))
1073 (not (string=
1074 (nth 1 (gnus-gethash (gnus-group-prefixed-name
1075 group server)
1076 nnimap-mailbox-info))
1077 (imap-mailbox-get 'uidnext group
1078 nnimap-server-buffer))))
1079 (push (list group) slowgroups)
1080 (insert (nth 3 (gnus-gethash (gnus-group-prefixed-name
1081 group server)
1082 nnimap-mailbox-info))))))))
1083 (dolist (group slowgroups)
1084 (if nnimap-retrieve-groups-asynchronous
1085 (setq group (car group)))
1086 (gnus-message 7 "nnimap: Mailbox %s modified" group)
1087 (imap-mailbox-put 'uidnext nil group nnimap-server-buffer)
1088 (or (member "\\NoSelect" (imap-mailbox-get 'list-flags group
1089 nnimap-server-buffer))
1090 (let* ((info (nnimap-find-minmax-uid group 'examine))
1091 (str (format "\"%s\" %d %d y\n" group
1092 (or (nth 2 info) 0)
1093 (max 1 (or (nth 1 info) 1)))))
1094 (when (> (or (imap-mailbox-get 'recent group
1095 nnimap-server-buffer) 0)
1096 0)
1097 (push (list (cons group 0)) nnmail-split-history))
1098 (insert str)
1099 (when nnimap-retrieve-groups-asynchronous
1100 (gnus-sethash
1101 (gnus-group-prefixed-name group server)
1102 (list (or (imap-mailbox-get
1103 'uidvalidity group nnimap-server-buffer)
1104 (imap-mailbox-status
1105 group 'uidvalidity nnimap-server-buffer))
1106 (or (imap-mailbox-get
1107 'uidnext group nnimap-server-buffer)
1108 (imap-mailbox-status
1109 group 'uidnext nnimap-server-buffer))
1110 (or (imap-mailbox-get
1111 'unseen group nnimap-server-buffer)
1112 (imap-mailbox-status
1113 group 'unseen nnimap-server-buffer))
1114 str)
1115 nnimap-mailbox-info)))))))
851 (gnus-message 5 "nnimap: Checking mailboxes...done") 1116 (gnus-message 5 "nnimap: Checking mailboxes...done")
852 'active)) 1117 'active))
853 1118
854 (deffoo nnimap-request-update-info-internal (group info &optional server) 1119 (deffoo nnimap-request-update-info-internal (group info &optional server)
855 (when (nnimap-possibly-change-group group server) 1120 (when (nnimap-possibly-change-group group server)
856 (when info;; xxx what does this mean? should we create a info? 1121 (when info ;; xxx what does this mean? should we create a info?
857 (with-current-buffer nnimap-server-buffer 1122 (with-current-buffer nnimap-server-buffer
858 (gnus-message 5 "nnimap: Updating info for %s..." 1123 (gnus-message 5 "nnimap: Updating info for %s..."
859 (gnus-info-group info)) 1124 (gnus-info-group info))
860 1125
861 (when (nnimap-mark-permanent-p 'read) 1126 (when (nnimap-mark-permanent-p 'read)
879 (list (cons (car seen) (car seen))) 1144 (list (cons (car seen) (car seen)))
880 seen)) 1145 seen))
881 (gnus-info-set-read info seen))) 1146 (gnus-info-set-read info seen)))
882 1147
883 (mapcar (lambda (pred) 1148 (mapcar (lambda (pred)
884 (when (and (nnimap-mark-permanent-p (cdr pred)) 1149 (when (or (eq (cdr pred) 'recent)
885 (member (nnimap-mark-to-flag (cdr pred)) 1150 (and (nnimap-mark-permanent-p (cdr pred))
886 (imap-mailbox-get 'flags))) 1151 (member (nnimap-mark-to-flag (cdr pred))
1152 (imap-mailbox-get 'flags))))
887 (gnus-info-set-marks 1153 (gnus-info-set-marks
888 info 1154 info
889 (nnimap-update-alist-soft 1155 (gnus-update-alist-soft
890 (cdr pred) 1156 (cdr pred)
891 (gnus-compress-sequence 1157 (gnus-compress-sequence
892 (imap-search (nnimap-mark-to-predicate (cdr pred)))) 1158 (imap-search (nnimap-mark-to-predicate (cdr pred))))
893 (gnus-info-marks info)) 1159 (gnus-info-marks info))
894 t))) 1160 t)))
895 gnus-article-mark-lists) 1161 gnus-article-mark-lists)
896 1162
897 ;; nnimap mark dormant article as ticked too (for other clients) 1163 (when nnimap-importantize-dormant
898 ;; so we remove that mark for gnus since we support dormant 1164 ;; nnimap mark dormant article as ticked too (for other clients)
899 (gnus-info-set-marks 1165 ;; so we remove that mark for gnus since we support dormant
900 info 1166 (gnus-info-set-marks
901 (nnimap-update-alist-soft 1167 info
902 'tick 1168 (gnus-update-alist-soft
903 (gnus-remove-from-range 1169 'tick
904 (cdr-safe (assoc 'tick (gnus-info-marks info))) 1170 (gnus-remove-from-range
905 (cdr-safe (assoc 'dormant (gnus-info-marks info)))) 1171 (cdr-safe (assoc 'tick (gnus-info-marks info)))
906 (gnus-info-marks info)) 1172 (cdr-safe (assoc 'dormant (gnus-info-marks info))))
907 t) 1173 (gnus-info-marks info))
1174 t))
908 1175
909 (gnus-message 5 "nnimap: Updating info for %s...done" 1176 (gnus-message 5 "nnimap: Updating info for %s...done"
910 (gnus-info-group info)) 1177 (gnus-info-group info))
911 1178
912 info)))) 1179 info))))
924 (while (setq action (pop actions)) 1191 (while (setq action (pop actions))
925 (let ((range (nth 0 action)) 1192 (let ((range (nth 0 action))
926 (what (nth 1 action)) 1193 (what (nth 1 action))
927 (cmdmarks (nth 2 action)) 1194 (cmdmarks (nth 2 action))
928 marks) 1195 marks)
1196 ;; bookmark can't be stored (not list/range
1197 (setq cmdmarks (delq 'bookmark cmdmarks))
1198 ;; killed can't be stored (not list/range
1199 (setq cmdmarks (delq 'killed cmdmarks))
1200 ;; unsent are for nndraft groups only
1201 (setq cmdmarks (delq 'unsent cmdmarks))
929 ;; cache flags are pointless on the server 1202 ;; cache flags are pointless on the server
930 (setq cmdmarks (delq 'cache cmdmarks)) 1203 (setq cmdmarks (delq 'cache cmdmarks))
931 ;; flag dormant articles as ticked 1204 ;; seen flags are local to each gnus
932 (if (memq 'dormant cmdmarks) 1205 (setq cmdmarks (delq 'seen cmdmarks))
933 (setq cmdmarks (cons 'tick cmdmarks))) 1206 ;; recent marks can't be set
1207 (setq cmdmarks (delq 'recent cmdmarks))
1208 (when nnimap-importantize-dormant
1209 ;; flag dormant articles as ticked
1210 (if (memq 'dormant cmdmarks)
1211 (setq cmdmarks (cons 'tick cmdmarks))))
934 ;; remove stuff we are forbidden to store 1212 ;; remove stuff we are forbidden to store
935 (mapcar (lambda (mark) 1213 (mapcar (lambda (mark)
936 (if (imap-message-flag-permanent-p 1214 (if (imap-message-flag-permanent-p
937 (nnimap-mark-to-flag mark)) 1215 (nnimap-mark-to-flag mark))
938 (setq marks (cons mark marks)))) 1216 (setq marks (cons mark marks))))
952 (nnimap-mark-to-flag marks nil t))))))) 1230 (nnimap-mark-to-flag marks nil t)))))))
953 (gnus-message 7 "nnimap: Setting marks in %s...done" group)))) 1231 (gnus-message 7 "nnimap: Setting marks in %s...done" group))))
954 nil) 1232 nil)
955 1233
956 (defun nnimap-split-fancy () 1234 (defun nnimap-split-fancy ()
957 "Like nnmail-split-fancy, but uses nnimap-split-fancy." 1235 "Like the function `nnmail-split-fancy', but uses `nnimap-split-fancy'."
958 (let ((nnmail-split-fancy nnimap-split-fancy)) 1236 (let ((nnmail-split-fancy nnimap-split-fancy))
959 (nnmail-split-fancy))) 1237 (nnmail-split-fancy)))
960 1238
961 (defun nnimap-split-to-groups (rules) 1239 (defun nnimap-split-to-groups (rules)
962 ;; tries to match all rules in nnimap-split-rule against content of 1240 ;; tries to match all rules in nnimap-split-rule against content of
974 (let ((group (car rule)) 1252 (let ((group (car rule))
975 (regexp (cadr rule))) 1253 (regexp (cadr rule)))
976 (goto-char (point-min)) 1254 (goto-char (point-min))
977 (when (and (if (stringp regexp) 1255 (when (and (if (stringp regexp)
978 (progn 1256 (progn
979 (setq regrepp (string-match "\\\\[0-9&]" group)) 1257 (if (not (stringp group))
1258 (setq group (eval group))
1259 (setq regrepp
1260 (string-match "\\\\[0-9&]" group)))
980 (re-search-forward regexp nil t)) 1261 (re-search-forward regexp nil t))
981 (funcall regexp group)) 1262 (funcall regexp group))
982 ;; Don't enter the article into the same group twice. 1263 ;; Don't enter the article into the same group twice.
983 (not (assoc group to-groups))) 1264 (not (assoc group to-groups)))
984 (push (if regrepp 1265 (push (if regrepp
996 (setq alist (cdr alist))) 1277 (setq alist (cdr alist)))
997 element)) 1278 element))
998 1279
999 (defun nnimap-split-find-rule (server inbox) 1280 (defun nnimap-split-find-rule (server inbox)
1000 (if (and (listp nnimap-split-rule) (listp (car nnimap-split-rule)) 1281 (if (and (listp nnimap-split-rule) (listp (car nnimap-split-rule))
1001 (list (cdar nnimap-split-rule)) (listp (cadar nnimap-split-rule))) 1282 (list (cdar nnimap-split-rule)) (listp (cadar nnimap-split-rule)))
1002 ;; extended format 1283 ;; extended format
1003 (cadr (nnimap-assoc-match inbox (cdr (nnimap-assoc-match 1284 (cadr (nnimap-assoc-match inbox (cdr (nnimap-assoc-match
1004 server nnimap-split-rule)))) 1285 server nnimap-split-rule))))
1005 nnimap-split-rule)) 1286 nnimap-split-rule))
1006 1287
1013 (when (nnimap-possibly-change-server server) 1294 (when (nnimap-possibly-change-server server)
1014 (with-current-buffer nnimap-server-buffer 1295 (with-current-buffer nnimap-server-buffer
1015 (let (rule inbox removeorig (inboxes (nnimap-split-find-inbox server))) 1296 (let (rule inbox removeorig (inboxes (nnimap-split-find-inbox server)))
1016 ;; iterate over inboxes 1297 ;; iterate over inboxes
1017 (while (and (setq inbox (pop inboxes)) 1298 (while (and (setq inbox (pop inboxes))
1018 (nnimap-possibly-change-group inbox));; SELECT 1299 (nnimap-possibly-change-group inbox)) ;; SELECT
1019 ;; find split rule for this server / inbox 1300 ;; find split rule for this server / inbox
1020 (when (setq rule (nnimap-split-find-rule server inbox)) 1301 (when (setq rule (nnimap-split-find-rule server inbox))
1021 ;; iterate over articles 1302 ;; iterate over articles
1022 (dolist (article (imap-search nnimap-split-predicate)) 1303 (dolist (article (imap-search nnimap-split-predicate))
1023 (when (nnimap-request-head article) 1304 (when (if (if (eq nnimap-split-download-body 'default)
1305 nnimap-split-download-body-default
1306 nnimap-split-download-body)
1307 (and (nnimap-request-article article)
1308 (with-current-buffer nntp-server-buffer (mail-narrow-to-head)))
1309 (nnimap-request-head article))
1024 ;; copy article to right group(s) 1310 ;; copy article to right group(s)
1025 (setq removeorig nil) 1311 (setq removeorig nil)
1026 (dolist (to-group (nnimap-split-to-groups rule)) 1312 (dolist (to-group (nnimap-split-to-groups rule))
1027 (if (imap-message-copy (number-to-string article) 1313 (cond ((eq to-group 'junk)
1028 to-group nil 'nocopyuid) 1314 (message "IMAP split removed %s:%s:%d" server inbox
1029 (progn 1315 article)
1030 (message "IMAP split moved %s:%s:%d to %s" server inbox 1316 (setq removeorig t))
1031 article to-group) 1317 ((imap-message-copy (number-to-string article)
1032 (setq removeorig t) 1318 to-group nil 'nocopyuid)
1033 ;; Add the group-art list to the history list. 1319 (message "IMAP split moved %s:%s:%d to %s" server
1034 (push (list (cons to-group 0)) nnmail-split-history)) 1320 inbox article to-group)
1035 (message "IMAP split failed to move %s:%s:%d to %s" server 1321 (setq removeorig t)
1036 inbox article to-group))) 1322 (when nnmail-cache-accepted-message-ids
1323 (with-current-buffer nntp-server-buffer
1324 (let (msgid)
1325 (and (setq msgid
1326 (nnmail-fetch-field "message-id"))
1327 (nnmail-cache-insert msgid
1328 to-group
1329 (nnmail-fetch-field "subject"))))))
1330 ;; Add the group-art list to the history list.
1331 (push (list (cons to-group 0)) nnmail-split-history))
1332 (t
1333 (message "IMAP split failed to move %s:%s:%d to %s"
1334 server inbox article to-group))))
1335 (if (if (eq nnimap-split-download-body 'default)
1336 nnimap-split-download-body-default
1337 nnimap-split-download-body)
1338 (widen))
1037 ;; remove article if it was successfully copied somewhere 1339 ;; remove article if it was successfully copied somewhere
1038 (and removeorig 1340 (and removeorig
1039 (imap-message-flags-add (format "%d" article) 1341 (imap-message-flags-add (format "%d" article)
1040 "\\Seen \\Deleted"))))) 1342 "\\Seen \\Deleted")))))
1041 (when (imap-mailbox-select inbox);; just in case 1343 (when (imap-mailbox-select inbox) ;; just in case
1042 ;; todo: UID EXPUNGE (if available) to remove splitted articles 1344 ;; todo: UID EXPUNGE (if available) to remove splitted articles
1043 (imap-mailbox-expunge) 1345 (imap-mailbox-expunge)
1044 (imap-mailbox-close))) 1346 (imap-mailbox-close)))
1347 (when nnmail-cache-accepted-message-ids
1348 (nnmail-cache-close))
1045 t)))) 1349 t))))
1046 1350
1047 (deffoo nnimap-request-scan (&optional group server) 1351 (deffoo nnimap-request-scan (&optional group server)
1048 (nnimap-split-articles group server)) 1352 (nnimap-split-articles group server))
1049 1353
1054 (if (> (length server) 0) " on " "") server) 1358 (if (> (length server) 0) " on " "") server)
1055 (erase-buffer) 1359 (erase-buffer)
1056 (nnimap-before-find-minmax-bugworkaround) 1360 (nnimap-before-find-minmax-bugworkaround)
1057 (dolist (pattern (nnimap-pattern-to-list-arguments 1361 (dolist (pattern (nnimap-pattern-to-list-arguments
1058 nnimap-list-pattern)) 1362 nnimap-list-pattern))
1059 (dolist (mbx (imap-mailbox-lsub "*" (car pattern) nil 1363 (dolist (mbx (imap-mailbox-lsub (cdr pattern) (car pattern) nil
1060 nnimap-server-buffer)) 1364 nnimap-server-buffer))
1061 (or (catch 'found 1365 (or (catch 'found
1062 (dolist (mailbox (imap-mailbox-get 'list-flags mbx 1366 (dolist (mailbox (imap-mailbox-get 'list-flags mbx
1063 nnimap-server-buffer)) 1367 nnimap-server-buffer))
1064 (if (string= (downcase mailbox) "\\noselect") 1368 (if (string= (downcase mailbox) "\\noselect")
1065 (throw 'found t))) 1369 (throw 'found t)))
1066 nil) 1370 nil)
1067 (let ((info (nnimap-find-minmax-uid mbx 'examine))) 1371 (let ((info (nnimap-find-minmax-uid mbx 'examine)))
1068 (when info 1372 (when info
1069 (insert (format "\"%s\" %d %d y\n" 1373 (insert (format "\"%s\" %d %d y\n"
1070 mbx (or (nth 2 info) 0) 1374 mbx (or (nth 2 info) 0)
1071 (max 1 (or (nth 1 info) 1))))))))) 1375 (max 1 (or (nth 1 info) 1)))))))))
1072 (gnus-message 5 "nnimap: Listing subscribed mailboxes%s%s...done" 1376 (gnus-message 5 "nnimap: Listing subscribed mailboxes%s%s...done"
1073 (if (> (length server) 0) " on " "") server)) 1377 (if (> (length server) 0) " on " "") server))
1074 t)) 1378 t))
1075 1379
1076 (deffoo nnimap-request-create-group (group &optional server args) 1380 (deffoo nnimap-request-create-group (group &optional server args)
1077 (when (nnimap-possibly-change-server server) 1381 (when (nnimap-possibly-change-server server)
1078 (or (imap-mailbox-status group 'uidvalidity nnimap-server-buffer) 1382 (or (imap-mailbox-status group 'uidvalidity nnimap-server-buffer)
1079 (imap-mailbox-create group nnimap-server-buffer)))) 1383 (imap-mailbox-create group nnimap-server-buffer)
1384 (nnheader-report 'nnimap "%S"
1385 (imap-error-text nnimap-server-buffer)))))
1080 1386
1081 (defun nnimap-time-substract (time1 time2) 1387 (defun nnimap-time-substract (time1 time2)
1082 "Return TIME for TIME1 - TIME2." 1388 "Return TIME for TIME1 - TIME2."
1083 (let* ((ms (- (car time1) (car time2))) 1389 (let* ((ms (- (car time1) (car time2)))
1084 (ls (- (nth 1 time1) (nth 1 time2)))) 1390 (ls (- (nth 1 time1) (nth 1 time2))))
1085 (if (< ls 0) 1391 (if (< ls 0)
1086 (list (- ms 1) (+ (expt 2 16) ls)) 1392 (list (- ms 1) (+ (expt 2 16) ls))
1087 (list ms ls)))) 1393 (list ms ls))))
1088 1394
1395 (eval-when-compile (require 'parse-time))
1089 (defun nnimap-date-days-ago (daysago) 1396 (defun nnimap-date-days-ago (daysago)
1090 "Return date, in format \"3-Aug-1998\", for DAYSAGO days ago." 1397 "Return date, in format \"3-Aug-1998\", for DAYSAGO days ago."
1398 (require 'parse-time)
1091 (let* ((time (nnimap-time-substract (current-time) (days-to-time daysago))) 1399 (let* ((time (nnimap-time-substract (current-time) (days-to-time daysago)))
1092 (date (format-time-string 1400 (date (format-time-string
1093 (format "%%d-%s-%%Y" 1401 (format "%%d-%s-%%Y"
1094 (capitalize (car (rassoc (nth 4 (decode-time time)) 1402 (capitalize (car (rassoc (nth 4 (decode-time time))
1095 parse-time-months)))) 1403 parse-time-months))))
1100 1408
1101 (defun nnimap-request-expire-articles-progress () 1409 (defun nnimap-request-expire-articles-progress ()
1102 (gnus-message 5 "nnimap: Marking article %d for deletion..." 1410 (gnus-message 5 "nnimap: Marking article %d for deletion..."
1103 imap-current-message)) 1411 imap-current-message))
1104 1412
1413 (defun nnimap-expiry-target (arts group server)
1414 (unless (eq nnmail-expiry-target 'delete)
1415 (with-temp-buffer
1416 (dolist (art arts)
1417 (nnimap-request-article art group server (current-buffer))
1418 ;; hints for optimization in `nnimap-request-accept-article'
1419 (let ((nnimap-current-move-article art)
1420 (nnimap-current-move-group group)
1421 (nnimap-current-move-server server))
1422 (nnmail-expiry-target-group nnmail-expiry-target group))))
1423 ;; It is not clear if `nnmail-expiry-target' somehow cause the
1424 ;; current group to be changed or not, so we make sure here.
1425 (nnimap-possibly-change-group group server)))
1426
1105 ;; Notice that we don't actually delete anything, we just mark them deleted. 1427 ;; Notice that we don't actually delete anything, we just mark them deleted.
1106 (deffoo nnimap-request-expire-articles (articles group &optional server force) 1428 (deffoo nnimap-request-expire-articles (articles group &optional server force)
1107 (let ((artseq (gnus-compress-sequence articles))) 1429 (let ((artseq (gnus-compress-sequence articles)))
1108 (when (and artseq (nnimap-possibly-change-group group server)) 1430 (when (and artseq (nnimap-possibly-change-group group server))
1109 (with-current-buffer nnimap-server-buffer 1431 (with-current-buffer nnimap-server-buffer
1110 (if force 1432 (let ((days (or (and nnmail-expiry-wait-function
1111 (and (imap-message-flags-add 1433 (funcall nnmail-expiry-wait-function group))
1112 (imap-range-to-message-set artseq) "\\Deleted") 1434 nnmail-expiry-wait)))
1113 (setq articles nil)) 1435 (cond ((or force (eq days 'immediate))
1114 (let ((days (or (and nnmail-expiry-wait-function 1436 (let ((oldarts (imap-search
1115 (funcall nnmail-expiry-wait-function group)) 1437 (concat "UID "
1116 nnmail-expiry-wait))) 1438 (imap-range-to-message-set artseq)))))
1117 (cond ((eq days 'immediate) 1439 (when oldarts
1118 (and (imap-message-flags-add 1440 (nnimap-expiry-target oldarts group server)
1119 (imap-range-to-message-set artseq) "\\Deleted") 1441 (when (imap-message-flags-add
1120 (setq articles nil))) 1442 (imap-range-to-message-set
1121 ((numberp days) 1443 (gnus-compress-sequence oldarts)) "\\Deleted")
1122 (let ((oldarts (imap-search 1444 (setq articles (gnus-set-difference
1123 (format "UID %s NOT SINCE %s" 1445 articles oldarts))))))
1124 (imap-range-to-message-set artseq) 1446 ((and nnimap-search-uids-not-since-is-evil (numberp days))
1125 (nnimap-date-days-ago days)))) 1447 (let* ((all-new-articles
1126 (imap-fetch-data-hook 1448 (gnus-compress-sequence
1127 '(nnimap-request-expire-articles-progress))) 1449 (imap-search (format "SINCE %s"
1128 (and oldarts 1450 (nnimap-date-days-ago days)))))
1129 (imap-message-flags-add 1451 (oldartseq
1130 (imap-range-to-message-set 1452 (gnus-range-difference artseq all-new-articles))
1131 (gnus-compress-sequence oldarts)) 1453 (oldarts (gnus-uncompress-range oldartseq)))
1132 "\\Deleted") 1454 (when oldarts
1133 (setq articles (gnus-set-difference 1455 (nnimap-expiry-target oldarts group server)
1134 articles oldarts))))))))))) 1456 (when (imap-message-flags-add
1457 (imap-range-to-message-set oldartseq)
1458 "\\Deleted")
1459 (setq articles (gnus-set-difference
1460 articles oldarts))))))
1461 ((numberp days)
1462 (let ((oldarts (imap-search
1463 (format nnimap-expunge-search-string
1464 (imap-range-to-message-set artseq)
1465 (nnimap-date-days-ago days))))
1466 (imap-fetch-data-hook
1467 '(nnimap-request-expire-articles-progress)))
1468 (when oldarts
1469 (nnimap-expiry-target oldarts group server)
1470 (when (imap-message-flags-add
1471 (imap-range-to-message-set
1472 (gnus-compress-sequence oldarts)) "\\Deleted")
1473 (setq articles (gnus-set-difference
1474 articles oldarts)))))))))))
1135 ;; return articles not deleted 1475 ;; return articles not deleted
1136 articles) 1476 articles)
1137 1477
1138 (deffoo nnimap-request-move-article (article group server 1478 (deffoo nnimap-request-move-article (article group server
1139 accept-form &optional last) 1479 accept-form &optional last)
1150 (buffer-disable-undo (current-buffer)) 1490 (buffer-disable-undo (current-buffer))
1151 (insert-buffer-substring nntp-server-buffer) 1491 (insert-buffer-substring nntp-server-buffer)
1152 (setq result (eval accept-form)) 1492 (setq result (eval accept-form))
1153 (kill-buffer buf) 1493 (kill-buffer buf)
1154 result) 1494 result)
1155 (nnimap-request-expire-articles (list article) group server t)) 1495 (imap-message-flags-add
1496 (imap-range-to-message-set (list article))
1497 "\\Deleted" 'silent nnimap-server-buffer))
1156 result)))) 1498 result))))
1157 1499
1158 (deffoo nnimap-request-accept-article (group &optional server last) 1500 (deffoo nnimap-request-accept-article (group &optional server last)
1159 (when (nnimap-possibly-change-server server) 1501 (when (nnimap-possibly-change-server server)
1160 (let (uid) 1502 (let (uid)
1170 (with-current-buffer (current-buffer) 1512 (with-current-buffer (current-buffer)
1171 (goto-char (point-min)) 1513 (goto-char (point-min))
1172 ;; remove any 'From blabla' lines, some IMAP servers 1514 ;; remove any 'From blabla' lines, some IMAP servers
1173 ;; reject the entire message otherwise. 1515 ;; reject the entire message otherwise.
1174 (when (looking-at "^From[^:]") 1516 (when (looking-at "^From[^:]")
1175 (kill-region (point) (progn (forward-line) (point)))) 1517 (delete-region (point) (progn (forward-line) (point))))
1176 ;; turn into rfc822 format (\r\n eol's) 1518 ;; turn into rfc822 format (\r\n eol's)
1177 (while (search-forward "\n" nil t) 1519 (while (search-forward "\n" nil t)
1178 (replace-match "\r\n"))) 1520 (replace-match "\r\n"))
1179 ;; this 'or' is for Cyrus server bug 1521 (when nnmail-cache-accepted-message-ids
1180 (or (null (imap-current-mailbox nnimap-server-buffer)) 1522 (nnmail-cache-insert (nnmail-fetch-field "message-id")
1181 (imap-mailbox-unselect nnimap-server-buffer)) 1523 group
1524 (nnmail-fetch-field "subject"))))
1525 (when (and last nnmail-cache-accepted-message-ids)
1526 (nnmail-cache-close))
1527 ;; this 'or' is for Cyrus server bug
1528 (or (null (imap-current-mailbox nnimap-server-buffer))
1529 (imap-mailbox-unselect nnimap-server-buffer))
1182 (imap-message-append group (current-buffer) nil nil 1530 (imap-message-append group (current-buffer) nil nil
1183 nnimap-server-buffer))) 1531 nnimap-server-buffer)))
1184 (cons group (nth 1 uid)) 1532 (cons group (nth 1 uid))
1185 (nnheader-report 'nnimap (imap-error-text nnimap-server-buffer)))))) 1533 (nnheader-report 'nnimap (imap-error-text nnimap-server-buffer))))))
1186 1534
1197 (when (nnimap-possibly-change-server server) 1545 (when (nnimap-possibly-change-server server)
1198 (imap-mailbox-rename group new-name nnimap-server-buffer))) 1546 (imap-mailbox-rename group new-name nnimap-server-buffer)))
1199 1547
1200 (defun nnimap-expunge (mailbox server) 1548 (defun nnimap-expunge (mailbox server)
1201 (when (nnimap-possibly-change-group mailbox server) 1549 (when (nnimap-possibly-change-group mailbox server)
1202 (imap-mailbox-expunge nnimap-server-buffer))) 1550 (imap-mailbox-expunge nil nnimap-server-buffer)))
1203 1551
1204 (defun nnimap-acl-get (mailbox server) 1552 (defun nnimap-acl-get (mailbox server)
1205 (when (nnimap-possibly-change-server server) 1553 (when (nnimap-possibly-change-server server)
1206 (and (imap-capability 'ACL nnimap-server-buffer) 1554 (and (imap-capability 'ACL nnimap-server-buffer)
1207 (imap-mailbox-acl-get mailbox nnimap-server-buffer)))) 1555 (imap-mailbox-acl-get mailbox nnimap-server-buffer))))
1245 1593
1246 (defconst nnimap-mark-to-predicate-alist 1594 (defconst nnimap-mark-to-predicate-alist
1247 (mapcar 1595 (mapcar
1248 (lambda (pair) ; cdr is the mark 1596 (lambda (pair) ; cdr is the mark
1249 (or (assoc (cdr pair) 1597 (or (assoc (cdr pair)
1250 '((read . "SEEN") 1598 '((read . "SEEN")
1251 (tick . "FLAGGED") 1599 (tick . "FLAGGED")
1252 (draft . "DRAFT") 1600 (draft . "DRAFT")
1253 (reply . "ANSWERED"))) 1601 (recent . "RECENT")
1254 (cons (cdr pair) 1602 (reply . "ANSWERED")))
1255 (format "KEYWORD gnus-%s" (symbol-name (cdr pair)))))) 1603 (cons (cdr pair)
1604 (format "KEYWORD gnus-%s" (symbol-name (cdr pair))))))
1256 (cons '(read . read) gnus-article-mark-lists))) 1605 (cons '(read . read) gnus-article-mark-lists)))
1257 1606
1258 (defun nnimap-mark-to-predicate (pred) 1607 (defun nnimap-mark-to-predicate (pred)
1259 "Convert a Gnus mark (a symbol such as read, tick, expire) to a IMAP predicate. 1608 "Convert a Gnus mark (a symbol such as read, tick, expire) to a IMAP predicate.
1260 This is a string such as \"SEEN\", \"FLAGGED\", \"KEYWORD gnus-expire\", 1609 This is a string such as \"SEEN\", \"FLAGGED\", \"KEYWORD gnus-expire\",
1263 1612
1264 (defconst nnimap-mark-to-flag-alist 1613 (defconst nnimap-mark-to-flag-alist
1265 (mapcar 1614 (mapcar
1266 (lambda (pair) 1615 (lambda (pair)
1267 (or (assoc (cdr pair) 1616 (or (assoc (cdr pair)
1268 '((read . "\\Seen") 1617 '((read . "\\Seen")
1269 (tick . "\\Flagged") 1618 (tick . "\\Flagged")
1270 (draft . "\\Draft") 1619 (draft . "\\Draft")
1271 (reply . "\\Answered"))) 1620 (recent . "\\Recent")
1272 (cons (cdr pair) 1621 (reply . "\\Answered")))
1273 (format "gnus-%s" (symbol-name (cdr pair)))))) 1622 (cons (cdr pair)
1623 (format "gnus-%s" (symbol-name (cdr pair))))))
1274 (cons '(read . read) gnus-article-mark-lists))) 1624 (cons '(read . read) gnus-article-mark-lists)))
1275 1625
1276 (defun nnimap-mark-to-flag-1 (preds) 1626 (defun nnimap-mark-to-flag-1 (preds)
1277 (if (and (not (null preds)) (listp preds)) 1627 (if (and (not (null preds)) (listp preds))
1278 (cons (nnimap-mark-to-flag (car preds)) 1628 (cons (nnimap-mark-to-flag (car preds))
1298 1648
1299 (defun nnimap-mark-permanent-p (mark &optional group) 1649 (defun nnimap-mark-permanent-p (mark &optional group)
1300 "Return t iff MARK can be permanently (between IMAP sessions) saved on articles, in GROUP." 1650 "Return t iff MARK can be permanently (between IMAP sessions) saved on articles, in GROUP."
1301 (imap-message-flag-permanent-p (nnimap-mark-to-flag mark))) 1651 (imap-message-flag-permanent-p (nnimap-mark-to-flag mark)))
1302 1652
1303 (defun nnimap-remassoc (key alist)
1304 "Delete by side effect any elements of LIST whose car is `equal' to KEY.
1305 The modified LIST is returned. If the first member
1306 of LIST has a car that is `equal' to KEY, there is no way to remove it
1307 by side effect; therefore, write `(setq foo (remassoc key foo))' to be
1308 sure of changing the value of `foo'."
1309 (when alist
1310 (if (equal key (caar alist))
1311 (cdr alist)
1312 (setcdr alist (nnimap-remassoc key (cdr alist)))
1313 alist)))
1314
1315 (defun nnimap-update-alist-soft (key value alist)
1316 (if value
1317 (cons (cons key value) (nnimap-remassoc key alist))
1318 (nnimap-remassoc key alist)))
1319
1320 (when nnimap-debug 1653 (when nnimap-debug
1321 (require 'trace) 1654 (require 'trace)
1322 (buffer-disable-undo (get-buffer-create nnimap-debug)) 1655 (buffer-disable-undo (get-buffer-create nnimap-debug-buffer))
1323 (mapcar (lambda (f) (trace-function-background f nnimap-debug)) 1656 (mapcar (lambda (f) (trace-function-background f nnimap-debug-buffer))
1324 '( 1657 '(
1325 nnimap-possibly-change-server 1658 nnimap-possibly-change-server
1326 nnimap-verify-uidvalidity 1659 nnimap-verify-uidvalidity
1327 nnimap-find-minmax-uid 1660 nnimap-find-minmax-uid
1328 nnimap-before-find-minmax-bugworkaround 1661 nnimap-before-find-minmax-bugworkaround
1329 nnimap-possibly-change-group 1662 nnimap-possibly-change-group
1330 ;;nnimap-replace-whitespace 1663 ;;nnimap-replace-whitespace
1331 nnimap-retrieve-headers-progress 1664 nnimap-retrieve-headers-progress
1332 nnimap-retrieve-which-headers 1665 nnimap-retrieve-which-headers
1333 nnimap-group-overview-filename 1666 nnimap-group-overview-filename
1334 nnimap-retrieve-headers-from-file 1667 nnimap-retrieve-headers-from-file
1335 nnimap-retrieve-headers-from-server 1668 nnimap-retrieve-headers-from-server
1336 nnimap-retrieve-headers 1669 nnimap-retrieve-headers
1337 nnimap-open-connection 1670 nnimap-open-connection
1338 nnimap-open-server 1671 nnimap-open-server
1339 nnimap-server-opened 1672 nnimap-server-opened
1340 nnimap-close-server 1673 nnimap-close-server
1341 nnimap-request-close 1674 nnimap-request-close
1342 nnimap-status-message 1675 nnimap-status-message
1343 ;;nnimap-demule 1676 ;;nnimap-demule
1344 nnimap-request-article-part 1677 nnimap-request-article-part
1345 nnimap-request-article 1678 nnimap-request-article
1346 nnimap-request-head 1679 nnimap-request-head
1347 nnimap-request-body 1680 nnimap-request-body
1348 nnimap-request-group 1681 nnimap-request-group
1349 nnimap-close-group 1682 nnimap-close-group
1350 nnimap-pattern-to-list-arguments 1683 nnimap-pattern-to-list-arguments
1351 nnimap-request-list 1684 nnimap-request-list
1352 nnimap-request-post 1685 nnimap-request-post
1353 nnimap-retrieve-groups 1686 nnimap-retrieve-groups
1354 nnimap-request-update-info-internal 1687 nnimap-request-update-info-internal
1355 nnimap-request-type 1688 nnimap-request-type
1356 nnimap-request-set-mark 1689 nnimap-request-set-mark
1357 nnimap-split-to-groups 1690 nnimap-split-to-groups
1358 nnimap-split-find-rule 1691 nnimap-split-find-rule
1359 nnimap-split-find-inbox 1692 nnimap-split-find-inbox
1360 nnimap-split-articles 1693 nnimap-split-articles
1361 nnimap-request-scan 1694 nnimap-request-scan
1362 nnimap-request-newgroups 1695 nnimap-request-newgroups
1363 nnimap-request-create-group 1696 nnimap-request-create-group
1364 nnimap-time-substract 1697 nnimap-time-substract
1365 nnimap-date-days-ago 1698 nnimap-date-days-ago
1366 nnimap-request-expire-articles-progress 1699 nnimap-request-expire-articles-progress
1367 nnimap-request-expire-articles 1700 nnimap-request-expire-articles
1368 nnimap-request-move-article 1701 nnimap-request-move-article
1369 nnimap-request-accept-article 1702 nnimap-request-accept-article
1370 nnimap-request-delete-group 1703 nnimap-request-delete-group
1371 nnimap-request-rename-group 1704 nnimap-request-rename-group
1372 gnus-group-nnimap-expunge 1705 gnus-group-nnimap-expunge
1373 gnus-group-nnimap-edit-acl 1706 gnus-group-nnimap-edit-acl
1374 gnus-group-nnimap-edit-acl-done 1707 gnus-group-nnimap-edit-acl-done
1375 nnimap-group-mode-hook 1708 nnimap-group-mode-hook
1376 nnimap-mark-to-predicate 1709 nnimap-mark-to-predicate
1377 nnimap-mark-to-flag-1 1710 nnimap-mark-to-flag-1
1378 nnimap-mark-to-flag 1711 nnimap-mark-to-flag
1379 nnimap-mark-permanent-p 1712 nnimap-mark-permanent-p
1380 nnimap-remassoc 1713 )))
1381 nnimap-update-alist-soft
1382 )))
1383 1714
1384 (provide 'nnimap) 1715 (provide 'nnimap)
1385 1716
1717 ;; arch-tag: 2b001f20-3ff9-4094-a0ad-46807c1ba70b
1386 ;;; nnimap.el ends here 1718 ;;; nnimap.el ends here