e5936e8a51b4a532c22d9c3501b2ca8cb91824a6
[gnus] / lisp / imap.el
1 ;;; imap.el --- imap library
2 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
3 ;;        Free Software Foundation, Inc.
4
5 ;; Author: Simon Josefsson <jas@pdc.kth.se>
6 ;; Keywords: mail
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; 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
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;; imap.el is a elisp library providing an interface for talking to
28 ;; IMAP servers.
29 ;;
30 ;; imap.el is roughly divided in two parts, one that parses IMAP
31 ;; responses from the server and storing data into buffer-local
32 ;; variables, and one for utility functions which send commands to
33 ;; server, waits for an answer, and return information.  The latter
34 ;; part is layered on top of the previous.
35 ;;
36 ;; The imap.el API consist of the following functions, other functions
37 ;; in this file should not be called directly and the result of doing
38 ;; so are at best undefined.
39 ;;
40 ;; Global commands:
41 ;;
42 ;; imap-open,       imap-opened,    imap-authenticate, imap-close,
43 ;; imap-capability, imap-namespace, imap-error-text
44 ;;
45 ;; Mailbox commands:
46 ;;
47 ;; imap-mailbox-get,       imap-mailbox-map,         imap-current-mailbox,
48 ;; imap-current-mailbox-p, imap-search,              imap-mailbox-select,
49 ;; imap-mailbox-examine,   imap-mailbox-unselect,    imap-mailbox-expunge
50 ;; imap-mailbox-close,     imap-mailbox-create,      imap-mailbox-delete
51 ;; imap-mailbox-rename,    imap-mailbox-lsub,        imap-mailbox-list
52 ;; imap-mailbox-subscribe, imap-mailbox-unsubscribe, imap-mailbox-status
53 ;; imap-mailbox-acl-get,   imap-mailbox-acl-set,     imap-mailbox-acl-delete
54 ;;
55 ;; Message commands:
56 ;;
57 ;; imap-fetch-asynch,                 imap-fetch,
58 ;; imap-current-message,              imap-list-to-message-set,
59 ;; imap-message-get,                  imap-message-map
60 ;; imap-message-envelope-date,        imap-message-envelope-subject,
61 ;; imap-message-envelope-from,        imap-message-envelope-sender,
62 ;; imap-message-envelope-reply-to,    imap-message-envelope-to,
63 ;; imap-message-envelope-cc,          imap-message-envelope-bcc
64 ;; imap-message-envelope-in-reply-to, imap-message-envelope-message-id
65 ;; imap-message-body,                 imap-message-flag-permanent-p
66 ;; imap-message-flags-set,            imap-message-flags-del
67 ;; imap-message-flags-add,            imap-message-copyuid
68 ;; imap-message-copy,                 imap-message-appenduid
69 ;; imap-message-append,               imap-envelope-from
70 ;; imap-body-lines
71 ;;
72 ;; It is my hope that these commands should be pretty self
73 ;; explanatory for someone that know IMAP.  All functions have
74 ;; additional documentation on how to invoke them.
75 ;;
76 ;; imap.el support RFC1730/2060/RFC3501 (IMAP4/IMAP4rev1), implemented
77 ;; IMAP extensions are RFC2195 (CRAM-MD5), RFC2086 (ACL), RFC2342
78 ;; (NAMESPACE), RFC2359 (UIDPLUS), the IMAP-part of RFC2595 (STARTTLS,
79 ;; LOGINDISABLED) (with use of external library starttls.el and
80 ;; program starttls), and the GSSAPI / kerberos V4 sections of RFC1731
81 ;; (with use of external program `imtest'), RFC2971 (ID).  It also
82 ;; take advantage the UNSELECT extension in Cyrus IMAPD.
83 ;;
84 ;; Without the work of John McClary Prevost and Jim Radford this library
85 ;; would not have seen the light of day.  Many thanks.
86 ;;
87 ;; This is a transcript of short interactive session for demonstration
88 ;; purposes.
89 ;;
90 ;; (imap-open "my.mail.server")
91 ;; => " *imap* my.mail.server:0"
92 ;;
93 ;; The rest are invoked with current buffer as the buffer returned by
94 ;; `imap-open'.  It is possible to do all without this, but it would
95 ;; look ugly here since `buffer' is always the last argument for all
96 ;; imap.el API functions.
97 ;;
98 ;; (imap-authenticate "myusername" "mypassword")
99 ;; => auth
100 ;;
101 ;; (imap-mailbox-lsub "*")
102 ;; => ("INBOX.sentmail" "INBOX.private" "INBOX.draft" "INBOX.spam")
103 ;;
104 ;; (imap-mailbox-list "INBOX.n%")
105 ;; => ("INBOX.namedroppers" "INBOX.nnimap" "INBOX.ntbugtraq")
106 ;;
107 ;; (imap-mailbox-select "INBOX.nnimap")
108 ;; => "INBOX.nnimap"
109 ;;
110 ;; (imap-mailbox-get 'exists)
111 ;; => 166
112 ;;
113 ;; (imap-mailbox-get 'uidvalidity)
114 ;; => "908992622"
115 ;;
116 ;; (imap-search "FLAGGED SINCE 18-DEC-98")
117 ;; => (235 236)
118 ;;
119 ;; (imap-fetch 235 "RFC822.PEEK" 'RFC822)
120 ;; => "X-Sieve: cmu-sieve 1.3^M\nX-Username: <jas@pdc.kth.se>^M\r...."
121 ;;
122 ;; Todo:
123 ;;
124 ;; o Parse UIDs as strings? We need to overcome the 28 bit limit somehow.
125 ;; o Don't use `read' at all (important places already fixed)
126 ;; o Accept list of articles instead of message set string in most
127 ;;   imap-message-* functions.
128 ;; o Send strings as literal if they contain, e.g., ".
129 ;;
130 ;; Revision history:
131 ;;
132 ;;  - 19991218 added starttls/digest-md5 patch,
133 ;;             by Daiki Ueno <ueno@ueda.info.waseda.ac.jp>
134 ;;             NB! you need SLIM for starttls.el and digest-md5.el
135 ;;  - 19991023 commited to pgnus
136 ;;
137
138 ;;; Code:
139
140 (eval-when-compile (require 'cl))
141 (eval-and-compile
142   (autoload 'starttls-open-stream "starttls")
143   (autoload 'starttls-negotiate "starttls")
144   (autoload 'sasl-find-mechanism "sasl")
145   (autoload 'digest-md5-parse-digest-challenge "digest-md5")
146   (autoload 'digest-md5-digest-response "digest-md5")
147   (autoload 'digest-md5-digest-uri "digest-md5")
148   (autoload 'digest-md5-challenge "digest-md5")
149   (autoload 'rfc2104-hash "rfc2104")
150   (autoload 'utf7-encode "utf7")
151   (autoload 'utf7-decode "utf7")
152   (autoload 'format-spec "format-spec")
153   (autoload 'format-spec-make "format-spec")
154   (autoload 'open-tls-stream "tls"))
155
156 ;; User variables.
157
158 (defgroup imap nil
159   "Low-level IMAP issues."
160   :version "21.1"
161   :group 'mail)
162
163 (defcustom imap-kerberos4-program '("imtest -m kerberos_v4 -u %l -p %p %s"
164                                     "imtest -kp %s %p")
165   "List of strings containing commands for Kerberos 4 authentication.
166 %s is replaced with server hostname, %p with port to connect to, and
167 %l with the value of `imap-default-user'.  The program should accept
168 IMAP commands on stdin and return responses to stdout.  Each entry in
169 the list is tried until a successful connection is made."
170   :group 'imap
171   :type '(repeat string))
172
173 (defcustom imap-gssapi-program (list
174                                 (concat "gsasl --client --connect %s:%p "
175                                         "--imap --application-data "
176                                         "--mechanism GSSAPI "
177                                         "--authentication-id %l")
178                                 "imtest -m gssapi -u %l -p %p %s")
179   "List of strings containing commands for GSSAPI (krb5) authentication.
180 %s is replaced with server hostname, %p with port to connect to, and
181 %l with the value of `imap-default-user'.  The program should accept
182 IMAP commands on stdin and return responses to stdout.  Each entry in
183 the list is tried until a successful connection is made."
184   :group 'imap
185   :type '(repeat string))
186
187 (defcustom imap-ssl-program '("openssl s_client -quiet -ssl3 -connect %s:%p"
188                               "openssl s_client -quiet -ssl2 -connect %s:%p"
189                               "s_client -quiet -ssl3 -connect %s:%p"
190                               "s_client -quiet -ssl2 -connect %s:%p")
191   "A string, or list of strings, containing commands for SSL connections.
192 Within a string, %s is replaced with the server address and %p with
193 port number on server.  The program should accept IMAP commands on
194 stdin and return responses to stdout.  Each entry in the list is tried
195 until a successful connection is made."
196   :group 'imap
197   :type '(choice string
198                  (repeat string)))
199
200 (defcustom imap-shell-program '("ssh %s imapd"
201                                 "rsh %s imapd"
202                                 "ssh %g ssh %s imapd"
203                                 "rsh %g rsh %s imapd")
204   "A list of strings, containing commands for IMAP connection.
205 Within a string, %s is replaced with the server address, %p with port
206 number on server, %g with `imap-shell-host', and %l with
207 `imap-default-user'.  The program should read IMAP commands from stdin
208 and write IMAP response to stdout. Each entry in the list is tried
209 until a successful connection is made."
210   :group 'imap
211   :type '(repeat string))
212
213 (defcustom imap-process-connection-type nil
214   "*Value for `process-connection-type' to use for Kerberos4, GSSAPI and SSL.
215 The `process-connection-type' variable control type of device
216 used to communicate with subprocesses.  Values are nil to use a
217 pipe, or t or `pty' to use a pty.  The value has no effect if the
218 system has no ptys or if all ptys are busy: then a pipe is used
219 in any case.  The value takes effect when a IMAP server is
220 opened, changing it after that has no effect."
221   :version "22.1"
222   :group 'imap
223   :type 'boolean)
224
225 (defcustom imap-use-utf7 t
226   "If non-nil, do utf7 encoding/decoding of mailbox names.
227 Since the UTF7 decoding currently only decodes into ISO-8859-1
228 characters, you may disable this decoding if you need to access UTF7
229 encoded mailboxes which doesn't translate into ISO-8859-1."
230   :group 'imap
231   :type 'boolean)
232
233 (defcustom imap-log nil
234   "If non-nil, a imap session trace is placed in *imap-log* buffer.
235 Note that username, passwords and other privacy sensitive
236 information (such as e-mail) may be stored in the *imap-log*
237 buffer.  It is not written to disk, however.  Do not enable this
238 variable unless you are comfortable with that."
239   :group 'imap
240   :type 'boolean)
241
242 (defcustom imap-debug nil
243   "If non-nil, random debug spews are placed in *imap-debug* buffer.
244 Note that username, passwords and other privacy sensitive
245 information (such as e-mail) may be stored in the *imap-debug*
246 buffer.  It is not written to disk, however.  Do not enable this
247 variable unless you are comfortable with that."
248   :group 'imap
249   :type 'boolean)
250
251 (defcustom imap-shell-host "gateway"
252   "Hostname of rlogin proxy."
253   :group 'imap
254   :type 'string)
255
256 (defcustom imap-default-user (user-login-name)
257   "Default username to use."
258   :group 'imap
259   :type 'string)
260
261 (defcustom imap-read-timeout (if (string-match
262                                   "windows-nt\\|os/2\\|emx\\|cygwin"
263                                   (symbol-name system-type))
264                                  1.0
265                                0.1)
266   "*How long to wait between checking for the end of output.
267 Shorter values mean quicker response, but is more CPU intensive."
268   :type 'number
269   :group 'imap)
270
271 (defcustom imap-store-password nil
272   "If non-nil, store session password without promting."
273   :group 'imap
274   :type 'boolean)
275
276 ;; Various variables.
277
278 (defvar imap-fetch-data-hook nil
279   "Hooks called after receiving each FETCH response.")
280
281 (defvar imap-streams '(gssapi kerberos4 starttls tls ssl network shell)
282   "Priority of streams to consider when opening connection to server.")
283
284 (defvar imap-stream-alist
285   '((gssapi    imap-gssapi-stream-p    imap-gssapi-open)
286     (kerberos4 imap-kerberos4-stream-p imap-kerberos4-open)
287     (tls       imap-tls-p              imap-tls-open)
288     (ssl       imap-ssl-p              imap-ssl-open)
289     (network   imap-network-p          imap-network-open)
290     (shell     imap-shell-p            imap-shell-open)
291     (starttls  imap-starttls-p         imap-starttls-open))
292   "Definition of network streams.
293
294 \(NAME CHECK OPEN)
295
296 NAME names the stream, CHECK is a function returning non-nil if the
297 server support the stream and OPEN is a function for opening the
298 stream.")
299
300 (defvar imap-authenticators '(gssapi
301                               kerberos4
302                               digest-md5
303                               cram-md5
304                               ;;sasl
305                               login
306                               anonymous)
307   "Priority of authenticators to consider when authenticating to server.")
308
309 (defvar imap-authenticator-alist
310   '((gssapi     imap-gssapi-auth-p    imap-gssapi-auth)
311     (kerberos4  imap-kerberos4-auth-p imap-kerberos4-auth)
312     (sasl       imap-sasl-auth-p      imap-sasl-auth)
313     (cram-md5   imap-cram-md5-p       imap-cram-md5-auth)
314     (login      imap-login-p          imap-login-auth)
315     (anonymous  imap-anonymous-p      imap-anonymous-auth)
316     (digest-md5 imap-digest-md5-p     imap-digest-md5-auth))
317   "Definition of authenticators.
318
319 \(NAME CHECK AUTHENTICATE)
320
321 NAME names the authenticator.  CHECK is a function returning non-nil if
322 the server support the authenticator and AUTHENTICATE is a function
323 for doing the actual authentication.")
324
325 (defvar imap-error nil
326   "Error codes from the last command.")
327
328 ;; Internal constants.  Change these and die.
329
330 (defconst imap-default-port 143)
331 (defconst imap-default-ssl-port 993)
332 (defconst imap-default-tls-port 993)
333 (defconst imap-default-stream 'network)
334 (defconst imap-coding-system-for-read 'binary)
335 (defconst imap-coding-system-for-write 'binary)
336 (defconst imap-local-variables '(imap-server
337                                  imap-port
338                                  imap-client-eol
339                                  imap-server-eol
340                                  imap-auth
341                                  imap-stream
342                                  imap-username
343                                  imap-password
344                                  imap-current-mailbox
345                                  imap-current-target-mailbox
346                                  imap-message-data
347                                  imap-capability
348                                  imap-id
349                                  imap-namespace
350                                  imap-state
351                                  imap-reached-tag
352                                  imap-failed-tags
353                                  imap-tag
354                                  imap-process
355                                  imap-calculate-literal-size-first
356                                  imap-mailbox-data))
357 (defconst imap-log-buffer "*imap-log*")
358 (defconst imap-debug-buffer "*imap-debug*")
359
360 ;; Internal variables.
361
362 (defvar imap-stream nil)
363 (defvar imap-auth nil)
364 (defvar imap-server nil)
365 (defvar imap-port nil)
366 (defvar imap-username nil)
367 (defvar imap-password nil)
368 (defvar imap-calculate-literal-size-first nil)
369 (defvar imap-state 'closed
370   "IMAP state.
371 Valid states are `closed', `initial', `nonauth', `auth', `selected'
372 and `examine'.")
373
374 (defvar imap-server-eol "\r\n"
375   "The EOL string sent from the server.")
376
377 (defvar imap-client-eol "\r\n"
378   "The EOL string we send to the server.")
379
380 (defvar imap-current-mailbox nil
381   "Current mailbox name.")
382
383 (defvar imap-current-target-mailbox nil
384   "Current target mailbox for COPY and APPEND commands.")
385
386 (defvar imap-mailbox-data nil
387   "Obarray with mailbox data.")
388
389 (defvar imap-mailbox-prime 997
390   "Length of imap-mailbox-data.")
391
392 (defvar imap-current-message nil
393   "Current message number.")
394
395 (defvar imap-message-data nil
396   "Obarray with message data.")
397
398 (defvar imap-message-prime 997
399   "Length of imap-message-data.")
400
401 (defvar imap-capability nil
402   "Capability for server.")
403
404 (defvar imap-id nil
405   "Identity of server.
406 See RFC 2971.")
407
408 (defvar imap-namespace nil
409   "Namespace for current server.")
410
411 (defvar imap-reached-tag 0
412   "Lower limit on command tags that have been parsed.")
413
414 (defvar imap-failed-tags nil
415   "Alist of tags that failed.
416 Each element is a list with four elements; tag (a integer), response
417 state (a symbol, `OK', `NO' or `BAD'), response code (a string), and
418 human readable response text (a string).")
419
420 (defvar imap-tag 0
421   "Command tag number.")
422
423 (defvar imap-process nil
424   "Process.")
425
426 (defvar imap-continuation nil
427   "Non-nil indicates that the server emitted a continuation request.
428 The actual value is really the text on the continuation line.")
429
430 (defvar imap-callbacks nil
431   "List of response tags and callbacks, on the form `(number . function)'.
432 The function should take two arguments, the first the IMAP tag and the
433 second the status (OK, NO, BAD etc) of the command.")
434
435 \f
436 ;; Utility functions:
437
438 (defun imap-remassoc (key alist)
439   "Delete by side effect any elements of LIST whose car is `equal' to KEY.
440 The modified LIST is returned.  If the first member
441 of LIST has a car that is `equal' to KEY, there is no way to remove it
442 by side effect; therefore, write `(setq foo (remassoc key foo))' to be
443 sure of changing the value of `foo'."
444   (when alist
445     (if (equal key (caar alist))
446         (cdr alist)
447       (setcdr alist (imap-remassoc key (cdr alist)))
448       alist)))
449
450 (defsubst imap-disable-multibyte ()
451   "Enable multibyte in the current buffer."
452   (when (fboundp 'set-buffer-multibyte)
453     (set-buffer-multibyte nil)))
454
455 (defsubst imap-utf7-encode (string)
456   (if imap-use-utf7
457       (and string
458            (condition-case ()
459                (utf7-encode string t)
460              (error (message
461                      "imap: Could not UTF7 encode `%s', using it unencoded..."
462                      string)
463                     string)))
464     string))
465
466 (defsubst imap-utf7-decode (string)
467   (if imap-use-utf7
468       (and string
469            (condition-case ()
470                (utf7-decode string t)
471              (error (message
472                      "imap: Could not UTF7 decode `%s', using it undecoded..."
473                      string)
474                     string)))
475     string))
476
477 (defsubst imap-ok-p (status)
478   (if (eq status 'OK)
479       t
480     (setq imap-error status)
481     nil))
482
483 (defun imap-error-text (&optional buffer)
484   (with-current-buffer (or buffer (current-buffer))
485     (nth 3 (car imap-failed-tags))))
486
487 \f
488 ;; Server functions; stream stuff:
489
490 (defun imap-kerberos4-stream-p (buffer)
491   (imap-capability 'AUTH=KERBEROS_V4 buffer))
492
493 (defun imap-kerberos4-open (name buffer server port)
494   (let ((cmds imap-kerberos4-program)
495         cmd done)
496     (while (and (not done) (setq cmd (pop cmds)))
497       (message "Opening Kerberos 4 IMAP connection with `%s'..." cmd)
498       (erase-buffer)
499       (let* ((port (or port imap-default-port))
500              (coding-system-for-read imap-coding-system-for-read)
501              (coding-system-for-write imap-coding-system-for-write)
502              (process-connection-type imap-process-connection-type)
503              (process (start-process
504                        name buffer shell-file-name shell-command-switch
505                        (format-spec
506                         cmd
507                         (format-spec-make
508                          ?s server
509                          ?p (number-to-string port)
510                          ?l imap-default-user))))
511              response)
512         (when process
513           (with-current-buffer buffer
514             (setq imap-client-eol "\n"
515                   imap-calculate-literal-size-first t)
516             (while (and (memq (process-status process) '(open run))
517                         (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
518                         (goto-char (point-min))
519                         ;; Athena IMTEST can output SSL verify errors
520                         (or (while (looking-at "^verify error:num=")
521                               (forward-line))
522                             t)
523                         (or (while (looking-at "^TLS connection established")
524                               (forward-line))
525                             t)
526                         ;; cyrus 1.6.x (13? < x <= 22) queries capabilities
527                         (or (while (looking-at "^C:")
528                               (forward-line))
529                             t)
530                         ;; cyrus 1.6 imtest print "S: " before server greeting
531                         (or (not (looking-at "S: "))
532                             (forward-char 3)
533                             t)
534                         (not (and (imap-parse-greeting)
535                                   ;; success in imtest < 1.6:
536                                   (or (re-search-forward
537                                        "^__\\(.*\\)__\n" nil t)
538                                       ;; success in imtest 1.6:
539                                       (re-search-forward
540                                        "^\\(Authenticat.*\\)" nil t))
541                                   (setq response (match-string 1)))))
542               (accept-process-output process 1)
543               (sit-for 1))
544             (and imap-log
545                  (with-current-buffer (get-buffer-create imap-log-buffer)
546                    (imap-disable-multibyte)
547                    (buffer-disable-undo)
548                    (goto-char (point-max))
549                    (insert-buffer-substring buffer)))
550             (erase-buffer)
551             (message "Opening Kerberos 4 IMAP connection with `%s'...%s" cmd
552                      (if response (concat "done, " response) "failed"))
553             (if (and response (let ((case-fold-search nil))
554                                 (not (string-match "failed" response))))
555                 (setq done process)
556               (if (memq (process-status process) '(open run))
557                   (imap-send-command "LOGOUT"))
558               (delete-process process)
559               nil)))))
560     done))
561
562 (defun imap-gssapi-stream-p (buffer)
563   (imap-capability 'AUTH=GSSAPI buffer))
564
565 (defun imap-gssapi-open (name buffer server port)
566   (let ((cmds imap-gssapi-program)
567         cmd done)
568     (while (and (not done) (setq cmd (pop cmds)))
569       (message "Opening GSSAPI IMAP connection with `%s'..." cmd)
570       (erase-buffer)
571       (let* ((port (or port imap-default-port))
572              (coding-system-for-read imap-coding-system-for-read)
573              (coding-system-for-write imap-coding-system-for-write)
574              (process-connection-type imap-process-connection-type)
575              (process (start-process
576                        name buffer shell-file-name shell-command-switch
577                        (format-spec
578                         cmd
579                         (format-spec-make
580                          ?s server
581                          ?p (number-to-string port)
582                          ?l imap-default-user))))
583              response)
584         (when process
585           (with-current-buffer buffer
586             (setq imap-client-eol "\n"
587                   imap-calculate-literal-size-first t)
588             (while (and (memq (process-status process) '(open run))
589                         (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
590                         (goto-char (point-min))
591                         ;; cyrus 1.6.x (13? < x <= 22) queries capabilities
592                         (or (while (looking-at "^C:")
593                               (forward-line))
594                             t)
595                         ;; cyrus 1.6 imtest print "S: " before server greeting
596                         (or (not (looking-at "S: "))
597                             (forward-char 3)
598                             t)
599                         (not (and (imap-parse-greeting)
600                                   ;; success in imtest 1.6:
601                                   (re-search-forward
602                                    (concat "^\\(\\(Authenticat.*\\)\\|\\("
603                                            "Client authentication "
604                                            "finished.*\\)\\)")
605                                    nil t)
606                                   (setq response (match-string 1)))))
607               (accept-process-output process 1)
608               (sit-for 1))
609             (and imap-log
610                  (with-current-buffer (get-buffer-create imap-log-buffer)
611                    (imap-disable-multibyte)
612                    (buffer-disable-undo)
613                    (goto-char (point-max))
614                    (insert-buffer-substring buffer)))
615             (erase-buffer)
616             (message "GSSAPI IMAP connection: %s" (or response "failed"))
617             (if (and response (let ((case-fold-search nil))
618                                 (not (string-match "failed" response))))
619                 (setq done process)
620               (if (memq (process-status process) '(open run))
621                   (imap-send-command "LOGOUT"))
622               (delete-process process)
623               nil)))))
624     done))
625
626 (defun imap-ssl-p (buffer)
627   nil)
628
629 (defun imap-ssl-open (name buffer server port)
630   "Open a SSL connection to server."
631   (let ((cmds (if (listp imap-ssl-program) imap-ssl-program
632                 (list imap-ssl-program)))
633         cmd done)
634     (while (and (not done) (setq cmd (pop cmds)))
635       (message "imap: Opening SSL connection with `%s'..." cmd)
636       (erase-buffer)
637       (let* ((port (or port imap-default-ssl-port))
638              (coding-system-for-read imap-coding-system-for-read)
639              (coding-system-for-write imap-coding-system-for-write)
640              (process-connection-type imap-process-connection-type)
641              (set-process-query-on-exit-flag
642               (if (fboundp 'set-process-query-on-exit-flag)
643                   'set-process-query-on-exit-flag
644                 'process-kill-without-query))
645              process)
646         (when (progn
647                 (setq process (start-process
648                                name buffer shell-file-name
649                                shell-command-switch
650                                (format-spec cmd
651                                             (format-spec-make
652                                              ?s server
653                                              ?p (number-to-string port)))))
654                 (funcall set-process-query-on-exit-flag process nil)
655                 process)
656           (with-current-buffer buffer
657             (goto-char (point-min))
658             (while (and (memq (process-status process) '(open run))
659                         (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
660                         (goto-char (point-max))
661                         (forward-line -1)
662                         (not (imap-parse-greeting)))
663               (accept-process-output process 1)
664               (sit-for 1))
665             (and imap-log
666                  (with-current-buffer (get-buffer-create imap-log-buffer)
667                    (imap-disable-multibyte)
668                    (buffer-disable-undo)
669                    (goto-char (point-max))
670                    (insert-buffer-substring buffer)))
671             (erase-buffer)
672             (when (memq (process-status process) '(open run))
673               (setq done process))))))
674     (if done
675         (progn
676           (message "imap: Opening SSL connection with `%s'...done" cmd)
677           done)
678       (message "imap: Opening SSL connection with `%s'...failed" cmd)
679       nil)))
680
681 (defun imap-tls-p (buffer)
682   nil)
683
684 (defun imap-tls-open (name buffer server port)
685   (let* ((port (or port imap-default-tls-port))
686          (coding-system-for-read imap-coding-system-for-read)
687          (coding-system-for-write imap-coding-system-for-write)
688          (process (open-tls-stream name buffer server port)))
689     (when process
690       (while (and (memq (process-status process) '(open run))
691                   (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
692                   (goto-char (point-max))
693                   (forward-line -1)
694                   (not (imap-parse-greeting)))
695         (accept-process-output process 1)
696         (sit-for 1))
697       (and imap-log
698            (with-current-buffer (get-buffer-create imap-log-buffer)
699              (imap-disable-multibyte)
700              (buffer-disable-undo)
701              (goto-char (point-max))
702              (insert-buffer-substring buffer)))
703       (when (memq (process-status process) '(open run))
704         process))))
705
706 (defun imap-network-p (buffer)
707   t)
708
709 (defun imap-network-open (name buffer server port)
710   (let* ((port (or port imap-default-port))
711          (coding-system-for-read imap-coding-system-for-read)
712          (coding-system-for-write imap-coding-system-for-write)
713          (process (open-network-stream name buffer server port)))
714     (when process
715       (while (and (memq (process-status process) '(open run))
716                   (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
717                   (goto-char (point-min))
718                   (not (imap-parse-greeting)))
719         (accept-process-output process 1)
720         (sit-for 1))
721       (and imap-log
722            (with-current-buffer (get-buffer-create imap-log-buffer)
723              (imap-disable-multibyte)
724              (buffer-disable-undo)
725              (goto-char (point-max))
726              (insert-buffer-substring buffer)))
727       (when (memq (process-status process) '(open run))
728         process))))
729
730 (defun imap-shell-p (buffer)
731   nil)
732
733 (defun imap-shell-open (name buffer server port)
734   (let ((cmds (if (listp imap-shell-program) imap-shell-program
735                 (list imap-shell-program)))
736         cmd done)
737     (while (and (not done) (setq cmd (pop cmds)))
738       (message "imap: Opening IMAP connection with `%s'..." cmd)
739       (setq imap-client-eol "\n")
740       (let* ((port (or port imap-default-port))
741              (coding-system-for-read imap-coding-system-for-read)
742              (coding-system-for-write imap-coding-system-for-write)
743              (process (start-process
744                        name buffer shell-file-name shell-command-switch
745                        (format-spec
746                         cmd
747                         (format-spec-make
748                          ?s server
749                          ?g imap-shell-host
750                          ?p (number-to-string port)
751                          ?l imap-default-user)))))
752         (when process
753           (while (and (memq (process-status process) '(open run))
754                       (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
755                       (goto-char (point-max))
756                       (forward-line -1)
757                       (not (imap-parse-greeting)))
758             (accept-process-output process 1)
759             (sit-for 1))
760           (and imap-log
761                (with-current-buffer (get-buffer-create imap-log-buffer)
762                  (imap-disable-multibyte)
763                  (buffer-disable-undo)
764                  (goto-char (point-max))
765                  (insert-buffer-substring buffer)))
766           (erase-buffer)
767           (when (memq (process-status process) '(open run))
768             (setq done process)))))
769     (if done
770         (progn
771           (message "imap: Opening IMAP connection with `%s'...done" cmd)
772           done)
773       (message "imap: Opening IMAP connection with `%s'...failed" cmd)
774       nil)))
775
776 (defun imap-starttls-p (buffer)
777   (imap-capability 'STARTTLS buffer))
778
779 (defun imap-starttls-open (name buffer server port)
780   (let* ((port (or port imap-default-port))
781          (coding-system-for-read imap-coding-system-for-read)
782          (coding-system-for-write imap-coding-system-for-write)
783          (process (starttls-open-stream name buffer server port))
784          done tls-info)
785     (message "imap: Connecting with STARTTLS...")
786     (when process
787       (while (and (memq (process-status process) '(open run))
788                   (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
789                   (goto-char (point-max))
790                   (forward-line -1)
791                   (not (imap-parse-greeting)))
792         (accept-process-output process 1)
793         (sit-for 1))
794       (imap-send-command "STARTTLS")
795       (while (and (memq (process-status process) '(open run))
796                   (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
797                   (goto-char (point-max))
798                   (forward-line -1)
799                   (not (re-search-forward "[0-9]+ OK.*\r?\n" nil t)))
800         (accept-process-output process 1)
801         (sit-for 1))
802       (and imap-log
803            (with-current-buffer (get-buffer-create imap-log-buffer)
804              (buffer-disable-undo)
805              (goto-char (point-max))
806              (insert-buffer-substring buffer)))
807       (when (and (setq tls-info (starttls-negotiate process))
808                  (memq (process-status process) '(open run)))
809         (setq done process)))
810     (if (stringp tls-info)
811         (message "imap: STARTTLS info: %s" tls-info))
812     (message "imap: Connecting with STARTTLS...%s" (if done "done" "failed"))
813     done))
814
815 ;; Server functions; authenticator stuff:
816
817 (defun imap-interactive-login (buffer loginfunc)
818   "Login to server in BUFFER.
819 LOGINFUNC is passed a username and a password, it should return t if
820 it where successful authenticating itself to the server, nil otherwise.
821 Returns t if login was successful, nil otherwise."
822   (with-current-buffer buffer
823     (make-local-variable 'imap-username)
824     (make-local-variable 'imap-password)
825     (let (user passwd ret)
826       ;;      (condition-case ()
827       (while (or (not user) (not passwd))
828         (setq user (or imap-username
829                        (read-from-minibuffer
830                         (concat "IMAP username for " imap-server
831                                 " (using stream `" (symbol-name imap-stream)
832                                 "'): ")
833                         (or user imap-default-user))))
834         (setq passwd (or imap-password
835                          (read-passwd
836                           (concat "IMAP password for " user "@"
837                                   imap-server " (using authenticator `"
838                                   (symbol-name imap-auth) "'): "))))
839         (when (and user passwd)
840           (if (funcall loginfunc user passwd)
841               (progn
842                 (setq ret t
843                       imap-username user)
844                 (when (and (not imap-password)
845                            (or imap-store-password
846                                (y-or-n-p "Store password for this session? ")))
847                   (setq imap-password passwd)))
848             (message "Login failed...")
849             (setq passwd nil)
850             (setq imap-password nil)
851             (sit-for 1))))
852       ;;        (quit (with-current-buffer buffer
853       ;;                (setq user nil
854       ;;                      passwd nil)))
855       ;;        (error (with-current-buffer buffer
856       ;;                 (setq user nil
857       ;;                       passwd nil))))
858       ret)))
859
860 (defun imap-gssapi-auth-p (buffer)
861   (eq imap-stream 'gssapi))
862
863 (defun imap-gssapi-auth (buffer)
864   (message "imap: Authenticating using GSSAPI...%s"
865            (if (eq imap-stream 'gssapi) "done" "failed"))
866   (eq imap-stream 'gssapi))
867
868 (defun imap-kerberos4-auth-p (buffer)
869   (and (imap-capability 'AUTH=KERBEROS_V4 buffer)
870        (eq imap-stream 'kerberos4)))
871
872 (defun imap-kerberos4-auth (buffer)
873   (message "imap: Authenticating using Kerberos 4...%s"
874            (if (eq imap-stream 'kerberos4) "done" "failed"))
875   (eq imap-stream 'kerberos4))
876
877 (defun imap-cram-md5-p (buffer)
878   (imap-capability 'AUTH=CRAM-MD5 buffer))
879
880 (defun imap-cram-md5-auth (buffer)
881   "Login to server using the AUTH CRAM-MD5 method."
882   (message "imap: Authenticating using CRAM-MD5...")
883   (let ((done (imap-interactive-login
884                buffer
885                (lambda (user passwd)
886                  (imap-ok-p
887                   (imap-send-command-wait
888                    (list
889                     "AUTHENTICATE CRAM-MD5"
890                     (lambda (challenge)
891                       (let* ((decoded (base64-decode-string challenge))
892                              (hash (rfc2104-hash 'md5 64 16 passwd decoded))
893                              (response (concat user " " hash))
894                              (encoded (base64-encode-string response)))
895                         encoded)))))))))
896     (if done
897         (message "imap: Authenticating using CRAM-MD5...done")
898       (message "imap: Authenticating using CRAM-MD5...failed"))))
899
900 (defun imap-login-p (buffer)
901   (and (not (imap-capability 'LOGINDISABLED buffer))
902        (not (imap-capability 'X-LOGIN-CMD-DISABLED buffer))))
903
904 (defun imap-login-auth (buffer)
905   "Login to server using the LOGIN command."
906   (message "imap: Plaintext authentication...")
907   (imap-interactive-login buffer
908                           (lambda (user passwd)
909                             (imap-ok-p (imap-send-command-wait
910                                         (concat "LOGIN \"" user "\" \""
911                                                 passwd "\""))))))
912
913 (defun imap-anonymous-p (buffer)
914   t)
915
916 (defun imap-anonymous-auth (buffer)
917   (message "imap: Logging in anonymously...")
918   (with-current-buffer buffer
919     (imap-ok-p (imap-send-command-wait
920                 (concat "LOGIN anonymous \"" (concat (user-login-name) "@"
921                                                      (system-name)) "\"")))))
922
923 ;;; Compiler directives.
924
925 (defvar imap-sasl-client)
926 (defvar imap-sasl-step)
927
928 (defun imap-sasl-make-mechanisms (buffer)
929   (let ((mecs '()))
930     (mapc (lambda (sym)
931             (let ((name (symbol-name sym)))
932               (if (and (> (length name) 5)
933                        (string-equal "AUTH=" (substring name 0 5 )))
934                   (setq mecs (cons (substring name 5) mecs)))))
935           (imap-capability nil buffer))
936     mecs))
937
938 (defun imap-sasl-auth-p (buffer)
939   (and (condition-case ()
940            (require 'sasl)
941          (error nil))
942        (sasl-find-mechanism (imap-sasl-make-mechanisms buffer))))
943
944 (defun imap-sasl-auth (buffer)
945   "Login to server using the SASL method."
946   (message "imap: Authenticating using SASL...")
947   (with-current-buffer buffer
948     (make-local-variable 'imap-username)
949     (make-local-variable 'imap-sasl-client)
950     (make-local-variable 'imap-sasl-step)
951     (let ((mechanism (sasl-find-mechanism (imap-sasl-make-mechanisms buffer)))
952           logged user)
953       (while (not logged)
954         (setq user (or imap-username
955                        (read-from-minibuffer
956                         (concat "IMAP username for " imap-server " using SASL "
957                                 (sasl-mechanism-name mechanism) ": ")
958                         (or user imap-default-user))))
959         (when user
960           (setq imap-sasl-client (sasl-make-client mechanism user "imap2" imap-server)
961                 imap-sasl-step (sasl-next-step imap-sasl-client nil))
962           (let ((tag (imap-send-command
963                       (if (sasl-step-data imap-sasl-step)
964                           (format "AUTHENTICATE %s %s"
965                                   (sasl-mechanism-name mechanism)
966                                   (sasl-step-data imap-sasl-step))
967                         (format "AUTHENTICATE %s" (sasl-mechanism-name mechanism)))
968                       buffer)))
969             (while (eq (imap-wait-for-tag tag) 'INCOMPLETE)
970               (sasl-step-set-data imap-sasl-step (base64-decode-string imap-continuation))
971               (setq imap-continuation nil
972                     imap-sasl-step (sasl-next-step imap-sasl-client imap-sasl-step))
973               (imap-send-command-1 (if (sasl-step-data imap-sasl-step)
974                                        (base64-encode-string (sasl-step-data imap-sasl-step) t)
975                                      "")))
976             (if (imap-ok-p (imap-wait-for-tag tag))
977                 (setq imap-username user
978                       logged t)
979               (message "Login failed...")
980               (sit-for 1)))))
981       logged)))
982
983 (defun imap-digest-md5-p (buffer)
984   (and (imap-capability 'AUTH=DIGEST-MD5 buffer)
985        (condition-case ()
986            (require 'digest-md5)
987          (error nil))))
988
989 (defun imap-digest-md5-auth (buffer)
990   "Login to server using the AUTH DIGEST-MD5 method."
991   (message "imap: Authenticating using DIGEST-MD5...")
992   (imap-interactive-login
993    buffer
994    (lambda (user passwd)
995      (let ((tag
996             (imap-send-command
997              (list
998               "AUTHENTICATE DIGEST-MD5"
999               (lambda (challenge)
1000                 (digest-md5-parse-digest-challenge
1001                  (base64-decode-string challenge))
1002                 (let* ((digest-uri
1003                         (digest-md5-digest-uri
1004                          "imap" (digest-md5-challenge 'realm)))
1005                        (response
1006                         (digest-md5-digest-response
1007                          user passwd digest-uri)))
1008                   (base64-encode-string response 'no-line-break))))
1009              )))
1010        (if (not (eq (imap-wait-for-tag tag) 'INCOMPLETE))
1011            nil
1012          (setq imap-continuation nil)
1013          (imap-send-command-1 "")
1014          (imap-ok-p (imap-wait-for-tag tag)))))))
1015
1016 ;; Server functions:
1017
1018 (defun imap-open-1 (buffer)
1019   (with-current-buffer buffer
1020     (erase-buffer)
1021     (setq imap-current-mailbox nil
1022           imap-current-message nil
1023           imap-state 'initial
1024           imap-process (condition-case ()
1025                            (funcall (nth 2 (assq imap-stream
1026                                                  imap-stream-alist))
1027                                     "imap" buffer imap-server imap-port)
1028                          ((error quit) nil)))
1029     (when imap-process
1030       (set-process-filter imap-process 'imap-arrival-filter)
1031       (set-process-sentinel imap-process 'imap-sentinel)
1032       (while (and (eq imap-state 'initial)
1033                   (memq (process-status imap-process) '(open run)))
1034         (message "Waiting for response from %s..." imap-server)
1035         (accept-process-output imap-process 1))
1036       (message "Waiting for response from %s...done" imap-server)
1037       (and (memq (process-status imap-process) '(open run))
1038            imap-process))))
1039
1040 (defun imap-open (server &optional port stream auth buffer)
1041   "Open a IMAP connection to host SERVER at PORT returning a buffer.
1042 If PORT is unspecified, a default value is used (143 except
1043 for SSL which use 993).
1044 STREAM indicates the stream to use, see `imap-streams' for available
1045 streams.  If nil, it choices the best stream the server is capable of.
1046 AUTH indicates authenticator to use, see `imap-authenticators' for
1047 available authenticators.  If nil, it choices the best stream the
1048 server is capable of.
1049 BUFFER can be a buffer or a name of a buffer, which is created if
1050 necessary.  If nil, the buffer name is generated."
1051   (setq buffer (or buffer (format " *imap* %s:%d" server (or port 0))))
1052   (with-current-buffer (get-buffer-create buffer)
1053     (if (imap-opened buffer)
1054         (imap-close buffer))
1055     (mapcar 'make-local-variable imap-local-variables)
1056     (imap-disable-multibyte)
1057     (buffer-disable-undo)
1058     (setq imap-server (or server imap-server))
1059     (setq imap-port (or port imap-port))
1060     (setq imap-auth (or auth imap-auth))
1061     (setq imap-stream (or stream imap-stream))
1062     (message "imap: Connecting to %s..." imap-server)
1063     (if (null (let ((imap-stream (or imap-stream imap-default-stream)))
1064                 (imap-open-1 buffer)))
1065         (progn
1066           (message "imap: Connecting to %s...failed" imap-server)
1067           nil)
1068       (when (null imap-stream)
1069         ;; Need to choose stream.
1070         (let ((streams imap-streams))
1071           (while (setq stream (pop streams))
1072             ;; OK to use this stream?
1073             (when (funcall (nth 1 (assq stream imap-stream-alist)) buffer)
1074               ;; Stream changed?
1075               (if (not (eq imap-default-stream stream))
1076                   (with-current-buffer (get-buffer-create
1077                                         (generate-new-buffer-name " *temp*"))
1078                     (mapcar 'make-local-variable imap-local-variables)
1079                     (imap-disable-multibyte)
1080                     (buffer-disable-undo)
1081                     (setq imap-server (or server imap-server))
1082                     (setq imap-port (or port imap-port))
1083                     (setq imap-auth (or auth imap-auth))
1084                     (message "imap: Reconnecting with stream `%s'..." stream)
1085                     (if (null (let ((imap-stream stream))
1086                                 (imap-open-1 (current-buffer))))
1087                         (progn
1088                           (kill-buffer (current-buffer))
1089                           (message
1090                            "imap: Reconnecting with stream `%s'...failed"
1091                            stream))
1092                       ;; We're done, kill the first connection
1093                       (imap-close buffer)
1094                       (kill-buffer buffer)
1095                       (rename-buffer buffer)
1096                       (message "imap: Reconnecting with stream `%s'...done"
1097                                stream)
1098                       (setq imap-stream stream)
1099                       (setq imap-capability nil)
1100                       (setq streams nil)))
1101                 ;; We're done
1102                 (message "imap: Connecting to %s...done" imap-server)
1103                 (setq imap-stream stream)
1104                 (setq imap-capability nil)
1105                 (setq streams nil))))))
1106       (when (imap-opened buffer)
1107         (setq imap-mailbox-data (make-vector imap-mailbox-prime 0)))
1108       (when imap-stream
1109         buffer))))
1110
1111 (defun imap-opened (&optional buffer)
1112   "Return non-nil if connection to imap server in BUFFER is open.
1113 If BUFFER is nil then the current buffer is used."
1114   (and (setq buffer (get-buffer (or buffer (current-buffer))))
1115        (buffer-live-p buffer)
1116        (with-current-buffer buffer
1117          (and imap-process
1118               (memq (process-status imap-process) '(open run))))))
1119
1120 (defun imap-authenticate (&optional user passwd buffer)
1121   "Authenticate to server in BUFFER, using current buffer if nil.
1122 It uses the authenticator specified when opening the server.  If the
1123 authenticator requires username/passwords, they are queried from the
1124 user and optionally stored in the buffer.  If USER and/or PASSWD is
1125 specified, the user will not be questioned and the username and/or
1126 password is remembered in the buffer."
1127   (with-current-buffer (or buffer (current-buffer))
1128     (if (not (eq imap-state 'nonauth))
1129         (or (eq imap-state 'auth)
1130             (eq imap-state 'selected)
1131             (eq imap-state 'examine))
1132       (make-local-variable 'imap-username)
1133       (make-local-variable 'imap-password)
1134       (if user (setq imap-username user))
1135       (if passwd (setq imap-password passwd))
1136       (if imap-auth
1137           (and (funcall (nth 2 (assq imap-auth
1138                                      imap-authenticator-alist)) buffer)
1139                (setq imap-state 'auth))
1140         ;; Choose authenticator.
1141         (let ((auths imap-authenticators)
1142               auth)
1143           (while (setq auth (pop auths))
1144             ;; OK to use authenticator?
1145             (when (funcall (nth 1 (assq auth imap-authenticator-alist)) buffer)
1146               (message "imap: Authenticating to `%s' using `%s'..."
1147                        imap-server auth)
1148               (setq imap-auth auth)
1149               (if (funcall (nth 2 (assq auth imap-authenticator-alist)) buffer)
1150                   (progn
1151                     (message "imap: Authenticating to `%s' using `%s'...done"
1152                              imap-server auth)
1153                     (setq auths nil))
1154                 (message "imap: Authenticating to `%s' using `%s'...failed"
1155                          imap-server auth)))))
1156         imap-state))))
1157
1158 (defun imap-close (&optional buffer)
1159   "Close connection to server in BUFFER.
1160 If BUFFER is nil, the current buffer is used."
1161   (with-current-buffer (or buffer (current-buffer))
1162     (when (imap-opened)
1163       (condition-case nil
1164           (imap-send-command-wait "LOGOUT")
1165         (quit nil)))
1166     (when (and imap-process
1167                (memq (process-status imap-process) '(open run)))
1168       (delete-process imap-process))
1169     (setq imap-current-mailbox nil
1170           imap-current-message nil
1171           imap-process nil)
1172     (erase-buffer)
1173     t))
1174
1175 (defun imap-capability (&optional identifier buffer)
1176   "Return a list of identifiers which server in BUFFER support.
1177 If IDENTIFIER, return non-nil if it's among the servers capabilities.
1178 If BUFFER is nil, the current buffer is assumed."
1179   (with-current-buffer (or buffer (current-buffer))
1180     (unless imap-capability
1181       (unless (imap-ok-p (imap-send-command-wait "CAPABILITY"))
1182         (setq imap-capability '(IMAP2))))
1183     (if identifier
1184         (memq (intern (upcase (symbol-name identifier))) imap-capability)
1185       imap-capability)))
1186
1187 (defun imap-id (&optional list-of-values buffer)
1188   "Identify client to server in BUFFER, and return server identity.
1189 LIST-OF-VALUES is nil, or a plist with identifier and value
1190 strings to send to the server to identify the client.
1191
1192 Return a list of identifiers which server in BUFFER support, or
1193 nil if it doesn't support ID or returns no information.
1194
1195 If BUFFER is nil, the current buffer is assumed."
1196   (with-current-buffer (or buffer (current-buffer))
1197     (when (and (imap-capability 'ID)
1198                (imap-ok-p (imap-send-command-wait
1199                            (if (null list-of-values)
1200                                "ID NIL"
1201                              (concat "ID (" (mapconcat (lambda (el)
1202                                                          (concat "\"" el "\""))
1203                                                        list-of-values
1204                                                        " ") ")")))))
1205       imap-id)))
1206
1207 (defun imap-namespace (&optional buffer)
1208   "Return a namespace hierarchy at server in BUFFER.
1209 If BUFFER is nil, the current buffer is assumed."
1210   (with-current-buffer (or buffer (current-buffer))
1211     (unless imap-namespace
1212       (when (imap-capability 'NAMESPACE)
1213         (imap-send-command-wait "NAMESPACE")))
1214     imap-namespace))
1215
1216 (defun imap-send-command-wait (command &optional buffer)
1217   (imap-wait-for-tag (imap-send-command command buffer) buffer))
1218
1219 \f
1220 ;; Mailbox functions:
1221
1222 (defun imap-mailbox-put (propname value &optional mailbox buffer)
1223   (with-current-buffer (or buffer (current-buffer))
1224     (if imap-mailbox-data
1225         (put (intern (or mailbox imap-current-mailbox) imap-mailbox-data)
1226              propname value)
1227       (error "Imap-mailbox-data is nil, prop %s value %s mailbox %s buffer %s"
1228              propname value mailbox (current-buffer)))
1229     t))
1230
1231 (defsubst imap-mailbox-get-1 (propname &optional mailbox)
1232   (get (intern-soft (or mailbox imap-current-mailbox) imap-mailbox-data)
1233        propname))
1234
1235 (defun imap-mailbox-get (propname &optional mailbox buffer)
1236   (let ((mailbox (imap-utf7-encode mailbox)))
1237     (with-current-buffer (or buffer (current-buffer))
1238       (imap-mailbox-get-1 propname (or mailbox imap-current-mailbox)))))
1239
1240 (defun imap-mailbox-map-1 (func &optional mailbox-decoder buffer)
1241   (with-current-buffer (or buffer (current-buffer))
1242     (let (result)
1243       (mapatoms
1244        (lambda (s)
1245          (push (funcall func (if mailbox-decoder
1246                                  (funcall mailbox-decoder (symbol-name s))
1247                                (symbol-name s))) result))
1248        imap-mailbox-data)
1249       result)))
1250
1251 (defun imap-mailbox-map (func &optional buffer)
1252   "Map a function across each mailbox in `imap-mailbox-data', returning a list.
1253 Function should take a mailbox name (a string) as
1254 the only argument."
1255   (imap-mailbox-map-1 func 'imap-utf7-decode buffer))
1256
1257 (defun imap-current-mailbox (&optional buffer)
1258   (with-current-buffer (or buffer (current-buffer))
1259     (imap-utf7-decode imap-current-mailbox)))
1260
1261 (defun imap-current-mailbox-p-1 (mailbox &optional examine)
1262   (and (string= mailbox imap-current-mailbox)
1263        (or (and examine
1264                 (eq imap-state 'examine))
1265            (and (not examine)
1266                 (eq imap-state 'selected)))))
1267
1268 (defun imap-current-mailbox-p (mailbox &optional examine buffer)
1269   (with-current-buffer (or buffer (current-buffer))
1270     (imap-current-mailbox-p-1 (imap-utf7-encode mailbox) examine)))
1271
1272 (defun imap-mailbox-select-1 (mailbox &optional examine)
1273   "Select MAILBOX on server in BUFFER.
1274 If EXAMINE is non-nil, do a read-only select."
1275   (if (imap-current-mailbox-p-1 mailbox examine)
1276       imap-current-mailbox
1277     (setq imap-current-mailbox mailbox)
1278     (if (imap-ok-p (imap-send-command-wait
1279                     (concat (if examine "EXAMINE" "SELECT") " \""
1280                             mailbox "\"")))
1281         (progn
1282           (setq imap-message-data (make-vector imap-message-prime 0)
1283                 imap-state (if examine 'examine 'selected))
1284           imap-current-mailbox)
1285       ;; Failed SELECT/EXAMINE unselects current mailbox
1286       (setq imap-current-mailbox nil))))
1287
1288 (defun imap-mailbox-select (mailbox &optional examine buffer)
1289   (with-current-buffer (or buffer (current-buffer))
1290     (imap-utf7-decode
1291      (imap-mailbox-select-1 (imap-utf7-encode mailbox) examine))))
1292
1293 (defun imap-mailbox-examine-1 (mailbox &optional buffer)
1294   (with-current-buffer (or buffer (current-buffer))
1295     (imap-mailbox-select-1 mailbox 'examine)))
1296
1297 (defun imap-mailbox-examine (mailbox &optional buffer)
1298   "Examine MAILBOX on server in BUFFER."
1299   (imap-mailbox-select mailbox 'examine buffer))
1300
1301 (defun imap-mailbox-unselect (&optional buffer)
1302   "Close current folder in BUFFER, without expunging articles."
1303   (with-current-buffer (or buffer (current-buffer))
1304     (when (or (eq imap-state 'auth)
1305               (and (imap-capability 'UNSELECT)
1306                    (imap-ok-p (imap-send-command-wait "UNSELECT")))
1307               (and (imap-ok-p
1308                     (imap-send-command-wait (concat "EXAMINE \""
1309                                                     imap-current-mailbox
1310                                                     "\"")))
1311                    (imap-ok-p (imap-send-command-wait "CLOSE"))))
1312       (setq imap-current-mailbox nil
1313             imap-message-data nil
1314             imap-state 'auth)
1315       t)))
1316
1317 (defun imap-mailbox-expunge (&optional asynch buffer)
1318   "Expunge articles in current folder in BUFFER.
1319 If ASYNCH, do not wait for succesful completion of the command.
1320 If BUFFER is nil the current buffer is assumed."
1321   (with-current-buffer (or buffer (current-buffer))
1322     (when (and imap-current-mailbox (not (eq imap-state 'examine)))
1323       (if asynch
1324           (imap-send-command "EXPUNGE")
1325       (imap-ok-p (imap-send-command-wait "EXPUNGE"))))))
1326
1327 (defun imap-mailbox-close (&optional asynch buffer)
1328   "Expunge articles and close current folder in BUFFER.
1329 If ASYNCH, do not wait for succesful completion of the command.
1330 If BUFFER is nil the current buffer is assumed."
1331   (with-current-buffer (or buffer (current-buffer))
1332     (when imap-current-mailbox
1333       (if asynch
1334           (imap-add-callback (imap-send-command "CLOSE")
1335                              `(lambda (tag status)
1336                                 (message "IMAP mailbox `%s' closed... %s"
1337                                          imap-current-mailbox status)
1338                                 (when (eq ,imap-current-mailbox
1339                                           imap-current-mailbox)
1340                                   ;; Don't wipe out data if another mailbox
1341                                   ;; was selected...
1342                                   (setq imap-current-mailbox nil
1343                                         imap-message-data nil
1344                                         imap-state 'auth))))
1345         (when (imap-ok-p (imap-send-command-wait "CLOSE"))
1346           (setq imap-current-mailbox nil
1347                 imap-message-data nil
1348                 imap-state 'auth)))
1349       t)))
1350
1351 (defun imap-mailbox-create-1 (mailbox)
1352   (imap-ok-p (imap-send-command-wait (list "CREATE \"" mailbox "\""))))
1353
1354 (defun imap-mailbox-create (mailbox &optional buffer)
1355   "Create MAILBOX on server in BUFFER.
1356 If BUFFER is nil the current buffer is assumed."
1357   (with-current-buffer (or buffer (current-buffer))
1358     (imap-mailbox-create-1 (imap-utf7-encode mailbox))))
1359
1360 (defun imap-mailbox-delete (mailbox &optional buffer)
1361   "Delete MAILBOX on server in BUFFER.
1362 If BUFFER is nil the current buffer is assumed."
1363   (let ((mailbox (imap-utf7-encode mailbox)))
1364     (with-current-buffer (or buffer (current-buffer))
1365       (imap-ok-p
1366        (imap-send-command-wait (list "DELETE \"" mailbox "\""))))))
1367
1368 (defun imap-mailbox-rename (oldname newname &optional buffer)
1369   "Rename mailbox OLDNAME to NEWNAME on server in BUFFER.
1370 If BUFFER is nil the current buffer is assumed."
1371   (let ((oldname (imap-utf7-encode oldname))
1372         (newname (imap-utf7-encode newname)))
1373     (with-current-buffer (or buffer (current-buffer))
1374       (imap-ok-p
1375        (imap-send-command-wait (list "RENAME \"" oldname "\" "
1376                                      "\"" newname "\""))))))
1377
1378 (defun imap-mailbox-lsub (&optional root reference add-delimiter buffer)
1379   "Return a list of subscribed mailboxes on server in BUFFER.
1380 If ROOT is non-nil, only list matching mailboxes.  If ADD-DELIMITER is
1381 non-nil, a hierarchy delimiter is added to root.  REFERENCE is a
1382 implementation-specific string that has to be passed to lsub command."
1383   (with-current-buffer (or buffer (current-buffer))
1384     ;; Make sure we know the hierarchy separator for root's hierarchy
1385     (when (and add-delimiter (null (imap-mailbox-get-1 'delimiter root)))
1386       (imap-send-command-wait (concat "LIST \"" reference "\" \""
1387                                       (imap-utf7-encode root) "\"")))
1388     ;; clear list data (NB not delimiter and other stuff)
1389     (imap-mailbox-map-1 (lambda (mailbox)
1390                           (imap-mailbox-put 'lsub nil mailbox)))
1391     (when (imap-ok-p
1392            (imap-send-command-wait
1393             (concat "LSUB \"" reference "\" \"" (imap-utf7-encode root)
1394                     (and add-delimiter (imap-mailbox-get-1 'delimiter root))
1395                     "%\"")))
1396       (let (out)
1397         (imap-mailbox-map-1 (lambda (mailbox)
1398                               (when (imap-mailbox-get-1 'lsub mailbox)
1399                                 (push (imap-utf7-decode mailbox) out))))
1400         (nreverse out)))))
1401
1402 (defun imap-mailbox-list (root &optional reference add-delimiter buffer)
1403   "Return a list of mailboxes matching ROOT on server in BUFFER.
1404 If ADD-DELIMITER is non-nil, a hierarchy delimiter is added to
1405 root.  REFERENCE is a implementation-specific string that has to be
1406 passed to list command."
1407   (with-current-buffer (or buffer (current-buffer))
1408     ;; Make sure we know the hierarchy separator for root's hierarchy
1409     (when (and add-delimiter (null (imap-mailbox-get-1 'delimiter root)))
1410       (imap-send-command-wait (concat "LIST \"" reference "\" \""
1411                                       (imap-utf7-encode root) "\"")))
1412     ;; clear list data (NB not delimiter and other stuff)
1413     (imap-mailbox-map-1 (lambda (mailbox)
1414                           (imap-mailbox-put 'list nil mailbox)))
1415     (when (imap-ok-p
1416            (imap-send-command-wait
1417             (concat "LIST \"" reference "\" \"" (imap-utf7-encode root)
1418                     (and add-delimiter (imap-mailbox-get-1 'delimiter root))
1419                     "%\"")))
1420       (let (out)
1421         (imap-mailbox-map-1 (lambda (mailbox)
1422                               (when (imap-mailbox-get-1 'list mailbox)
1423                                 (push (imap-utf7-decode mailbox) out))))
1424         (nreverse out)))))
1425
1426 (defun imap-mailbox-subscribe (mailbox &optional buffer)
1427   "Send the SUBSCRIBE command on the mailbox to server in BUFFER.
1428 Returns non-nil if successful."
1429   (with-current-buffer (or buffer (current-buffer))
1430     (imap-ok-p (imap-send-command-wait (concat "SUBSCRIBE \""
1431                                                (imap-utf7-encode mailbox)
1432                                                "\"")))))
1433
1434 (defun imap-mailbox-unsubscribe (mailbox &optional buffer)
1435   "Send the SUBSCRIBE command on the mailbox to server in BUFFER.
1436 Returns non-nil if successful."
1437   (with-current-buffer (or buffer (current-buffer))
1438     (imap-ok-p (imap-send-command-wait (concat "UNSUBSCRIBE "
1439                                                (imap-utf7-encode mailbox)
1440                                                "\"")))))
1441
1442 (defun imap-mailbox-status (mailbox items &optional buffer)
1443   "Get status items ITEM in MAILBOX from server in BUFFER.
1444 ITEMS can be a symbol or a list of symbols, valid symbols are one of
1445 the STATUS data items -- ie 'messages, 'recent, 'uidnext, 'uidvalidity
1446 or 'unseen.  If ITEMS is a list of symbols, a list of values is
1447 returned, if ITEMS is a symbol only its value is returned."
1448   (with-current-buffer (or buffer (current-buffer))
1449     (when (imap-ok-p
1450            (imap-send-command-wait (list "STATUS \""
1451                                          (imap-utf7-encode mailbox)
1452                                          "\" "
1453                                          (upcase
1454                                           (format "%s"
1455                                                   (if (listp items)
1456                                                       items
1457                                                     (list items)))))))
1458       (if (listp items)
1459           (mapcar (lambda (item)
1460                     (imap-mailbox-get item mailbox))
1461                   items)
1462         (imap-mailbox-get items mailbox)))))
1463
1464 (defun imap-mailbox-status-asynch (mailbox items &optional buffer)
1465   "Send status item request ITEM on MAILBOX to server in BUFFER.
1466 ITEMS can be a symbol or a list of symbols, valid symbols are one of
1467 the STATUS data items -- ie 'messages, 'recent, 'uidnext, 'uidvalidity
1468 or 'unseen.  The IMAP command tag is returned."
1469   (with-current-buffer (or buffer (current-buffer))
1470     (imap-send-command (list "STATUS \""
1471                              (imap-utf7-encode mailbox)
1472                              "\" "
1473                              (format "%s"
1474                                      (if (listp items)
1475                                          items
1476                                        (list items)))))))
1477
1478 (defun imap-mailbox-acl-get (&optional mailbox buffer)
1479   "Get ACL on mailbox from server in BUFFER."
1480   (let ((mailbox (imap-utf7-encode mailbox)))
1481     (with-current-buffer (or buffer (current-buffer))
1482       (when (imap-ok-p
1483              (imap-send-command-wait (list "GETACL \""
1484                                            (or mailbox imap-current-mailbox)
1485                                            "\"")))
1486         (imap-mailbox-get-1 'acl (or mailbox imap-current-mailbox))))))
1487
1488 (defun imap-mailbox-acl-set (identifier rights &optional mailbox buffer)
1489   "Change/set ACL for IDENTIFIER to RIGHTS in MAILBOX from server in BUFFER."
1490   (let ((mailbox (imap-utf7-encode mailbox)))
1491     (with-current-buffer (or buffer (current-buffer))
1492       (imap-ok-p
1493        (imap-send-command-wait (list "SETACL \""
1494                                      (or mailbox imap-current-mailbox)
1495                                      "\" "
1496                                      identifier
1497                                      " "
1498                                      rights))))))
1499
1500 (defun imap-mailbox-acl-delete (identifier &optional mailbox buffer)
1501   "Removes any <identifier,rights> pair for IDENTIFIER in MAILBOX from server in BUFFER."
1502   (let ((mailbox (imap-utf7-encode mailbox)))
1503     (with-current-buffer (or buffer (current-buffer))
1504       (imap-ok-p
1505        (imap-send-command-wait (list "DELETEACL \""
1506                                      (or mailbox imap-current-mailbox)
1507                                      "\" "
1508                                      identifier))))))
1509
1510 \f
1511 ;; Message functions:
1512
1513 (defun imap-current-message (&optional buffer)
1514   (with-current-buffer (or buffer (current-buffer))
1515     imap-current-message))
1516
1517 (defun imap-list-to-message-set (list)
1518   (mapconcat (lambda (item)
1519                (number-to-string item))
1520              (if (listp list)
1521                  list
1522                (list list))
1523              ","))
1524
1525 (defun imap-range-to-message-set (range)
1526   (mapconcat
1527    (lambda (item)
1528      (if (consp item)
1529          (format "%d:%d"
1530                  (car item) (cdr item))
1531        (format "%d" item)))
1532    (if (and (listp range) (not (listp (cdr range))))
1533        (list range) ;; make (1 . 2) into ((1 . 2))
1534      range)
1535    ","))
1536
1537 (defun imap-fetch-asynch (uids props &optional nouidfetch buffer)
1538   (with-current-buffer (or buffer (current-buffer))
1539     (imap-send-command (format "%sFETCH %s %s" (if nouidfetch "" "UID ")
1540                                (if (listp uids)
1541                                    (imap-list-to-message-set uids)
1542                                  uids)
1543                                props))))
1544
1545 (defun imap-fetch (uids props &optional receive nouidfetch buffer)
1546   "Fetch properties PROPS from message set UIDS from server in BUFFER.
1547 UIDS can be a string, number or a list of numbers.  If RECEIVE
1548 is non-nil return these properties."
1549   (with-current-buffer (or buffer (current-buffer))
1550     (when (imap-ok-p (imap-send-command-wait
1551                       (format "%sFETCH %s %s" (if nouidfetch "" "UID ")
1552                               (if (listp uids)
1553                                   (imap-list-to-message-set uids)
1554                                 uids)
1555                               props)))
1556       (if (or (null receive) (stringp uids))
1557           t
1558         (if (listp uids)
1559             (mapcar (lambda (uid)
1560                       (if (listp receive)
1561                           (mapcar (lambda (prop)
1562                                     (imap-message-get uid prop))
1563                                   receive)
1564                         (imap-message-get uid receive)))
1565                     uids)
1566           (imap-message-get uids receive))))))
1567
1568 (defun imap-message-put (uid propname value &optional buffer)
1569   (with-current-buffer (or buffer (current-buffer))
1570     (if imap-message-data
1571         (put (intern (number-to-string uid) imap-message-data)
1572              propname value)
1573       (error "Imap-message-data is nil, uid %s prop %s value %s buffer %s"
1574              uid propname value (current-buffer)))
1575     t))
1576
1577 (defun imap-message-get (uid propname &optional buffer)
1578   (with-current-buffer (or buffer (current-buffer))
1579     (get (intern-soft (number-to-string uid) imap-message-data)
1580          propname)))
1581
1582 (defun imap-message-map (func propname &optional buffer)
1583   "Map a function across each mailbox in `imap-message-data', returning a list."
1584   (with-current-buffer (or buffer (current-buffer))
1585     (let (result)
1586       (mapatoms
1587        (lambda (s)
1588          (push (funcall func (get s 'UID) (get s propname)) result))
1589        imap-message-data)
1590       result)))
1591
1592 (defmacro imap-message-envelope-date (uid &optional buffer)
1593   `(with-current-buffer (or ,buffer (current-buffer))
1594      (elt (imap-message-get ,uid 'ENVELOPE) 0)))
1595
1596 (defmacro imap-message-envelope-subject (uid &optional buffer)
1597   `(with-current-buffer (or ,buffer (current-buffer))
1598      (elt (imap-message-get ,uid 'ENVELOPE) 1)))
1599
1600 (defmacro imap-message-envelope-from (uid &optional buffer)
1601   `(with-current-buffer (or ,buffer (current-buffer))
1602      (elt (imap-message-get ,uid 'ENVELOPE) 2)))
1603
1604 (defmacro imap-message-envelope-sender (uid &optional buffer)
1605   `(with-current-buffer (or ,buffer (current-buffer))
1606      (elt (imap-message-get ,uid 'ENVELOPE) 3)))
1607
1608 (defmacro imap-message-envelope-reply-to (uid &optional buffer)
1609   `(with-current-buffer (or ,buffer (current-buffer))
1610      (elt (imap-message-get ,uid 'ENVELOPE) 4)))
1611
1612 (defmacro imap-message-envelope-to (uid &optional buffer)
1613   `(with-current-buffer (or ,buffer (current-buffer))
1614      (elt (imap-message-get ,uid 'ENVELOPE) 5)))
1615
1616 (defmacro imap-message-envelope-cc (uid &optional buffer)
1617   `(with-current-buffer (or ,buffer (current-buffer))
1618      (elt (imap-message-get ,uid 'ENVELOPE) 6)))
1619
1620 (defmacro imap-message-envelope-bcc (uid &optional buffer)
1621   `(with-current-buffer (or ,buffer (current-buffer))
1622      (elt (imap-message-get ,uid 'ENVELOPE) 7)))
1623
1624 (defmacro imap-message-envelope-in-reply-to (uid &optional buffer)
1625   `(with-current-buffer (or ,buffer (current-buffer))
1626      (elt (imap-message-get ,uid 'ENVELOPE) 8)))
1627
1628 (defmacro imap-message-envelope-message-id (uid &optional buffer)
1629   `(with-current-buffer (or ,buffer (current-buffer))
1630      (elt (imap-message-get ,uid 'ENVELOPE) 9)))
1631
1632 (defmacro imap-message-body (uid &optional buffer)
1633   `(with-current-buffer (or ,buffer (current-buffer))
1634      (imap-message-get ,uid 'BODY)))
1635
1636 (defun imap-search (predicate &optional buffer)
1637   (with-current-buffer (or buffer (current-buffer))
1638     (imap-mailbox-put 'search 'dummy)
1639     (when (imap-ok-p (imap-send-command-wait (concat "UID SEARCH " predicate)))
1640       (if (eq (imap-mailbox-get-1 'search imap-current-mailbox) 'dummy)
1641           (progn
1642             (message "Missing SEARCH response to a SEARCH command (server not RFC compliant)...")
1643             nil)
1644         (imap-mailbox-get-1 'search imap-current-mailbox)))))
1645
1646 (defun imap-message-flag-permanent-p (flag &optional mailbox buffer)
1647   "Return t iff FLAG can be permanently (between IMAP sessions) saved on articles, in MAILBOX on server in BUFFER."
1648   (with-current-buffer (or buffer (current-buffer))
1649     (or (member "\\*" (imap-mailbox-get 'permanentflags mailbox))
1650         (member flag (imap-mailbox-get 'permanentflags mailbox)))))
1651
1652 (defun imap-message-flags-set (articles flags &optional silent buffer)
1653   (when (and articles flags)
1654     (with-current-buffer (or buffer (current-buffer))
1655       (imap-ok-p (imap-send-command-wait
1656                   (concat "UID STORE " articles
1657                           " FLAGS" (if silent ".SILENT") " (" flags ")"))))))
1658
1659 (defun imap-message-flags-del (articles flags &optional silent buffer)
1660   (when (and articles flags)
1661     (with-current-buffer (or buffer (current-buffer))
1662       (imap-ok-p (imap-send-command-wait
1663                   (concat "UID STORE " articles
1664                           " -FLAGS" (if silent ".SILENT") " (" flags ")"))))))
1665
1666 (defun imap-message-flags-add (articles flags &optional silent buffer)
1667   (when (and articles flags)
1668     (with-current-buffer (or buffer (current-buffer))
1669       (imap-ok-p (imap-send-command-wait
1670                   (concat "UID STORE " articles
1671                           " +FLAGS" (if silent ".SILENT") " (" flags ")"))))))
1672
1673 (defun imap-message-copyuid-1 (mailbox)
1674   (if (imap-capability 'UIDPLUS)
1675       (list (nth 0 (imap-mailbox-get-1 'copyuid mailbox))
1676             (string-to-number (nth 2 (imap-mailbox-get-1 'copyuid mailbox))))
1677     (let ((old-mailbox imap-current-mailbox)
1678           (state imap-state)
1679           (imap-message-data (make-vector 2 0)))
1680       (when (imap-mailbox-examine-1 mailbox)
1681         (prog1
1682             (and (imap-fetch "*" "UID")
1683                  (list (imap-mailbox-get-1 'uidvalidity mailbox)
1684                        (apply 'max (imap-message-map
1685                                     (lambda (uid prop) uid) 'UID))))
1686           (if old-mailbox
1687               (imap-mailbox-select old-mailbox (eq state 'examine))
1688             (imap-mailbox-unselect)))))))
1689
1690 (defun imap-message-copyuid (mailbox &optional buffer)
1691   (with-current-buffer (or buffer (current-buffer))
1692     (imap-message-copyuid-1 (imap-utf7-decode mailbox))))
1693
1694 (defun imap-message-copy (articles mailbox
1695                                    &optional dont-create no-copyuid buffer)
1696   "Copy ARTICLES (a string message set) to MAILBOX on server in
1697 BUFFER, creating mailbox if it doesn't exist.  If dont-create is
1698 non-nil, it will not create a mailbox.  On success, return a list with
1699 the UIDVALIDITY of the mailbox the article(s) was copied to as the
1700 first element, rest of list contain the saved articles' UIDs."
1701   (when articles
1702     (with-current-buffer (or buffer (current-buffer))
1703       (let ((mailbox (imap-utf7-encode mailbox)))
1704         (if (let ((cmd (concat "UID COPY " articles " \"" mailbox "\""))
1705                   (imap-current-target-mailbox mailbox))
1706               (if (imap-ok-p (imap-send-command-wait cmd))
1707                   t
1708                 (when (and (not dont-create)
1709                            ;; removed because of buggy Oracle server
1710                            ;; that doesn't send TRYCREATE tags (which
1711                            ;; is a MUST according to specifications):
1712                            ;;(imap-mailbox-get-1 'trycreate mailbox)
1713                            (imap-mailbox-create-1 mailbox))
1714                   (imap-ok-p (imap-send-command-wait cmd)))))
1715             (or no-copyuid
1716                 (imap-message-copyuid-1 mailbox)))))))
1717
1718 (defun imap-message-appenduid-1 (mailbox)
1719   (if (imap-capability 'UIDPLUS)
1720       (imap-mailbox-get-1 'appenduid mailbox)
1721     (let ((old-mailbox imap-current-mailbox)
1722           (state imap-state)
1723           (imap-message-data (make-vector 2 0)))
1724       (when (imap-mailbox-examine-1 mailbox)
1725         (prog1
1726             (and (imap-fetch "*" "UID")
1727                  (list (imap-mailbox-get-1 'uidvalidity mailbox)
1728                        (apply 'max (imap-message-map
1729                                     (lambda (uid prop) uid) 'UID))))
1730           (if old-mailbox
1731               (imap-mailbox-select old-mailbox (eq state 'examine))
1732             (imap-mailbox-unselect)))))))
1733
1734 (defun imap-message-appenduid (mailbox &optional buffer)
1735   (with-current-buffer (or buffer (current-buffer))
1736     (imap-message-appenduid-1 (imap-utf7-encode mailbox))))
1737
1738 (defun imap-message-append (mailbox article &optional flags date-time buffer)
1739   "Append ARTICLE (a buffer) to MAILBOX on server in BUFFER.
1740 FLAGS and DATE-TIME is currently not used.  Return a cons holding
1741 uidvalidity of MAILBOX and UID the newly created article got, or nil
1742 on failure."
1743   (let ((mailbox (imap-utf7-encode mailbox)))
1744     (with-current-buffer (or buffer (current-buffer))
1745       (and (let ((imap-current-target-mailbox mailbox))
1746              (imap-ok-p
1747               (imap-send-command-wait
1748                (list "APPEND \"" mailbox "\" "  article))))
1749            (imap-message-appenduid-1 mailbox)))))
1750
1751 (defun imap-body-lines (body)
1752   "Return number of lines in article by looking at the mime bodystructure BODY."
1753   (if (listp body)
1754       (if (stringp (car body))
1755           (cond ((and (string= (upcase (car body)) "TEXT")
1756                       (numberp (nth 7 body)))
1757                  (nth 7 body))
1758                 ((and (string= (upcase (car body)) "MESSAGE")
1759                       (numberp (nth 9 body)))
1760                  (nth 9 body))
1761                 (t 0))
1762         (apply '+ (mapcar 'imap-body-lines body)))
1763     0))
1764
1765 (defun imap-envelope-from (from)
1766   "Return a from string line."
1767   (and from
1768        (concat (aref from 0)
1769                (if (aref from 0) " <")
1770                (aref from 2)
1771                "@"
1772                (aref from 3)
1773                (if (aref from 0) ">"))))
1774
1775 \f
1776 ;; Internal functions.
1777
1778 (defun imap-add-callback (tag func)
1779   (setq imap-callbacks (append (list (cons tag func)) imap-callbacks)))
1780
1781 (defun imap-send-command-1 (cmdstr)
1782   (setq cmdstr (concat cmdstr imap-client-eol))
1783   (and imap-log
1784        (with-current-buffer (get-buffer-create imap-log-buffer)
1785          (imap-disable-multibyte)
1786          (buffer-disable-undo)
1787          (goto-char (point-max))
1788          (insert cmdstr)))
1789   (process-send-string imap-process cmdstr))
1790
1791 (defun imap-send-command (command &optional buffer)
1792   (with-current-buffer (or buffer (current-buffer))
1793     (if (not (listp command)) (setq command (list command)))
1794     (let ((tag (setq imap-tag (1+ imap-tag)))
1795           cmd cmdstr)
1796       (setq cmdstr (concat (number-to-string imap-tag) " "))
1797       (while (setq cmd (pop command))
1798         (cond ((stringp cmd)
1799                (setq cmdstr (concat cmdstr cmd)))
1800               ((bufferp cmd)
1801                (let ((eol imap-client-eol)
1802                      (calcfirst imap-calculate-literal-size-first)
1803                      size)
1804                  (with-current-buffer cmd
1805                    (if calcfirst
1806                        (setq size (buffer-size)))
1807                    (when (not (equal eol "\r\n"))
1808                      ;; XXX modifies buffer!
1809                      (goto-char (point-min))
1810                      (while (search-forward "\r\n" nil t)
1811                        (replace-match eol)))
1812                    (if (not calcfirst)
1813                        (setq size (buffer-size))))
1814                  (setq cmdstr
1815                        (concat cmdstr (format "{%d}" size))))
1816                (unwind-protect
1817                    (progn
1818                      (imap-send-command-1 cmdstr)
1819                      (setq cmdstr nil)
1820                      (if (not (eq (imap-wait-for-tag tag) 'INCOMPLETE))
1821                          (setq command nil) ;; abort command if no cont-req
1822                        (let ((process imap-process)
1823                              (stream imap-stream)
1824                              (eol imap-client-eol))
1825                          (with-current-buffer cmd
1826                            (and imap-log
1827                                 (with-current-buffer (get-buffer-create
1828                                                       imap-log-buffer)
1829                                   (imap-disable-multibyte)
1830                                   (buffer-disable-undo)
1831                                   (goto-char (point-max))
1832                                   (insert-buffer-substring cmd)))
1833                            (process-send-region process (point-min)
1834                                                 (point-max)))
1835                          (process-send-string process imap-client-eol))))
1836                  (setq imap-continuation nil)))
1837               ((functionp cmd)
1838                (imap-send-command-1 cmdstr)
1839                (setq cmdstr nil)
1840                (unwind-protect
1841                    (if (not (eq (imap-wait-for-tag tag) 'INCOMPLETE))
1842                        (setq command nil) ;; abort command if no cont-req
1843                      (setq command (cons (funcall cmd imap-continuation)
1844                                          command)))
1845                  (setq imap-continuation nil)))
1846               (t
1847                (error "Unknown command type"))))
1848       (if cmdstr
1849           (imap-send-command-1 cmdstr))
1850       tag)))
1851
1852 (defun imap-wait-for-tag (tag &optional buffer)
1853   (with-current-buffer (or buffer (current-buffer))
1854     (let (imap-have-messaged)
1855       (while (and (null imap-continuation)
1856                   (memq (process-status imap-process) '(open run))
1857                   (< imap-reached-tag tag))
1858         (let ((len (/ (point-max) 1024))
1859               message-log-max)
1860           (unless (< len 10)
1861             (setq imap-have-messaged t)
1862             (message "imap read: %dk" len))
1863           (accept-process-output imap-process
1864                                  (truncate imap-read-timeout)
1865                                  (truncate (* (- imap-read-timeout
1866                                                  (truncate imap-read-timeout))
1867                                               1000)))))
1868       ;; A process can die _before_ we have processed everything it
1869       ;; has to say.  Moreover, this can happen in between the call to
1870       ;; accept-process-output and the call to process-status in an
1871       ;; iteration of the loop above.
1872       (when (and (null imap-continuation)
1873                  (< imap-reached-tag tag))
1874         (accept-process-output imap-process 0 0))
1875       (when imap-have-messaged
1876         (message ""))
1877       (and (memq (process-status imap-process) '(open run))
1878            (or (assq tag imap-failed-tags)
1879                (if imap-continuation
1880                    'INCOMPLETE
1881                  'OK))))))
1882
1883 (defun imap-sentinel (process string)
1884   (delete-process process))
1885
1886 (defun imap-find-next-line ()
1887   "Return point at end of current line, taking into account literals.
1888 Return nil if no complete line has arrived."
1889   (when (re-search-forward (concat imap-server-eol "\\|{\\([0-9]+\\)}"
1890                                    imap-server-eol)
1891                            nil t)
1892     (if (match-string 1)
1893         (if (< (point-max) (+ (point) (string-to-number (match-string 1))))
1894             nil
1895           (goto-char (+ (point) (string-to-number (match-string 1))))
1896           (imap-find-next-line))
1897       (point))))
1898
1899 (defun imap-arrival-filter (proc string)
1900   "IMAP process filter."
1901   ;; Sometimes, we are called even though the process has died.
1902   ;; Better abstain from doing stuff in that case.
1903   (when (buffer-name (process-buffer proc))
1904     (with-current-buffer (process-buffer proc)
1905       (goto-char (point-max))
1906       (insert string)
1907       (and imap-log
1908            (with-current-buffer (get-buffer-create imap-log-buffer)
1909              (imap-disable-multibyte)
1910              (buffer-disable-undo)
1911              (goto-char (point-max))
1912              (insert string)))
1913       (let (end)
1914         (goto-char (point-min))
1915         (while (setq end (imap-find-next-line))
1916           (save-restriction
1917             (narrow-to-region (point-min) end)
1918             (delete-backward-char (length imap-server-eol))
1919             (goto-char (point-min))
1920             (unwind-protect
1921                 (cond ((eq imap-state 'initial)
1922                        (imap-parse-greeting))
1923                       ((or (eq imap-state 'auth)
1924                            (eq imap-state 'nonauth)
1925                            (eq imap-state 'selected)
1926                            (eq imap-state 'examine))
1927                        (imap-parse-response))
1928                       (t
1929                        (message "Unknown state %s in arrival filter"
1930                                 imap-state)))
1931               (delete-region (point-min) (point-max)))))))))
1932
1933 \f
1934 ;; Imap parser.
1935
1936 (defsubst imap-forward ()
1937   (or (eobp) (forward-char)))
1938
1939 ;;   number          = 1*DIGIT
1940 ;;                       ; Unsigned 32-bit integer
1941 ;;                       ; (0 <= n < 4,294,967,296)
1942
1943 (defsubst imap-parse-number ()
1944   (when (looking-at "[0-9]+")
1945     (prog1
1946         (string-to-number (match-string 0))
1947       (goto-char (match-end 0)))))
1948
1949 ;;   literal         = "{" number "}" CRLF *CHAR8
1950 ;;                       ; Number represents the number of CHAR8s
1951
1952 (defsubst imap-parse-literal ()
1953   (when (looking-at "{\\([0-9]+\\)}\r\n")
1954     (let ((pos (match-end 0))
1955           (len (string-to-number (match-string 1))))
1956       (if (< (point-max) (+ pos len))
1957           nil
1958         (goto-char (+ pos len))
1959         (buffer-substring pos (+ pos len))))))
1960
1961 ;;   string          = quoted / literal
1962 ;;
1963 ;;   quoted          = DQUOTE *QUOTED-CHAR DQUOTE
1964 ;;
1965 ;;   QUOTED-CHAR     = <any TEXT-CHAR except quoted-specials> /
1966 ;;                     "\" quoted-specials
1967 ;;
1968 ;;   quoted-specials = DQUOTE / "\"
1969 ;;
1970 ;;   TEXT-CHAR       = <any CHAR except CR and LF>
1971
1972 (defsubst imap-parse-string ()
1973   (cond ((eq (char-after) ?\")
1974          (forward-char 1)
1975          (let ((p (point)) (name ""))
1976            (skip-chars-forward "^\"\\\\")
1977            (setq name (buffer-substring p (point)))
1978            (while (eq (char-after) ?\\)
1979              (setq p (1+ (point)))
1980              (forward-char 2)
1981              (skip-chars-forward "^\"\\\\")
1982              (setq name (concat name (buffer-substring p (point)))))
1983            (forward-char 1)
1984            name))
1985         ((eq (char-after) ?{)
1986          (imap-parse-literal))))
1987
1988 ;;   nil             = "NIL"
1989
1990 (defsubst imap-parse-nil ()
1991   (if (looking-at "NIL")
1992       (goto-char (match-end 0))))
1993
1994 ;;   nstring         = string / nil
1995
1996 (defsubst imap-parse-nstring ()
1997   (or (imap-parse-string)
1998       (and (imap-parse-nil)
1999            nil)))
2000
2001 ;;   astring         = atom / string
2002 ;;
2003 ;;   atom            = 1*ATOM-CHAR
2004 ;;
2005 ;;   ATOM-CHAR       = <any CHAR except atom-specials>
2006 ;;
2007 ;;   atom-specials   = "(" / ")" / "{" / SP / CTL / list-wildcards /
2008 ;;                     quoted-specials
2009 ;;
2010 ;;   list-wildcards  = "%" / "*"
2011 ;;
2012 ;;   quoted-specials = DQUOTE / "\"
2013
2014 (defsubst imap-parse-astring ()
2015   (or (imap-parse-string)
2016       (buffer-substring (point)
2017                         (if (re-search-forward "[(){ \r\n%*\"\\]" nil t)
2018                             (goto-char (1- (match-end 0)))
2019                           (end-of-line)
2020                           (point)))))
2021
2022 ;;   address         = "(" addr-name SP addr-adl SP addr-mailbox SP
2023 ;;                      addr-host ")"
2024 ;;
2025 ;;   addr-adl        = nstring
2026 ;;                       ; Holds route from [RFC-822] route-addr if
2027 ;;                       ; non-nil
2028 ;;
2029 ;;   addr-host       = nstring
2030 ;;                       ; nil indicates [RFC-822] group syntax.
2031 ;;                       ; Otherwise, holds [RFC-822] domain name
2032 ;;
2033 ;;   addr-mailbox    = nstring
2034 ;;                       ; nil indicates end of [RFC-822] group; if
2035 ;;                       ; non-nil and addr-host is nil, holds
2036 ;;                       ; [RFC-822] group name.
2037 ;;                       ; Otherwise, holds [RFC-822] local-part
2038 ;;                       ; after removing [RFC-822] quoting
2039 ;;
2040 ;;   addr-name       = nstring
2041 ;;                       ; If non-nil, holds phrase from [RFC-822]
2042 ;;                       ; mailbox after removing [RFC-822] quoting
2043 ;;
2044
2045 (defsubst imap-parse-address ()
2046   (let (address)
2047     (when (eq (char-after) ?\()
2048       (imap-forward)
2049       (setq address (vector (prog1 (imap-parse-nstring)
2050                               (imap-forward))
2051                             (prog1 (imap-parse-nstring)
2052                               (imap-forward))
2053                             (prog1 (imap-parse-nstring)
2054                               (imap-forward))
2055                             (imap-parse-nstring)))
2056       (when (eq (char-after) ?\))
2057         (imap-forward)
2058         address))))
2059
2060 ;;   address-list    = "(" 1*address ")" / nil
2061 ;;
2062 ;;   nil             = "NIL"
2063
2064 (defsubst imap-parse-address-list ()
2065   (if (eq (char-after) ?\()
2066       (let (address addresses)
2067         (imap-forward)
2068         (while (and (not (eq (char-after) ?\)))
2069                     ;; next line for MS Exchange bug
2070                     (progn (and (eq (char-after) ? ) (imap-forward)) t)
2071                     (setq address (imap-parse-address)))
2072           (setq addresses (cons address addresses)))
2073         (when (eq (char-after) ?\))
2074           (imap-forward)
2075           (nreverse addresses)))
2076     ;; With assert, the code might not be eval'd.
2077     ;; (assert (imap-parse-nil) t "In imap-parse-address-list")
2078     (imap-parse-nil)))
2079
2080 ;;   mailbox         = "INBOX" / astring
2081 ;;                       ; INBOX is case-insensitive.  All case variants of
2082 ;;                       ; INBOX (e.g. "iNbOx") MUST be interpreted as INBOX
2083 ;;                       ; not as an astring.  An astring which consists of
2084 ;;                       ; the case-insensitive sequence "I" "N" "B" "O" "X"
2085 ;;                       ; is considered to be INBOX and not an astring.
2086 ;;                       ;  Refer to section 5.1 for further
2087 ;;                       ; semantic details of mailbox names.
2088
2089 (defsubst imap-parse-mailbox ()
2090   (let ((mailbox (imap-parse-astring)))
2091     (if (string-equal "INBOX" (upcase mailbox))
2092         "INBOX"
2093       mailbox)))
2094
2095 ;;   greeting        = "*" SP (resp-cond-auth / resp-cond-bye) CRLF
2096 ;;
2097 ;;   resp-cond-auth  = ("OK" / "PREAUTH") SP resp-text
2098 ;;                       ; Authentication condition
2099 ;;
2100 ;;   resp-cond-bye   = "BYE" SP resp-text
2101
2102 (defun imap-parse-greeting ()
2103   "Parse a IMAP greeting."
2104   (cond ((looking-at "\\* OK ")
2105          (setq imap-state 'nonauth))
2106         ((looking-at "\\* PREAUTH ")
2107          (setq imap-state 'auth))
2108         ((looking-at "\\* BYE ")
2109          (setq imap-state 'closed))))
2110
2111 ;;   response        = *(continue-req / response-data) response-done
2112 ;;
2113 ;;   continue-req    = "+" SP (resp-text / base64) CRLF
2114 ;;
2115 ;;   response-data   = "*" SP (resp-cond-state / resp-cond-bye /
2116 ;;                     mailbox-data / message-data / capability-data) CRLF
2117 ;;
2118 ;;   response-done   = response-tagged / response-fatal
2119 ;;
2120 ;;   response-fatal  = "*" SP resp-cond-bye CRLF
2121 ;;                       ; Server closes connection immediately
2122 ;;
2123 ;;   response-tagged = tag SP resp-cond-state CRLF
2124 ;;
2125 ;;   resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text
2126 ;;                       ; Status condition
2127 ;;
2128 ;;   resp-cond-bye   = "BYE" SP resp-text
2129 ;;
2130 ;;   mailbox-data    =  "FLAGS" SP flag-list /
2131 ;;                      "LIST" SP mailbox-list /
2132 ;;                      "LSUB" SP mailbox-list /
2133 ;;                      "SEARCH" *(SP nz-number) /
2134 ;;                      "STATUS" SP mailbox SP "("
2135 ;;                            [status-att SP number *(SP status-att SP number)] ")" /
2136 ;;                      number SP "EXISTS" /
2137 ;;                      number SP "RECENT"
2138 ;;
2139 ;;   message-data    = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att))
2140 ;;
2141 ;;   capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1"
2142 ;;                     *(SP capability)
2143 ;;                       ; IMAP4rev1 servers which offer RFC 1730
2144 ;;                       ; compatibility MUST list "IMAP4" as the first
2145 ;;                       ; capability.
2146
2147 (defun imap-parse-response ()
2148   "Parse a IMAP command response."
2149   (let (token)
2150     (case (setq token (read (current-buffer)))
2151       (+ (setq imap-continuation
2152                (or (buffer-substring (min (point-max) (1+ (point)))
2153                                      (point-max))
2154                    t)))
2155       (* (case (prog1 (setq token (read (current-buffer)))
2156                  (imap-forward))
2157            (OK         (imap-parse-resp-text))
2158            (NO         (imap-parse-resp-text))
2159            (BAD        (imap-parse-resp-text))
2160            (BYE        (imap-parse-resp-text))
2161            (FLAGS      (imap-mailbox-put 'flags (imap-parse-flag-list)))
2162            (LIST       (imap-parse-data-list 'list))
2163            (LSUB       (imap-parse-data-list 'lsub))
2164            (SEARCH     (imap-mailbox-put
2165                         'search
2166                         (read (concat "(" (buffer-substring (point) (point-max)) ")"))))
2167            (STATUS     (imap-parse-status))
2168            (CAPABILITY (setq imap-capability
2169                                (read (concat "(" (upcase (buffer-substring
2170                                                           (point) (point-max)))
2171                                              ")"))))
2172            (ID         (setq imap-id (read (buffer-substring (point)
2173                                                              (point-max)))))
2174            (ACL        (imap-parse-acl))
2175            (t       (case (prog1 (read (current-buffer))
2176                             (imap-forward))
2177                       (EXISTS  (imap-mailbox-put 'exists token))
2178                       (RECENT  (imap-mailbox-put 'recent token))
2179                       (EXPUNGE t)
2180                       (FETCH   (imap-parse-fetch token))
2181                       (t       (message "Garbage: %s" (buffer-string)))))))
2182       (t (let (status)
2183            (if (not (integerp token))
2184                (message "Garbage: %s" (buffer-string))
2185              (case (prog1 (setq status (read (current-buffer)))
2186                      (imap-forward))
2187                (OK  (progn
2188                       (setq imap-reached-tag (max imap-reached-tag token))
2189                       (imap-parse-resp-text)))
2190                (NO  (progn
2191                       (setq imap-reached-tag (max imap-reached-tag token))
2192                       (save-excursion
2193                         (imap-parse-resp-text))
2194                       (let (code text)
2195                         (when (eq (char-after) ?\[)
2196                           (setq code (buffer-substring (point)
2197                                                        (search-forward "]")))
2198                           (imap-forward))
2199                         (setq text (buffer-substring (point) (point-max)))
2200                         (push (list token status code text)
2201                               imap-failed-tags))))
2202                (BAD (progn
2203                       (setq imap-reached-tag (max imap-reached-tag token))
2204                       (save-excursion
2205                         (imap-parse-resp-text))
2206                       (let (code text)
2207                         (when (eq (char-after) ?\[)
2208                           (setq code (buffer-substring (point)
2209                                                        (search-forward "]")))
2210                           (imap-forward))
2211                         (setq text (buffer-substring (point) (point-max)))
2212                         (push (list token status code text) imap-failed-tags)
2213                         (error "Internal error, tag %s status %s code %s text %s"
2214                                token status code text))))
2215                (t   (message "Garbage: %s" (buffer-string))))
2216              (when (assq token imap-callbacks)
2217                (funcall (cdr (assq token imap-callbacks)) token status)
2218                (setq imap-callbacks
2219                      (imap-remassoc token imap-callbacks)))))))))
2220
2221 ;;   resp-text       = ["[" resp-text-code "]" SP] text
2222 ;;
2223 ;;   text            = 1*TEXT-CHAR
2224 ;;
2225 ;;   TEXT-CHAR       = <any CHAR except CR and LF>
2226
2227 (defun imap-parse-resp-text ()
2228   (imap-parse-resp-text-code))
2229
2230 ;;   resp-text-code  = "ALERT" /
2231 ;;                     "BADCHARSET [SP "(" astring *(SP astring) ")" ] /
2232 ;;                     "NEWNAME" SP string SP string /
2233 ;;                     "PARSE" /
2234 ;;                     "PERMANENTFLAGS" SP "("
2235 ;;                               [flag-perm *(SP flag-perm)] ")" /
2236 ;;                     "READ-ONLY" /
2237 ;;                     "READ-WRITE" /
2238 ;;                     "TRYCREATE" /
2239 ;;                     "UIDNEXT" SP nz-number /
2240 ;;                     "UIDVALIDITY" SP nz-number /
2241 ;;                     "UNSEEN" SP nz-number /
2242 ;;                     resp-text-atom [SP 1*<any TEXT-CHAR except "]">]
2243 ;;
2244 ;;   resp_code_apnd  = "APPENDUID" SPACE nz_number SPACE uniqueid
2245 ;;
2246 ;;   resp_code_copy  = "COPYUID" SPACE nz_number SPACE set SPACE set
2247 ;;
2248 ;;   set             = sequence-num / (sequence-num ":" sequence-num) /
2249 ;;                        (set "," set)
2250 ;;                          ; Identifies a set of messages.  For message
2251 ;;                          ; sequence numbers, these are consecutive
2252 ;;                          ; numbers from 1 to the number of messages in
2253 ;;                          ; the mailbox
2254 ;;                          ; Comma delimits individual numbers, colon
2255 ;;                          ; delimits between two numbers inclusive.
2256 ;;                          ; Example: 2,4:7,9,12:* is 2,4,5,6,7,9,12,13,
2257 ;;                          ; 14,15 for a mailbox with 15 messages.
2258 ;;
2259 ;;   sequence-num    = nz-number / "*"
2260 ;;                          ; * is the largest number in use.  For message
2261 ;;                          ; sequence numbers, it is the number of messages
2262 ;;                          ; in the mailbox.  For unique identifiers, it is
2263 ;;                          ; the unique identifier of the last message in
2264 ;;                          ; the mailbox.
2265 ;;
2266 ;;   flag-perm       = flag / "\*"
2267 ;;
2268 ;;   flag            = "\Answered" / "\Flagged" / "\Deleted" /
2269 ;;                     "\Seen" / "\Draft" / flag-keyword / flag-extension
2270 ;;                       ; Does not include "\Recent"
2271 ;;
2272 ;;   flag-extension  = "\" atom
2273 ;;                       ; Future expansion.  Client implementations
2274 ;;                       ; MUST accept flag-extension flags.  Server
2275 ;;                       ; implementations MUST NOT generate
2276 ;;                       ; flag-extension flags except as defined by
2277 ;;                       ; future standard or standards-track
2278 ;;                       ; revisions of this specification.
2279 ;;
2280 ;;   flag-keyword    = atom
2281 ;;
2282 ;;   resp-text-atom  = 1*<any ATOM-CHAR except "]">
2283
2284 (defun imap-parse-resp-text-code ()
2285   ;; xxx next line for stalker communigate pro 3.3.1 bug
2286   (when (looking-at " \\[")
2287     (imap-forward))
2288   (when (eq (char-after) ?\[)
2289     (imap-forward)
2290     (cond ((search-forward "PERMANENTFLAGS " nil t)
2291            (imap-mailbox-put 'permanentflags (imap-parse-flag-list)))
2292           ((search-forward "UIDNEXT \\([0-9]+\\)" nil t)
2293            (imap-mailbox-put 'uidnext (match-string 1)))
2294           ((search-forward "UNSEEN " nil t)
2295            (imap-mailbox-put 'first-unseen (read (current-buffer))))
2296           ((looking-at "UIDVALIDITY \\([0-9]+\\)")
2297            (imap-mailbox-put 'uidvalidity (match-string 1)))
2298           ((search-forward "READ-ONLY" nil t)
2299            (imap-mailbox-put 'read-only t))
2300           ((search-forward "NEWNAME " nil t)
2301            (let (oldname newname)
2302              (setq oldname (imap-parse-string))
2303              (imap-forward)
2304              (setq newname (imap-parse-string))
2305              (imap-mailbox-put 'newname newname oldname)))
2306           ((search-forward "TRYCREATE" nil t)
2307            (imap-mailbox-put 'trycreate t imap-current-target-mailbox))
2308           ((looking-at "APPENDUID \\([0-9]+\\) \\([0-9]+\\)")
2309            (imap-mailbox-put 'appenduid
2310                              (list (match-string 1)
2311                                    (string-to-number (match-string 2)))
2312                              imap-current-target-mailbox))
2313           ((looking-at "COPYUID \\([0-9]+\\) \\([0-9,:]+\\) \\([0-9,:]+\\)")
2314            (imap-mailbox-put 'copyuid (list (match-string 1)
2315                                             (match-string 2)
2316                                             (match-string 3))
2317                              imap-current-target-mailbox))
2318           ((search-forward "ALERT] " nil t)
2319            (message "Imap server %s information: %s" imap-server
2320                     (buffer-substring (point) (point-max)))))))
2321
2322 ;;   mailbox-list    = "(" [mbx-list-flags] ")" SP
2323 ;;                      (DQUOTE QUOTED-CHAR DQUOTE / nil) SP mailbox
2324 ;;
2325 ;;   mbx-list-flags  = *(mbx-list-oflag SP) mbx-list-sflag
2326 ;;                     *(SP mbx-list-oflag) /
2327 ;;                     mbx-list-oflag *(SP mbx-list-oflag)
2328 ;;
2329 ;;   mbx-list-oflag  = "\Noinferiors" / flag-extension
2330 ;;                       ; Other flags; multiple possible per LIST response
2331 ;;
2332 ;;   mbx-list-sflag  = "\Noselect" / "\Marked" / "\Unmarked"
2333 ;;                       ; Selectability flags; only one per LIST response
2334 ;;
2335 ;;   QUOTED-CHAR     = <any TEXT-CHAR except quoted-specials> /
2336 ;;                     "\" quoted-specials
2337 ;;
2338 ;;   quoted-specials = DQUOTE / "\"
2339
2340 (defun imap-parse-data-list (type)
2341   (let (flags delimiter mailbox)
2342     (setq flags (imap-parse-flag-list))
2343     (when (looking-at " NIL\\| \"\\\\?\\(.\\)\"")
2344       (setq delimiter (match-string 1))
2345       (goto-char (1+ (match-end 0)))
2346       (when (setq mailbox (imap-parse-mailbox))
2347         (imap-mailbox-put type t mailbox)
2348         (imap-mailbox-put 'list-flags flags mailbox)
2349         (imap-mailbox-put 'delimiter delimiter mailbox)))))
2350
2351 ;;  msg_att         ::= "(" 1#("ENVELOPE" SPACE envelope /
2352 ;;                      "FLAGS" SPACE "(" #(flag / "\Recent") ")" /
2353 ;;                      "INTERNALDATE" SPACE date_time /
2354 ;;                      "RFC822" [".HEADER" / ".TEXT"] SPACE nstring /
2355 ;;                      "RFC822.SIZE" SPACE number /
2356 ;;                      "BODY" ["STRUCTURE"] SPACE body /
2357 ;;                      "BODY" section ["<" number ">"] SPACE nstring /
2358 ;;                      "UID" SPACE uniqueid) ")"
2359 ;;
2360 ;;  date_time       ::= <"> date_day_fixed "-" date_month "-" date_year
2361 ;;                      SPACE time SPACE zone <">
2362 ;;
2363 ;;  section         ::= "[" [section_text / (nz_number *["." nz_number]
2364 ;;                      ["." (section_text / "MIME")])] "]"
2365 ;;
2366 ;;  section_text    ::= "HEADER" / "HEADER.FIELDS" [".NOT"]
2367 ;;                      SPACE header_list / "TEXT"
2368 ;;
2369 ;;  header_fld_name ::= astring
2370 ;;
2371 ;;  header_list     ::= "(" 1#header_fld_name ")"
2372
2373 (defsubst imap-parse-header-list ()
2374   (when (eq (char-after) ?\()
2375     (let (strlist)
2376       (while (not (eq (char-after) ?\)))
2377         (imap-forward)
2378         (push (imap-parse-astring) strlist))
2379       (imap-forward)
2380       (nreverse strlist))))
2381
2382 (defsubst imap-parse-fetch-body-section ()
2383   (let ((section
2384          (buffer-substring (point) (1- (re-search-forward "[] ]" nil t)))))
2385     (if (eq (char-before) ? )
2386         (prog1
2387             (mapconcat 'identity (cons section (imap-parse-header-list)) " ")
2388           (search-forward "]" nil t))
2389       section)))
2390
2391 (defun imap-parse-fetch (response)
2392   (when (eq (char-after) ?\()
2393     (let (uid flags envelope internaldate rfc822 rfc822header rfc822text
2394               rfc822size body bodydetail bodystructure flags-empty)
2395       (while (not (eq (char-after) ?\)))
2396         (imap-forward)
2397         (let ((token (read (current-buffer))))
2398           (imap-forward)
2399           (cond ((eq token 'UID)
2400                  (setq uid (condition-case ()
2401                                (read (current-buffer))
2402                              (error))))
2403                 ((eq token 'FLAGS)
2404                  (setq flags (imap-parse-flag-list))
2405                  (if (not flags)
2406                      (setq flags-empty 't)))
2407                 ((eq token 'ENVELOPE)
2408                  (setq envelope (imap-parse-envelope)))
2409                 ((eq token 'INTERNALDATE)
2410                  (setq internaldate (imap-parse-string)))
2411                 ((eq token 'RFC822)
2412                  (setq rfc822 (imap-parse-nstring)))
2413                 ((eq token 'RFC822.HEADER)
2414                  (setq rfc822header (imap-parse-nstring)))
2415                 ((eq token 'RFC822.TEXT)
2416                  (setq rfc822text (imap-parse-nstring)))
2417                 ((eq token 'RFC822.SIZE)
2418                  (setq rfc822size (read (current-buffer))))
2419                 ((eq token 'BODY)
2420                  (if (eq (char-before) ?\[)
2421                      (push (list
2422                             (upcase (imap-parse-fetch-body-section))
2423                             (and (eq (char-after) ?<)
2424                                  (buffer-substring (1+ (point))
2425                                                    (search-forward ">" nil t)))
2426                             (progn (imap-forward)
2427                                    (imap-parse-nstring)))
2428                            bodydetail)
2429                    (setq body (imap-parse-body))))
2430                 ((eq token 'BODYSTRUCTURE)
2431                  (setq bodystructure (imap-parse-body))))))
2432       (when uid
2433         (setq imap-current-message uid)
2434         (imap-message-put uid 'UID uid)
2435         (and (or flags flags-empty) (imap-message-put uid 'FLAGS flags))
2436         (and envelope (imap-message-put uid 'ENVELOPE envelope))
2437         (and internaldate (imap-message-put uid 'INTERNALDATE internaldate))
2438         (and rfc822 (imap-message-put uid 'RFC822 rfc822))
2439         (and rfc822header (imap-message-put uid 'RFC822.HEADER rfc822header))
2440         (and rfc822text (imap-message-put uid 'RFC822.TEXT rfc822text))
2441         (and rfc822size (imap-message-put uid 'RFC822.SIZE rfc822size))
2442         (and body (imap-message-put uid 'BODY body))
2443         (and bodydetail (imap-message-put uid 'BODYDETAIL bodydetail))
2444         (and bodystructure (imap-message-put uid 'BODYSTRUCTURE bodystructure))
2445         (run-hooks 'imap-fetch-data-hook)))))
2446
2447 ;;   mailbox-data    =  ...
2448 ;;                      "STATUS" SP mailbox SP "("
2449 ;;                            [status-att SP number
2450 ;;                            *(SP status-att SP number)] ")"
2451 ;;                      ...
2452 ;;
2453 ;;   status-att      = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" /
2454 ;;                     "UNSEEN"
2455
2456 (defun imap-parse-status ()
2457   (let ((mailbox (imap-parse-mailbox)))
2458     (if (eq (char-after) ? )
2459         (forward-char))
2460     (when (and mailbox (eq (char-after) ?\())
2461       (while (and (not (eq (char-after) ?\)))
2462                   (or (forward-char) t)
2463                   (looking-at "\\([A-Za-z]+\\) "))
2464         (let ((token (match-string 1)))
2465           (goto-char (match-end 0))
2466           (cond ((string= token "MESSAGES")
2467                  (imap-mailbox-put 'messages (read (current-buffer)) mailbox))
2468                 ((string= token "RECENT")
2469                  (imap-mailbox-put 'recent (read (current-buffer)) mailbox))
2470                 ((string= token "UIDNEXT")
2471                  (and (looking-at "[0-9]+")
2472                       (imap-mailbox-put 'uidnext (match-string 0) mailbox)
2473                       (goto-char (match-end 0))))
2474                 ((string= token "UIDVALIDITY")
2475                  (and (looking-at "[0-9]+")
2476                       (imap-mailbox-put 'uidvalidity (match-string 0) mailbox)
2477                       (goto-char (match-end 0))))
2478                 ((string= token "UNSEEN")
2479                  (imap-mailbox-put 'unseen (read (current-buffer)) mailbox))
2480                 (t
2481                  (message "Unknown status data %s in mailbox %s ignored"
2482                           token mailbox)
2483                  (read (current-buffer)))))))))
2484
2485 ;;   acl_data        ::= "ACL" SPACE mailbox *(SPACE identifier SPACE
2486 ;;                        rights)
2487 ;;
2488 ;;   identifier      ::= astring
2489 ;;
2490 ;;   rights          ::= astring
2491
2492 (defun imap-parse-acl ()
2493   (let ((mailbox (imap-parse-mailbox))
2494         identifier rights acl)
2495     (while (eq (char-after) ?\ )
2496       (imap-forward)
2497       (setq identifier (imap-parse-astring))
2498       (imap-forward)
2499       (setq rights (imap-parse-astring))
2500       (setq acl (append acl (list (cons identifier rights)))))
2501     (imap-mailbox-put 'acl acl mailbox)))
2502
2503 ;;   flag-list       = "(" [flag *(SP flag)] ")"
2504 ;;
2505 ;;   flag            = "\Answered" / "\Flagged" / "\Deleted" /
2506 ;;                     "\Seen" / "\Draft" / flag-keyword / flag-extension
2507 ;;                       ; Does not include "\Recent"
2508 ;;
2509 ;;   flag-keyword    = atom
2510 ;;
2511 ;;   flag-extension  = "\" atom
2512 ;;                       ; Future expansion.  Client implementations
2513 ;;                       ; MUST accept flag-extension flags.  Server
2514 ;;                       ; implementations MUST NOT generate
2515 ;;                       ; flag-extension flags except as defined by
2516 ;;                       ; future standard or standards-track
2517 ;;                       ; revisions of this specification.
2518
2519 (defun imap-parse-flag-list ()
2520   (let (flag-list start)
2521     (assert (eq (char-after) ?\() nil "In imap-parse-flag-list")
2522     (while (and (not (eq (char-after) ?\)))
2523                 (setq start (progn
2524                               (imap-forward)
2525                               ;; next line for Courier IMAP bug.
2526                               (skip-chars-forward " ")
2527                               (point)))
2528                 (> (skip-chars-forward "^ )" (point-at-eol)) 0))
2529       (push (buffer-substring start (point)) flag-list))
2530     (assert (eq (char-after) ?\)) nil "In imap-parse-flag-list")
2531     (imap-forward)
2532     (nreverse flag-list)))
2533
2534 ;;   envelope        = "(" env-date SP env-subject SP env-from SP env-sender SP
2535 ;;                     env-reply-to SP env-to SP env-cc SP env-bcc SP
2536 ;;                     env-in-reply-to SP env-message-id ")"
2537 ;;
2538 ;;   env-bcc         = "(" 1*address ")" / nil
2539 ;;
2540 ;;   env-cc          = "(" 1*address ")" / nil
2541 ;;
2542 ;;   env-date        = nstring
2543 ;;
2544 ;;   env-from        = "(" 1*address ")" / nil
2545 ;;
2546 ;;   env-in-reply-to = nstring
2547 ;;
2548 ;;   env-message-id  = nstring
2549 ;;
2550 ;;   env-reply-to    = "(" 1*address ")" / nil
2551 ;;
2552 ;;   env-sender      = "(" 1*address ")" / nil
2553 ;;
2554 ;;   env-subject     = nstring
2555 ;;
2556 ;;   env-to          = "(" 1*address ")" / nil
2557
2558 (defun imap-parse-envelope ()
2559   (when (eq (char-after) ?\()
2560     (imap-forward)
2561     (vector (prog1 (imap-parse-nstring) ;; date
2562               (imap-forward))
2563             (prog1 (imap-parse-nstring) ;; subject
2564               (imap-forward))
2565             (prog1 (imap-parse-address-list) ;; from
2566               (imap-forward))
2567             (prog1 (imap-parse-address-list) ;; sender
2568               (imap-forward))
2569             (prog1 (imap-parse-address-list) ;; reply-to
2570               (imap-forward))
2571             (prog1 (imap-parse-address-list) ;; to
2572               (imap-forward))
2573             (prog1 (imap-parse-address-list) ;; cc
2574               (imap-forward))
2575             (prog1 (imap-parse-address-list) ;; bcc
2576               (imap-forward))
2577             (prog1 (imap-parse-nstring) ;; in-reply-to
2578               (imap-forward))
2579             (prog1 (imap-parse-nstring) ;; message-id
2580               (imap-forward)))))
2581
2582 ;;   body-fld-param  = "(" string SP string *(SP string SP string) ")" / nil
2583
2584 (defsubst imap-parse-string-list ()
2585   (cond ((eq (char-after) ?\() ;; body-fld-param
2586          (let (strlist str)
2587            (imap-forward)
2588            (while (setq str (imap-parse-string))
2589              (push str strlist)
2590              ;; buggy stalker communigate pro 3.0 doesn't print SPC
2591              ;; between body-fld-param's sometimes
2592              (or (eq (char-after) ?\")
2593                  (imap-forward)))
2594            (nreverse strlist)))
2595         ((imap-parse-nil)
2596          nil)))
2597
2598 ;;   body-extension  = nstring / number /
2599 ;;                      "(" body-extension *(SP body-extension) ")"
2600 ;;                       ; Future expansion.  Client implementations
2601 ;;                       ; MUST accept body-extension fields.  Server
2602 ;;                       ; implementations MUST NOT generate
2603 ;;                       ; body-extension fields except as defined by
2604 ;;                       ; future standard or standards-track
2605 ;;                       ; revisions of this specification.
2606
2607 (defun imap-parse-body-extension ()
2608   (if (eq (char-after) ?\()
2609       (let (b-e)
2610         (imap-forward)
2611         (push (imap-parse-body-extension) b-e)
2612         (while (eq (char-after) ?\ )
2613           (imap-forward)
2614           (push (imap-parse-body-extension) b-e))
2615         (assert (eq (char-after) ?\)) nil "In imap-parse-body-extension")
2616         (imap-forward)
2617         (nreverse b-e))
2618     (or (imap-parse-number)
2619         (imap-parse-nstring))))
2620
2621 ;;   body-ext-1part  = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang
2622 ;;                     *(SP body-extension)]]
2623 ;;                       ; MUST NOT be returned on non-extensible
2624 ;;                       ; "BODY" fetch
2625 ;;
2626 ;;   body-ext-mpart  = body-fld-param [SP body-fld-dsp [SP body-fld-lang
2627 ;;                     *(SP body-extension)]]
2628 ;;                       ; MUST NOT be returned on non-extensible
2629 ;;                       ; "BODY" fetch
2630
2631 (defsubst imap-parse-body-ext ()
2632   (let (ext)
2633     (when (eq (char-after) ?\ ) ;; body-fld-dsp
2634       (imap-forward)
2635       (let (dsp)
2636         (if (eq (char-after) ?\()
2637             (progn
2638               (imap-forward)
2639               (push (imap-parse-string) dsp)
2640               (imap-forward)
2641               (push (imap-parse-string-list) dsp)
2642               (imap-forward))
2643           ;; With assert, the code might not be eval'd.
2644           ;; (assert (imap-parse-nil) t "In imap-parse-body-ext")
2645           (imap-parse-nil))
2646         (push (nreverse dsp) ext))
2647       (when (eq (char-after) ?\ ) ;; body-fld-lang
2648         (imap-forward)
2649         (if (eq (char-after) ?\()
2650             (push (imap-parse-string-list) ext)
2651           (push (imap-parse-nstring) ext))
2652         (while (eq (char-after) ?\ ) ;; body-extension
2653           (imap-forward)
2654           (setq ext (append (imap-parse-body-extension) ext)))))
2655     ext))
2656
2657 ;;   body            = "(" body-type-1part / body-type-mpart ")"
2658 ;;
2659 ;;   body-ext-1part  = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang
2660 ;;                     *(SP body-extension)]]
2661 ;;                       ; MUST NOT be returned on non-extensible
2662 ;;                       ; "BODY" fetch
2663 ;;
2664 ;;   body-ext-mpart  = body-fld-param [SP body-fld-dsp [SP body-fld-lang
2665 ;;                     *(SP body-extension)]]
2666 ;;                       ; MUST NOT be returned on non-extensible
2667 ;;                       ; "BODY" fetch
2668 ;;
2669 ;;   body-fields     = body-fld-param SP body-fld-id SP body-fld-desc SP
2670 ;;                     body-fld-enc SP body-fld-octets
2671 ;;
2672 ;;   body-fld-desc   = nstring
2673 ;;
2674 ;;   body-fld-dsp    = "(" string SP body-fld-param ")" / nil
2675 ;;
2676 ;;   body-fld-enc    = (DQUOTE ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
2677 ;;                     "QUOTED-PRINTABLE") DQUOTE) / string
2678 ;;
2679 ;;   body-fld-id     = nstring
2680 ;;
2681 ;;   body-fld-lang   = nstring / "(" string *(SP string) ")"
2682 ;;
2683 ;;   body-fld-lines  = number
2684 ;;
2685 ;;   body-fld-md5    = nstring
2686 ;;
2687 ;;   body-fld-octets = number
2688 ;;
2689 ;;   body-fld-param  = "(" string SP string *(SP string SP string) ")" / nil
2690 ;;
2691 ;;   body-type-1part = (body-type-basic / body-type-msg / body-type-text)
2692 ;;                     [SP body-ext-1part]
2693 ;;
2694 ;;   body-type-basic = media-basic SP body-fields
2695 ;;                       ; MESSAGE subtype MUST NOT be "RFC822"
2696 ;;
2697 ;;   body-type-msg   = media-message SP body-fields SP envelope
2698 ;;                     SP body SP body-fld-lines
2699 ;;
2700 ;;   body-type-text  = media-text SP body-fields SP body-fld-lines
2701 ;;
2702 ;;   body-type-mpart = 1*body SP media-subtype
2703 ;;                     [SP body-ext-mpart]
2704 ;;
2705 ;;   media-basic     = ((DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" /
2706 ;;                     "MESSAGE" / "VIDEO") DQUOTE) / string) SP media-subtype
2707 ;;                       ; Defined in [MIME-IMT]
2708 ;;
2709 ;;   media-message   = DQUOTE "MESSAGE" DQUOTE SP DQUOTE "RFC822" DQUOTE
2710 ;;                      ; Defined in [MIME-IMT]
2711 ;;
2712 ;;   media-subtype   = string
2713 ;;                       ; Defined in [MIME-IMT]
2714 ;;
2715 ;;   media-text      = DQUOTE "TEXT" DQUOTE SP media-subtype
2716 ;;                       ; Defined in [MIME-IMT]
2717
2718 (defun imap-parse-body ()
2719   (let (body)
2720     (when (eq (char-after) ?\()
2721       (imap-forward)
2722       (if (eq (char-after) ?\()
2723           (let (subbody)
2724             (while (and (eq (char-after) ?\()
2725                         (setq subbody (imap-parse-body)))
2726              ;; buggy stalker communigate pro 3.0 insert a SPC between
2727               ;; parts in multiparts
2728               (when (and (eq (char-after) ?\ )
2729                          (eq (char-after (1+ (point))) ?\())
2730                 (imap-forward))
2731               (push subbody body))
2732             (imap-forward)
2733             (push (imap-parse-string) body) ;; media-subtype
2734             (when (eq (char-after) ?\ ) ;; body-ext-mpart:
2735               (imap-forward)
2736               (if (eq (char-after) ?\() ;; body-fld-param
2737                   (push (imap-parse-string-list) body)
2738                 (push (and (imap-parse-nil) nil) body))
2739               (setq body
2740                     (append (imap-parse-body-ext) body))) ;; body-ext-...
2741             (assert (eq (char-after) ?\)) nil "In imap-parse-body")
2742             (imap-forward)
2743             (nreverse body))
2744
2745         (push (imap-parse-string) body) ;; media-type
2746         (imap-forward)
2747         (push (imap-parse-string) body) ;; media-subtype
2748         (imap-forward)
2749         ;; next line for Sun SIMS bug
2750         (and (eq (char-after) ? ) (imap-forward))
2751         (if (eq (char-after) ?\() ;; body-fld-param
2752             (push (imap-parse-string-list) body)
2753           (push (and (imap-parse-nil) nil) body))
2754         (imap-forward)
2755         (push (imap-parse-nstring) body) ;; body-fld-id
2756         (imap-forward)
2757         (push (imap-parse-nstring) body) ;; body-fld-desc
2758         (imap-forward)
2759         ;; next `or' for Sun SIMS bug, it regard body-fld-enc as a
2760         ;; nstring and return nil instead of defaulting back to 7BIT
2761         ;; as the standard says.
2762         (push (or (imap-parse-nstring) "7BIT") body) ;; body-fld-enc
2763         (imap-forward)
2764         (push (imap-parse-number) body) ;; body-fld-octets
2765
2766    ;; ok, we're done parsing the required parts, what comes now is one
2767         ;; of three things:
2768         ;;
2769         ;; envelope       (then we're parsing body-type-msg)
2770         ;; body-fld-lines (then we're parsing body-type-text)
2771         ;; body-ext-1part (then we're parsing body-type-basic)
2772         ;;
2773   ;; the problem is that the two first are in turn optionally followed
2774 ;; by the third.  So we parse the first two here (if there are any)...
2775
2776         (when (eq (char-after) ?\ )
2777           (imap-forward)
2778           (let (lines)
2779             (cond ((eq (char-after) ?\() ;; body-type-msg:
2780                    (push (imap-parse-envelope) body) ;; envelope
2781                    (imap-forward)
2782                    (push (imap-parse-body) body) ;; body
2783                    ;; buggy stalker communigate pro 3.0 doesn't print
2784                    ;; number of lines in message/rfc822 attachment
2785                    (if (eq (char-after) ?\))
2786                        (push 0 body)
2787                      (imap-forward)
2788                      (push (imap-parse-number) body))) ;; body-fld-lines
2789                   ((setq lines (imap-parse-number)) ;; body-type-text:
2790                    (push lines body)) ;; body-fld-lines
2791                   (t
2792                    (backward-char))))) ;; no match...
2793
2794         ;; ...and then parse the third one here...
2795
2796         (when (eq (char-after) ?\ ) ;; body-ext-1part:
2797           (imap-forward)
2798           (push (imap-parse-nstring) body) ;; body-fld-md5
2799           (setq body (append (imap-parse-body-ext) body))) ;; body-ext-1part..
2800
2801         (assert (eq (char-after) ?\)) nil "In imap-parse-body 2")
2802         (imap-forward)
2803         (nreverse body)))))
2804
2805 (when imap-debug                        ; (untrace-all)
2806   (require 'trace)
2807   (buffer-disable-undo (get-buffer-create imap-debug-buffer))
2808   (mapcar (lambda (f) (trace-function-background f imap-debug-buffer))
2809           '(
2810             imap-utf7-encode
2811             imap-utf7-decode
2812             imap-error-text
2813             imap-kerberos4s-p
2814             imap-kerberos4-open
2815             imap-ssl-p
2816             imap-ssl-open
2817             imap-network-p
2818             imap-network-open
2819             imap-interactive-login
2820             imap-kerberos4a-p
2821             imap-kerberos4-auth
2822             imap-cram-md5-p
2823             imap-cram-md5-auth
2824             imap-login-p
2825             imap-login-auth
2826             imap-anonymous-p
2827             imap-anonymous-auth
2828             imap-open-1
2829             imap-open
2830             imap-opened
2831             imap-authenticate
2832             imap-close
2833             imap-capability
2834             imap-namespace
2835             imap-send-command-wait
2836             imap-mailbox-put
2837             imap-mailbox-get
2838             imap-mailbox-map-1
2839             imap-mailbox-map
2840             imap-current-mailbox
2841             imap-current-mailbox-p-1
2842             imap-current-mailbox-p
2843             imap-mailbox-select-1
2844             imap-mailbox-select
2845             imap-mailbox-examine-1
2846             imap-mailbox-examine
2847             imap-mailbox-unselect
2848             imap-mailbox-expunge
2849             imap-mailbox-close
2850             imap-mailbox-create-1
2851             imap-mailbox-create
2852             imap-mailbox-delete
2853             imap-mailbox-rename
2854             imap-mailbox-lsub
2855             imap-mailbox-list
2856             imap-mailbox-subscribe
2857             imap-mailbox-unsubscribe
2858             imap-mailbox-status
2859             imap-mailbox-acl-get
2860             imap-mailbox-acl-set
2861             imap-mailbox-acl-delete
2862             imap-current-message
2863             imap-list-to-message-set
2864             imap-fetch-asynch
2865             imap-fetch
2866             imap-message-put
2867             imap-message-get
2868             imap-message-map
2869             imap-search
2870             imap-message-flag-permanent-p
2871             imap-message-flags-set
2872             imap-message-flags-del
2873             imap-message-flags-add
2874             imap-message-copyuid-1
2875             imap-message-copyuid
2876             imap-message-copy
2877             imap-message-appenduid-1
2878             imap-message-appenduid
2879             imap-message-append
2880             imap-body-lines
2881             imap-envelope-from
2882             imap-send-command-1
2883             imap-send-command
2884             imap-wait-for-tag
2885             imap-sentinel
2886             imap-find-next-line
2887             imap-arrival-filter
2888             imap-parse-greeting
2889             imap-parse-response
2890             imap-parse-resp-text
2891             imap-parse-resp-text-code
2892             imap-parse-data-list
2893             imap-parse-fetch
2894             imap-parse-status
2895             imap-parse-acl
2896             imap-parse-flag-list
2897             imap-parse-envelope
2898             imap-parse-body-extension
2899             imap-parse-body
2900             )))
2901
2902 (provide 'imap)
2903
2904 ;;; arch-tag: 27369ed6-33e4-482f-96f1-8bb906ba70f7
2905 ;;; imap.el ends here