Require `cl' using `eval-when-compile'.
[elisp/gnus.git-] / lisp / imap.el
1 ;;; imap.el --- imap library
2 ;; Copyright (C) 1998,1999 Free Software Foundation, Inc.
3
4 ;; Author: Simon Josefsson <jas@pdc.kth.se>
5 ;; Keywords: mail
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25
26 ;; imap.el is a elisp library providing an interface for talking to
27 ;; IMAP servers.
28 ;;
29 ;; imap.el is roughly divided in two parts, one that parses IMAP
30 ;; responses from the server and storing data into buffer-local
31 ;; variables, and one for utility functions which send commands to
32 ;; server, waits for an answer, and return information. The latter
33 ;; part is layered on top of the previous.
34 ;;
35 ;; The imap.el API consist of the following functions, other functions
36 ;; in this file should not be called directly and the result of doing
37 ;; so are at best undefined.
38 ;;
39 ;; Global commands:
40 ;;
41 ;; imap-open,       imap-opened,    imap-authenticate, imap-close,
42 ;; imap-capability, imap-namespace, imap-error-text
43 ;;
44 ;; Mailbox commands:
45 ;;
46 ;; imap-mailbox-get,       imap-mailbox-map,         imap-current-mailbox, 
47 ;; imap-current-mailbox-p, imap-search,              imap-mailbox-select,
48 ;; imap-mailbox-examine,   imap-mailbox-unselect,    imap-mailbox-expunge
49 ;; imap-mailbox-close,     imap-mailbox-create,      imap-mailbox-delete
50 ;; imap-mailbox-rename,    imap-mailbox-lsub,        imap-mailbox-list
51 ;; imap-mailbox-subscribe, imap-mailbox-unsubscribe, imap-mailbox-status
52 ;; imap-mailbox-acl-get,   imap-mailbox-acl-set,     imap-mailbox-acl-delete
53 ;;
54 ;; Message commands:
55 ;;
56 ;; imap-fetch-asynch,                 imap-fetch,
57 ;; imap-current-message,              imap-list-to-message-set,
58 ;; imap-message-get,                  imap-message-map
59 ;; imap-message-envelope-date,        imap-message-envelope-subject, 
60 ;; imap-message-envelope-from,        imap-message-envelope-sender,
61 ;; imap-message-envelope-reply-to,    imap-message-envelope-to,
62 ;; imap-message-envelope-cc,          imap-message-envelope-bcc
63 ;; imap-message-envelope-in-reply-to, imap-message-envelope-message-id
64 ;; imap-message-body,                 imap-message-flag-permanent-p
65 ;; imap-message-flags-set,            imap-message-flags-del
66 ;; imap-message-flags-add,            imap-message-copyuid
67 ;; imap-message-copy,                 imap-message-appenduid
68 ;; imap-message-append,               imap-envelope-from
69 ;; imap-body-lines
70 ;;
71 ;; It is my hope that theese commands should be pretty self
72 ;; explanatory for someone that know IMAP. All functions have
73 ;; additional documentation on how to invoke them.
74 ;;
75 ;; imap.el support RFC1730/2060 (IMAP4/IMAP4rev1), implemented IMAP
76 ;; extensions are RFC2195 (CRAM-MD5), RFC2086 (ACL), RFC2342
77 ;; (NAMESPACE), RFC2359 (UIDPLUS), and the kerberos V4 part of RFC1731
78 ;; (with use of external program `imtest').  It also take advantage
79 ;; the UNSELECT extension in Cyrus IMAPD.
80 ;;
81 ;; Without the work of John McClary Prevost and Jim Radford this library
82 ;; would not have seen the light of day. Many thanks.
83 ;;
84 ;; This is a transcript of short interactive session for demonstration
85 ;; purposes.
86 ;;
87 ;; (imap-open "my.mail.server")
88 ;; => " *imap* my.mail.server:0"
89 ;;
90 ;; The rest are invoked with current buffer as the buffer returned by
91 ;; `imap-open'. It is possible to do all without this, but it would
92 ;; look ugly here since `buffer' is always the last argument for all
93 ;; imap.el API functions.
94 ;;
95 ;; (imap-authenticate "myusername" "mypassword")
96 ;; => auth
97 ;;
98 ;; (imap-mailbox-lsub "*")
99 ;; => ("INBOX.sentmail" "INBOX.private" "INBOX.draft" "INBOX.spam")
100 ;;
101 ;; (imap-mailbox-list "INBOX.n%")
102 ;; => ("INBOX.namedroppers" "INBOX.nnimap" "INBOX.ntbugtraq")
103 ;;
104 ;; (imap-mailbox-select "INBOX.nnimap")
105 ;; => "INBOX.nnimap"
106 ;;
107 ;; (imap-mailbox-get 'exists)
108 ;; => 166
109 ;;
110 ;; (imap-mailbox-get 'uidvalidity)
111 ;; => "908992622"
112 ;;
113 ;; (imap-search "FLAGGED SINCE 18-DEC-98")
114 ;; => (235 236)
115 ;;
116 ;; (imap-fetch 235 "RFC822.PEEK" 'RFC822)
117 ;; => "X-Sieve: cmu-sieve 1.3^M\nX-Username: <jas@pdc.kth.se>^M\r...."
118 ;;
119 ;; Todo:
120 ;; 
121 ;; o Parse UIDs as strings? We need to overcome the 28 bit limit somehow.
122 ;; o Don't use `read' at all (important places already fixed)
123 ;; o Accept list of articles instead of message set string in most
124 ;;   imap-message-* functions.
125 ;; o Cyrus IMAPd 1.6.x `imtest' support in the imtest wrapper
126 ;; o Format-spec'ify the ssl horror
127 ;;
128 ;; Revision history:
129 ;;
130 ;;  - this is unreleased software
131 ;;
132
133 ;;; Code:
134
135 (eval-when-compile (require 'cl))
136
137 (eval-and-compile
138   (autoload 'open-ssl-stream "ssl")
139   (autoload 'starttls-open-stream "starttls")
140   (autoload 'starttls-negotiate "starttls")
141   (autoload 'base64-decode-string "mel")
142   (autoload 'base64-encode-string "mel")
143   (autoload 'rfc2104-hash "rfc2104")
144   (autoload 'utf7-encode "utf7")
145   (autoload 'utf7-decode "utf7")
146   (autoload 'format-spec "format-spec")
147   (autoload 'format-spec-make "format-spec"))
148
149 (autoload 'md5 "md5")
150
151 ;; User variables.
152
153 (defvar imap-imtest-program "imtest -kp %s %p"
154   "How to call program for Kerberos 4 authentication.
155 %s is replaced with server and %p with port to connect to.  The
156 program should accept IMAP commands on stdin and return responses to
157 stdout.")
158
159 (defvar imap-ssl-program 'auto
160   "Program to use for SSL connections. It is called like this
161
162 `imap-ssl-program' `imap-ssl-arguments' -ssl2 -connect host:port
163
164 where -ssl2 can also be -ssl3 to indicate which ssl version to use. It
165 should accept IMAP commands on stdin and return responses to stdout.
166
167 For SSLeay set this to \"s_client\" and `imap-ssl-arguments' to nil,
168 for OpenSSL set this to \"openssl\" and `imap-ssl-arguments' to
169 \"s_client\".
170
171 If 'auto it tries s_client first and then openssl.")
172
173 (defvar imap-ssl-arguments nil
174   "Arguments to pass to `imap-ssl-program'.
175
176 For SSLeay set this to nil, for OpenSSL to \"s_client\".
177
178 If `imap-ssl-program' is 'auto this variable has no effect.")
179
180 (defvar imap-default-user (user-login-name)
181   "Default username to use.")
182
183 (defvar imap-error nil
184   "Error codes from the last command.")
185
186 ;; Various variables.
187
188 (defvar imap-fetch-data-hook nil
189   "Hooks called after receiving each FETCH response.")
190
191 (defvar imap-streams '(kerberos4 ssl network)
192   "Priority of streams to consider when opening connection to
193 server.")
194
195 (defvar imap-stream-alist
196   '((kerberos4 imap-kerberos4s-p imap-kerberos4-open)
197     (ssl       imap-ssl-p        imap-ssl-open)
198     (network   imap-network-p    imap-network-open)
199     (tls       imap-tls-p        imap-tls-open))
200   "Definition of network streams.
201
202 (NAME CHECK OPEN)
203
204 NAME names the stream, CHECK is a function returning non-nil if the
205 server support the stream and OPEN is a function for opening the
206 stream.")
207
208 (defvar imap-authenticators '(kerberos4 cram-md5 login anonymous)
209   "Priority of authenticators to consider when authenticating to
210 server.")
211
212 (defvar imap-authenticator-alist 
213   '((kerberos4 imap-kerberos4a-p imap-kerberos4-auth)
214     (cram-md5  imap-cram-md5-p   imap-cram-md5-auth)
215     (login     imap-login-p      imap-login-auth)
216     (anonymous imap-anonymous-p  imap-anonymous-auth))
217   "Definition of authenticators.
218
219 (NAME CHECK AUTHENTICATE)
220
221 NAME names the authenticator. CHECK is a function returning non-nil if
222 the server support the authenticator and AUTHENTICATE is a function
223 for doing the actuall authentification.")
224
225 (defvar imap-utf7-p nil
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
231 ;; Internal constants. Change theese and die.
232
233 (defconst imap-default-port 143)
234 (defconst imap-default-ssl-port 993)
235 (defconst imap-default-stream 'network)
236 (defconst imap-local-variables '(imap-server
237                                  imap-port
238                                  imap-client-eol
239                                  imap-server-eol
240                                  imap-auth
241                                  imap-stream
242                                  imap-username
243                                  imap-password
244                                  imap-current-mailbox
245                                  imap-current-target-mailbox
246                                  imap-message-data
247                                  imap-capability
248                                  imap-namespace
249                                  imap-state
250                                  imap-reached-tag
251                                  imap-failed-tags
252                                  imap-tag
253                                  imap-process
254                                  imap-mailbox-data))
255
256 ;; Internal variables.
257
258 (defvar imap-stream nil)
259 (defvar imap-auth nil)
260 (defvar imap-server nil)
261 (defvar imap-port nil)
262 (defvar imap-username nil)
263 (defvar imap-password nil)
264 (defvar imap-state 'closed 
265   "IMAP state. Valid states are `closed', `initial', `nonauth',
266 `auth', `selected' and `examine'.")
267
268 (defvar imap-server-eol "\r\n"
269   "The EOL string sent from the server.")
270
271 (defvar imap-client-eol "\r\n"
272   "The EOL string we send to the server.")
273
274 (defvar imap-current-mailbox nil
275   "Current mailbox name.")
276
277 (defvar imap-current-target-mailbox nil
278   "Current target mailbox for COPY and APPEND commands.")
279
280 (defvar imap-mailbox-data nil
281   "Obarray with mailbox data.")
282
283 (defvar imap-mailbox-prime 997
284   "Length of imap-mailbox-data.")
285
286 (defvar imap-current-message nil
287   "Current message number.")
288
289 (defvar imap-message-data nil
290   "Obarray with message data.")
291
292 (defvar imap-message-prime 997
293   "Length of imap-message-data.")
294
295 (defvar imap-capability nil
296   "Capability for server.")
297
298 (defvar imap-namespace nil
299   "Namespace for current server.")
300
301 (defvar imap-reached-tag 0
302   "Lower limit on command tags that have been parsed.")
303
304 (defvar imap-failed-tags nil 
305   "Alist of tags that failed. Each element is a list with four
306 elements; tag (a integer), response state (a symbol, `OK', `NO' or
307 `BAD'), response code (a string), and human readable response text (a
308 string).")
309
310 (defvar imap-tag 0
311   "Command tag number.")
312
313 (defvar imap-process nil
314   "Process.")
315
316 (defvar imap-continuation nil
317   "Non-nil indicates that the server emitted a continuation request. The
318 actually value is really the text on the continuation line.")
319
320 (defvar imap-log nil
321   "Imap session trace.")
322
323 (defvar imap-debug nil;"*imap-debug*"
324   "Random debug spew.")
325
326 \f
327 ;; Utility functions:
328
329 (defun imap-read-passwd (prompt &rest args)
330   "Read a password using PROMPT. If ARGS, PROMPT is used as an
331 argument to `format'."
332   (let ((prompt (if args
333                     (apply 'format prompt args)
334                   prompt)))
335     (funcall (if (or (fboundp 'read-passwd)
336                      (and (load "subr" t)
337                           (fboundp 'read-passwd))
338                      (and (load "passwd" t)
339                           (fboundp 'read-passwd)))
340                  'read-passwd
341                (autoload 'ange-ftp-read-passwd "ange-ftp")
342                'ange-ftp-read-passwd)
343              prompt)))
344
345 (defsubst imap-utf7-encode (string)
346   (if imap-utf7-p
347       (and string
348            (condition-case ()
349                (utf7-encode string t)
350              (error (message 
351                      "imap: Could not UTF7 encode `%s', using it unencoded..."
352                      string)
353                     string)))
354     string))
355
356 (defsubst imap-utf7-decode (string)
357   (if imap-utf7-p
358       (and string
359            (condition-case ()
360                (utf7-decode string t)
361              (error (message
362                      "imap: Could not UTF7 decode `%s', using it undecoded..."
363                      string)
364                     string)))
365     string))
366
367 (defsubst imap-ok-p (status)
368   (if (eq status 'OK)
369       t
370     (setq imap-error status)
371     nil))
372
373 (defun imap-error-text (&optional buffer)
374   (with-current-buffer (or buffer (current-buffer))
375     (nth 3 (car imap-failed-tags))))
376
377 \f
378 ;; Server functions; stream stuff:
379
380 (defun imap-kerberos4s-p (buffer)
381   (imap-capability 'AUTH=KERBEROS_V4 buffer))
382
383 (defun imap-kerberos4-open (name buffer server port)
384   (message "Opening Kerberized IMAP connection...")
385   (let* ((port (or port imap-default-port))
386          (process (as-binary-process
387                    (start-process
388                     name buffer shell-file-name shell-command-switch
389                     (format-spec
390                      imap-imtest-program
391                      (format-spec-make ?s server ?p (number-to-string port))
392                      )))))
393     (when process
394       (with-current-buffer buffer
395         (setq imap-client-eol "\n")
396         ;; Result of authentication is a string: __Full privacy protection__
397         (while (and (memq (process-status process) '(open run))
398                     (goto-char (point-min))
399                     (not (and (imap-parse-greeting)
400                               (re-search-forward "__\\(.*\\)__\n" nil t))))
401           (accept-process-output process 1)
402           (sit-for 1))
403         (and imap-log
404              (with-current-buffer (get-buffer-create imap-log)
405                (buffer-disable-undo)
406                (goto-char (point-max))
407                (insert-buffer-substring buffer)))
408       (let ((response (match-string 1)))
409         (erase-buffer)
410         (message "Kerberized IMAP connection: %s" response)
411         (if (and response (let ((case-fold-search nil))
412                             (not (string-match "failed" response))))
413             process
414           (if (memq (process-status process) '(open run))
415               (imap-send-command-wait "LOGOUT"))
416           (delete-process process)
417           nil))))))
418   
419 (defun imap-ssl-p (buffer)
420   nil)
421
422 (defun imap-ssl-open-2 (name buffer server port &optional extra-ssl-args)
423   (let* ((port (or port imap-default-ssl-port))
424          (ssl-program-name imap-ssl-program)
425          (ssl-program-arguments (append imap-ssl-arguments extra-ssl-args
426                                         (list "-connect" 
427                                               (format "%s:%d" server port))))
428          (process (ignore-errors 
429                     (as-binary-process 
430                      (open-ssl-stream name buffer server port)))))
431     (when process
432       (with-current-buffer buffer
433         (goto-char (point-min))
434         (while (and (memq (process-status process) '(open run))
435                     (goto-char (point-max))
436                     (forward-line -1)
437                     (not (imap-parse-greeting)))
438           (accept-process-output process 1)
439           (sit-for 1))
440         (and imap-log
441              (with-current-buffer (get-buffer-create imap-log)
442                (buffer-disable-undo)
443                (goto-char (point-max))
444                (insert-buffer-substring buffer)))
445         (erase-buffer))
446       (when (memq (process-status process) '(open run))
447         process))))
448
449 (defun imap-ssl-open-1 (name buffer server port &optional extra-ssl-args)
450   (or (and (eq imap-ssl-program 'auto)
451            (let ((imap-ssl-program "s_client")
452                  (imap-ssl-arguments nil))
453              (message "imap: Opening IMAP connection with %s %s..."
454                       imap-ssl-program (car-safe extra-ssl-args))
455              (imap-ssl-open-2 name buffer server port extra-ssl-args)))
456       (and (eq imap-ssl-program 'auto)
457            (let ((imap-ssl-program "openssl")
458                  (imap-ssl-arguments '("s_client")))
459              (message "imap: Opening IMAP connection with %s %s..."
460                       imap-ssl-program (car-safe extra-ssl-args))
461              (imap-ssl-open-2 name buffer server port extra-ssl-args)))
462       (and (not (eq imap-ssl-program 'auto))
463            (progn (message "imap: Opening IMAP connection with %s %s..."
464                            imap-ssl-program (car-safe extra-ssl-args))
465                   (imap-ssl-open-2 name buffer server port extra-ssl-args)))))
466            
467 (defun imap-ssl-open (name buffer server port)
468   (or (imap-ssl-open-1 name buffer server port '("-ssl3"))
469       (imap-ssl-open-1 name buffer server port '("-ssl2"))))
470
471 (defun imap-network-p (buffer)
472   t)
473
474 (defun imap-network-open (name buffer server port)
475   (let* ((port (or port imap-default-port))
476          (process (open-network-stream-as-binary name buffer server port)))
477     (when process
478       (while (and (memq (process-status process) '(open run))
479                   (goto-char (point-min))
480                   (not (imap-parse-greeting)))
481         (accept-process-output process 1)
482         (sit-for 1))
483       (and imap-log
484            (with-current-buffer (get-buffer-create imap-log)
485              (buffer-disable-undo)
486              (goto-char (point-max))
487              (insert-buffer-substring buffer)))
488       (when (memq (process-status process) '(open run))
489         process))))
490
491 (defun imap-tls-p (buffer)
492   (imap-capability 'STARTTLS buffer))
493
494 (defun imap-tls-open (name buffer server port)
495   (let* ((port (or port imap-default-port))
496          (process (as-binary-process
497                    (starttls-open-stream name buffer server port))))
498     (when process
499       (while (and (memq (process-status process) '(open run))
500                   (goto-char (point-min))
501                   (not (imap-parse-greeting)))
502         (accept-process-output process 1)
503         (sit-for 1))
504       (and imap-log
505            (with-current-buffer (get-buffer-create imap-log)
506              (buffer-disable-undo)
507              (goto-char (point-max))
508              (insert-buffer-substring buffer)))
509       (let ((imap-process process))
510         (unwind-protect
511             (progn
512               (set-process-filter imap-process 'imap-arrival-filter)
513               (when (and (eq imap-stream 'tls)
514                          (imap-ok-p (imap-send-command-wait "STARTTLS")))
515                 (starttls-negotiate imap-process)))
516           (set-process-filter imap-process nil)))
517       (when (memq (process-status process) '(open run))
518         process))))
519   
520 ;; Server functions; authenticator stuff:
521
522 (defun imap-interactive-login (buffer loginfunc)
523   "Login to server in BUFFER. LOGINFUNC is passed a username and a
524 password, it should return t if it where sucessful authenticating
525 itself to the server, nil otherwise. Returns t if login was
526 successful, nil otherwise."
527   (with-current-buffer buffer
528     (make-variable-buffer-local 'imap-username)
529     (make-variable-buffer-local 'imap-password)
530     (let (user passwd ret)
531 ;;      (condition-case ()
532           (while (or (not user) (not passwd))
533             (setq user (or imap-username
534                            (read-from-minibuffer 
535                             (concat "IMAP username for " imap-server ": ")
536                             (or user imap-default-user))))
537             (setq passwd (or imap-password
538                              (imap-read-passwd
539                               (concat "IMAP password for " user "@" 
540                                       imap-server ": "))))
541             (when (and user passwd)
542               (if (funcall loginfunc user passwd)
543                   (progn
544                     (setq ret t
545                           imap-username user)
546                     (if (and (not imap-password)
547                              (y-or-n-p "Store password for this session? "))
548                         (setq imap-password passwd)))
549                 (message "Login failed...")
550                 (setq passwd nil)
551                 (sit-for 1))))
552 ;;      (quit (with-current-buffer buffer
553 ;;              (setq user nil
554 ;;                    passwd nil)))
555 ;;      (error (with-current-buffer buffer
556 ;;               (setq user nil
557 ;;                     passwd nil))))
558       ret)))
559
560 (defun imap-kerberos4a-p (buffer)
561   (imap-capability 'AUTH=KERBEROS_V4 buffer))
562
563 (defun imap-kerberos4-auth (buffer)
564   (eq imap-stream 'kerberos4))
565
566 (defun imap-cram-md5-p (buffer)
567   (imap-capability 'AUTH=CRAM-MD5 buffer))
568
569 (defun imap-cram-md5-auth (buffer)
570   "Login to server using the AUTH CRAM-MD5 method."
571   (imap-interactive-login
572    buffer
573    (lambda (user passwd)
574      (imap-ok-p
575       (imap-send-command-wait
576        (list
577         "AUTHENTICATE CRAM-MD5"
578         (lambda (challenge)
579           (let* ((decoded (base64-decode-string challenge))
580                  (hash-function (if (and (featurep 'xemacs)
581                                          (>= (function-max-args 'md5) 4))
582                                     (lambda (object &optional start end)
583                                       (md5 object start end 'binary))
584                                   'md5))
585                  (hash (rfc2104-hash hash-function 64 16 passwd decoded))
586                  (response (concat user " " hash))
587                  (encoded (base64-encode-string response)))
588             encoded))))))))
589
590 (defun imap-login-p (buffer)
591   (not (imap-capability 'X-LOGIN-CMD-DISABLED buffer)))
592
593 (defun imap-login-auth (buffer)
594   "Login to server using the LOGIN command."
595   (imap-interactive-login buffer 
596                           (lambda (user passwd)
597                             (imap-ok-p (imap-send-command-wait 
598                                         (concat "LOGIN \"" user "\" \"" 
599                                                 passwd "\""))))))
600
601 (defun imap-anonymous-p (buffer)
602   t)
603
604 (defun imap-anonymous-auth (buffer)
605   (with-current-buffer buffer
606     (imap-ok-p (imap-send-command-wait
607                 (concat "LOGIN anonymous \"" (concat (user-login-name) "@" 
608                                                      (system-name)) "\"")))))
609
610 ;; Server functions:
611
612 (defun imap-open-1 (buffer)
613   (with-current-buffer buffer
614     (erase-buffer)
615     (setq imap-current-mailbox nil
616           imap-current-message nil
617           imap-state 'initial
618           imap-process (condition-case ()
619                            (funcall (nth 2 (assq imap-stream 
620                                                  imap-stream-alist))
621                                     "imap" buffer imap-server imap-port)
622                          ((error quit) nil)))
623     (when imap-process
624       (set-process-filter imap-process 'imap-arrival-filter)
625       (set-process-sentinel imap-process 'imap-sentinel)
626       (while (and (eq imap-state 'initial)
627                   (memq (process-status imap-process) '(open run)))
628         (message "Waiting for response from %s..." imap-server)
629         (accept-process-output imap-process 1))
630       (message "Waiting for response from %s...done" imap-server)
631       (and (memq (process-status imap-process) '(open run))
632            imap-process))))
633
634 (defun imap-open (server &optional port stream auth buffer)
635   "Open a IMAP connection to host SERVER at PORT returning a
636 buffer. If PORT is unspecified, a default value is used (143 except
637 for SSL which use 993).
638 STREAM indicates the stream to use, see `imap-streams' for available
639 streams. If nil, it choices the best stream the server is capable of.
640 AUTH indicates authenticator to use, see `imap-authenticators' for
641 available authenticators. If nil, it choices the best stream the
642 server is capable of.
643 BUFFER can be a buffer or a name of a buffer, which is created if
644 necessery. If nil, the buffer name is generated."
645   (setq buffer (or buffer (format " *imap* %s:%d" server (or port 0))))
646   (with-current-buffer (get-buffer-create buffer)
647     (if (imap-opened buffer)
648         (imap-close buffer))
649     (mapc 'make-variable-buffer-local imap-local-variables)
650     (buffer-disable-undo)
651     (setq imap-server (or server imap-server))
652     (setq imap-port (or port imap-port))
653     (setq imap-auth (or auth imap-auth))
654     (setq imap-stream (or stream imap-stream))
655     (when (let ((imap-stream (or imap-stream imap-default-stream)))
656             (imap-open-1 buffer))
657       ;; Choose stream.
658       (let (stream-changed)
659         (when (null imap-stream)
660           (let ((streams imap-streams))
661             (while (setq stream (pop streams))
662               (if (funcall (nth 1 (assq stream imap-stream-alist)) buffer)
663                   (setq stream-changed (not (eq (or imap-stream 
664                                                     imap-default-stream)
665                                                 stream))
666                         imap-stream stream
667                         streams nil)))
668             (unless imap-stream
669               (error "Couldn't figure out a stream for server"))))
670         (when stream-changed
671           (message "Reconnecting with %s..." imap-stream)
672           (imap-close buffer)
673           (imap-open-1 buffer)
674           (setq imap-capability nil)))
675       (if (imap-opened buffer)
676           ;; Choose authenticator
677           (when (null imap-auth)
678             (let ((auths imap-authenticators))
679               (while (setq auth (pop auths))
680                 (if (funcall (nth 1 (assq auth imap-authenticator-alist)) 
681                              buffer)
682                     (setq imap-auth auth
683                           auths nil)))
684               (unless imap-auth
685                 (error "Couldn't figure out authenticator for server"))))))
686     (when (imap-opened buffer)
687       (setq imap-mailbox-data (make-vector imap-mailbox-prime 0))
688       buffer)))
689
690 (defun imap-opened (&optional buffer)
691   "Return non-nil if connection to imap server in BUFFER is open. If
692 BUFFER is nil then the current buffer is used."
693   (and (setq buffer (get-buffer (or buffer (current-buffer))))
694        (buffer-live-p buffer)
695        (with-current-buffer buffer
696          (and imap-process
697               (memq (process-status imap-process) '(open run))))))
698
699 (defun imap-authenticate (&optional user passwd buffer)
700   "Authenticate to server in BUFFER, using current buffer if nil. It
701 uses the authenticator specified when opening the server. If the
702 authenticator requires username/passwords, they are queried from the
703 user and optionally stored in the buffer.  If USER and/or PASSWD is
704 specified, the user will not be questioned and the username and/or
705 password is remembered in the buffer."
706   (with-current-buffer (or buffer (current-buffer))
707     (when (eq imap-state 'nonauth)
708       (make-variable-buffer-local 'imap-username)
709       (make-variable-buffer-local 'imap-password)
710       (if user (setq imap-username user))
711       (if passwd (setq imap-password passwd))
712       (if (funcall (nth 2 (assq imap-auth imap-authenticator-alist)) buffer)
713           (setq imap-state 'auth)))))
714
715 (defun imap-close (&optional buffer)
716   "Close connection to server in BUFFER. If BUFFER is nil, the current
717 buffer is used."
718   (with-current-buffer (or buffer (current-buffer))
719     (and (imap-opened)
720          (not (imap-ok-p (imap-send-command-wait "LOGOUT")))
721          (message "Server %s didn't let me log out" imap-server))
722     (when (and imap-process
723                (memq (process-status imap-process) '(open run)))
724       (delete-process imap-process))
725     (setq imap-current-mailbox nil
726           imap-current-message nil
727           imap-process nil)
728     (erase-buffer)
729     t))
730
731 (defun imap-capability (&optional identifier buffer)
732   "Return a list of identifiers which server in BUFFER support. If
733 IDENTIFIER, return non-nil if it's among the servers capabilities. If
734 BUFFER is nil, the current buffer is assumed."
735   (with-current-buffer (or buffer (current-buffer))
736     (unless imap-capability
737       (unless (imap-ok-p (imap-send-command-wait "CAPABILITY"))
738         (setq imap-capability '(IMAP2))))
739     (if identifier
740         (memq (intern (upcase (symbol-name identifier))) imap-capability)
741       imap-capability)))
742
743 (defun imap-namespace (&optional buffer)
744   "Return a namespace hierarchy at server in BUFFER. If BUFFER is nil,
745 the current buffer is assumed."
746   (with-current-buffer (or buffer (current-buffer))
747     (unless imap-namespace
748       (when (imap-capability 'NAMESPACE)
749         (imap-send-command-wait "NAMESPACE")))
750     imap-namespace))
751
752 (defun imap-send-command-wait (command &optional buffer)
753   (imap-wait-for-tag (imap-send-command command buffer) buffer))
754
755 \f
756 ;; Mailbox functions:
757
758 (defun imap-mailbox-put (propname value &optional mailbox buffer)
759   (with-current-buffer (or buffer (current-buffer))
760     (if imap-mailbox-data
761         (put (intern (or mailbox imap-current-mailbox) imap-mailbox-data)
762              propname value)
763       (error "Imap-mailbox-data is nil, prop %s value %s mailbox %s buffer %s"
764              propname value mailbox (current-buffer)))
765     t))
766
767 (defsubst imap-mailbox-get-1 (propname &optional mailbox)
768   (get (intern-soft (or mailbox imap-current-mailbox) imap-mailbox-data)
769        propname))
770
771 (defun imap-mailbox-get (propname &optional mailbox buffer)
772   (let ((mailbox (imap-utf7-encode mailbox)))
773     (with-current-buffer (or buffer (current-buffer))
774       (imap-mailbox-get-1 propname (or mailbox imap-current-mailbox)))))
775
776 (defun imap-mailbox-map-1 (func &optional mailbox-decoder buffer)
777   (with-current-buffer (or buffer (current-buffer))
778     (let (result)
779       (mapatoms 
780        (lambda (s)
781          (push (funcall func (if mailbox-decoder
782                                  (funcall mailbox-decoder (symbol-name s))
783                                (symbol-name s))) result))
784        imap-mailbox-data)
785       result)))
786
787 (defun imap-mailbox-map (func &optional buffer)
788   "Map a function across each mailbox in `imap-mailbox-data',
789 returning a list. Function should take a mailbox name (a string) as
790 the only argument."
791   (imap-mailbox-map-1 func 'imap-utf7-decode buffer))
792
793 (defun imap-current-mailbox (&optional buffer)
794   (with-current-buffer (or buffer (current-buffer))
795     (imap-utf7-decode imap-current-mailbox)))
796
797 (defun imap-current-mailbox-p-1 (mailbox &optional examine)
798   (and (string= mailbox imap-current-mailbox)
799        (or (and examine
800                 (eq imap-state 'examine))
801            (and (not examine)
802                 (eq imap-state 'selected)))))
803
804 (defun imap-current-mailbox-p (mailbox &optional examine buffer)
805   (with-current-buffer (or buffer (current-buffer))
806     (imap-current-mailbox-p-1 (imap-utf7-encode mailbox) examine)))
807
808 (defun imap-mailbox-select-1 (mailbox &optional examine)
809   "Select MAILBOX on server in BUFFER. If EXAMINE is non-nil, do a
810 read-only select."
811   (if (imap-current-mailbox-p-1 mailbox examine)
812       imap-current-mailbox
813     (setq imap-current-mailbox mailbox)
814     (if (imap-ok-p (imap-send-command-wait
815                     (concat (if examine "EXAMINE" "SELECT") " \"" 
816                             mailbox "\"")))
817         (progn
818           (setq imap-message-data (make-vector imap-message-prime 0)
819                 imap-state (if examine 'examine 'selected))
820           imap-current-mailbox)
821       ;; Failed SELECT/EXAMINE unselects current mailbox
822       (setq imap-current-mailbox nil))))
823
824 (defun imap-mailbox-select (mailbox &optional examine buffer)  
825   (with-current-buffer (or buffer (current-buffer))
826     (imap-utf7-decode 
827      (imap-mailbox-select-1 (imap-utf7-encode mailbox) examine))))
828
829 (defun imap-mailbox-examine (mailbox &optional buffer)
830   "Examine MAILBOX on server in BUFFER"
831   (imap-mailbox-select mailbox 'exmine buffer))
832
833 (defun imap-mailbox-unselect (&optional buffer)
834   "Close current folder in BUFFER, without expunging articles."
835   (with-current-buffer (or buffer (current-buffer))
836     (when (or (eq imap-state 'auth)
837               (and (imap-capability 'UNSELECT)
838                    (imap-ok-p (imap-send-command-wait "UNSELECT")))
839               (and (imap-ok-p 
840                     (imap-send-command-wait (concat "EXAMINE \""
841                                                     imap-current-mailbox
842                                                     "\"")))
843                    (imap-ok-p (imap-send-command-wait "CLOSE"))))
844       (setq imap-current-mailbox nil
845             imap-message-data nil
846             imap-state 'auth)
847       t)))
848
849 (defun imap-mailbox-expunge (&optional buffer)
850   "Expunge articles in current folder in BUFFER. If BUFFER is
851 nil the current buffer is assumed."
852   (with-current-buffer (or buffer (current-buffer))
853     (when (and imap-current-mailbox (not (eq imap-state 'examine)))
854       (imap-ok-p (imap-send-command-wait "EXPUNGE")))))
855
856 (defun imap-mailbox-close (&optional buffer)
857   "Expunge articles and close current folder in BUFFER. If BUFFER is
858 nil the current buffer is assumed."
859   (with-current-buffer (or buffer (current-buffer))
860     (when (and imap-current-mailbox
861                (imap-ok-p (imap-send-command-wait "CLOSE")))
862         (setq imap-current-mailbox nil
863               imap-message-data nil
864               imap-state 'auth)
865         t)))
866
867 (defun imap-mailbox-create-1 (mailbox)
868   (imap-ok-p (imap-send-command-wait (list "CREATE \"" mailbox "\""))))
869
870 (defun imap-mailbox-create (mailbox &optional buffer)
871   "Create MAILBOX on server in BUFFER. If BUFFER is nil the current
872 buffer is assumed."
873   (with-current-buffer (or buffer (current-buffer))
874     (imap-mailbox-create-1 (imap-utf7-encode mailbox))))
875
876 (defun imap-mailbox-delete (mailbox &optional buffer)
877   "Delete MAILBOX on server in BUFFER. If BUFFER is nil the current
878 buffer is assumed."
879   (let ((mailbox (imap-utf7-encode mailbox)))
880     (with-current-buffer (or buffer (current-buffer))
881       (imap-ok-p
882        (imap-send-command-wait (list "DELETE \"" mailbox "\""))))))
883
884 (defun imap-mailbox-rename (oldname newname &optional buffer)
885   "Rename mailbox OLDNAME to NEWNAME on server in BUFFER. If BUFFER is
886 nil the current buffer is assumed."
887   (let ((oldname (imap-utf7-encode oldname))
888         (newname (imap-utf7-encode newname)))
889     (with-current-buffer (or buffer (current-buffer))
890       (imap-ok-p
891        (imap-send-command-wait (list "RENAME \"" oldname "\" "
892                                      "\"" newname "\""))))))
893
894 (defun imap-mailbox-lsub (&optional root reference add-delimiter buffer) 
895   "Return a list of subscribed mailboxes on server in BUFFER.
896 If ROOT is non-nil, only list matching mailboxes.  If ADD-DELIMITER is
897 non-nil, a hierarchy delimiter is added to root. REFERENCE is a
898 implementation-specific string that has to be passed to lsub command."
899   (with-current-buffer (or buffer (current-buffer))
900     ;; Make sure we know the hierarchy separator for root's hierarchy
901     (when (and add-delimiter (null (imap-mailbox-get-1 'delimiter root)))
902       (imap-send-command-wait (concat "LIST \"" reference "\" \""
903                                       (imap-utf7-encode root) "\"")))
904     ;; clear list data (NB not delimiter and other stuff)
905     (imap-mailbox-map-1 (lambda (mailbox)
906                           (imap-mailbox-put 'lsub nil mailbox)))
907     (when (imap-ok-p
908            (imap-send-command-wait 
909             (concat "LSUB \"" reference "\" \"" (imap-utf7-encode root)
910                     (and add-delimiter (imap-mailbox-get-1 'delimiter root))
911                     "%\"")))
912       (let (out)
913         (imap-mailbox-map-1 (lambda (mailbox)
914                               (when (imap-mailbox-get-1 'lsub mailbox)
915                                 (push (imap-utf7-decode mailbox) out))))
916         (nreverse out)))))
917
918 (defun imap-mailbox-list (root &optional reference add-delimiter buffer)
919   "Return a list of mailboxes matching ROOT on server in BUFFER.
920 If ADD-DELIMITER is non-nil, a hierarchy delimiter is added to
921 root. REFERENCE is a implementation-specific string that has to be
922 passed to list command."
923   (with-current-buffer (or buffer (current-buffer))
924     ;; Make sure we know the hierarchy separator for root's hierarchy
925     (when (and add-delimiter (null (imap-mailbox-get-1 'delimiter root)))
926       (imap-send-command-wait (concat "LIST \"" reference "\" \""
927                                       (imap-utf7-encode root) "\"")))
928     ;; clear list data (NB not delimiter and other stuff)
929     (imap-mailbox-map-1 (lambda (mailbox)
930                           (imap-mailbox-put 'list nil mailbox)))
931     (when (imap-ok-p
932            (imap-send-command-wait 
933             (concat "LIST \"" reference "\" \"" (imap-utf7-encode root)
934                     (and add-delimiter (imap-mailbox-get-1 'delimiter root))
935                     "%\"")))
936       (let (out)
937         (imap-mailbox-map-1 (lambda (mailbox)
938                               (when (imap-mailbox-get-1 'list mailbox)
939                                 (push (imap-utf7-decode mailbox) out))))
940         (nreverse out)))))
941
942 (defun imap-mailbox-subscribe (mailbox &optional buffer)
943   "Send the SUBSCRIBE command on the mailbox to server in
944 BUFFER. Returns non-nil if successful."
945   (with-current-buffer (or buffer (current-buffer))
946     (imap-ok-p (imap-send-command-wait (concat "SUBSCRIBE \"" 
947                                                (imap-utf7-encode mailbox)
948                                                "\"")))))
949
950 (defun imap-mailbox-unsubscribe (mailbox &optional buffer)
951   "Send the SUBSCRIBE command on the mailbox to server in
952 BUFFER. Returns non-nil if successful."
953   (with-current-buffer (or buffer (current-buffer))
954     (imap-ok-p (imap-send-command-wait (concat "UNSUBSCRIBE " 
955                                                (imap-utf7-encode mailbox)
956                                                "\"")))))
957
958 (defun imap-mailbox-status (mailbox items &optional buffer)
959   "Get status items ITEM in MAILBOX from server in BUFFER. ITEMS can
960 be a symbol or a list of symbols, valid symbols are one of the STATUS
961 data items -- ie 'messages, 'recent, 'uidnext, 'uidvalidity or
962 'unseen. If ITEMS is a list of symbols, a list of values is returned,
963 if ITEMS is a symbol only it's value is returned."
964   (with-current-buffer (or buffer (current-buffer))
965     (when (imap-ok-p 
966            (imap-send-command-wait (list "STATUS \""
967                                          (imap-utf7-encode mailbox)
968                                          "\" "
969                                          (format "%s"
970                                                  (if (listp items)
971                                                      items 
972                                                    (list items))))))
973       (if (listp items)
974           (mapcar (lambda (item)
975                     (imap-mailbox-get-1 item mailbox))
976                   items)
977         (imap-mailbox-get-1 items mailbox)))))
978
979 (defun imap-mailbox-acl-get (&optional mailbox buffer)
980   "Get ACL on mailbox from server in BUFFER."
981   (let ((mailbox (imap-utf7-encode mailbox)))
982     (with-current-buffer (or buffer (current-buffer))
983       (when (imap-ok-p
984              (imap-send-command-wait (list "GETACL \""
985                                            (or mailbox imap-current-mailbox)
986                                            "\"")))
987       (imap-mailbox-get-1 'acl (or mailbox imap-current-mailbox))))))
988
989 (defun imap-mailbox-acl-set (identifier rights &optional mailbox buffer)
990   "Change/set ACL for IDENTIFIER to RIGHTS in MAILBOX from server in
991 BUFFER."
992   (let ((mailbox (imap-utf7-encode mailbox)))
993     (with-current-buffer (or buffer (current-buffer))
994       (imap-ok-p
995        (imap-send-command-wait (list "SETACL \""
996                                      (or mailbox imap-current-mailbox)
997                                      "\" "
998                                      identifier
999                                      " "
1000                                      rights))))))
1001
1002 (defun imap-mailbox-acl-delete (identifier &optional mailbox buffer)
1003   "Removes any <identifier,rights> pair for IDENTIFIER in MAILBOX from
1004 server in BUFFER."
1005   (let ((mailbox (imap-utf7-encode mailbox)))
1006     (with-current-buffer (or buffer (current-buffer))
1007       (imap-ok-p
1008        (imap-send-command-wait (list "DELETEACL \""
1009                                      (or mailbox imap-current-mailbox)
1010                                      "\" "
1011                                      identifier))))))
1012
1013 \f
1014 ;; Message functions:
1015
1016 (defun imap-current-message (&optional buffer)
1017   (with-current-buffer (or buffer (current-buffer))
1018     imap-current-message))
1019
1020 (defun imap-list-to-message-set (list)
1021   (mapconcat (lambda (item)
1022                (number-to-string item))
1023              (if (listp list)
1024                  list
1025                (list list))
1026              ","))
1027
1028 (defun imap-fetch-asynch (uids props &optional nouidfetch buffer)
1029   (with-current-buffer (or buffer (current-buffer))
1030     (imap-send-command (format "%sFETCH %s %s" (if nouidfetch "" "UID ")
1031                                (if (listp uids)
1032                                    (imap-list-to-message-set uids)
1033                                  uids)
1034                                props))))
1035
1036 (defun imap-fetch (uids props &optional receive nouidfetch buffer)
1037   "Fetch properties PROPS from message set UIDS from server in
1038 BUFFER. UIDS can be a string, number or a list of numbers. If RECEIVE
1039 is non-nil return theese properties."
1040   (with-current-buffer (or buffer (current-buffer))
1041     (when (imap-ok-p (imap-send-command-wait 
1042                       (format "%sFETCH %s %s" (if nouidfetch "" "UID ")
1043                               (if (listp uids)
1044                                   (imap-list-to-message-set uids)
1045                                 uids)
1046                               props)))
1047       (if (or (null receive) (stringp uids))
1048           t
1049         (if (listp uids)
1050             (mapcar (lambda (uid)
1051                       (if (listp receive)
1052                           (mapcar (lambda (prop)
1053                                     (imap-message-get uid prop))
1054                                   receive)
1055                         (imap-message-get uid receive)))
1056                     uids)
1057           (imap-message-get uids receive))))))
1058     
1059 (defun imap-message-put (uid propname value &optional buffer)
1060   (with-current-buffer (or buffer (current-buffer))
1061     (if imap-message-data
1062         (put (intern (number-to-string uid) imap-message-data)
1063              propname value)
1064       (error "Imap-message-data is nil, uid %s prop %s value %s buffer %s"
1065              uid propname value (current-buffer)))
1066     t))
1067
1068 (defun imap-message-get (uid propname &optional buffer)
1069   (with-current-buffer (or buffer (current-buffer))
1070     (get (intern-soft (number-to-string uid) imap-message-data)
1071          propname)))
1072
1073 (defun imap-message-map (func propname &optional buffer)
1074   "Map a function across each mailbox in `imap-message-data',
1075 returning a list."
1076   (with-current-buffer (or buffer (current-buffer))
1077     (let (result)
1078       (mapatoms
1079        (lambda (s)
1080          (push (funcall func (get s 'UID) (get s propname)) result))
1081        imap-message-data)
1082       result)))
1083
1084 (defmacro imap-message-envelope-date (uid &optional buffer)
1085   `(with-current-buffer (or ,buffer (current-buffer))
1086      (elt (imap-message-get ,uid 'ENVELOPE) 0)))
1087
1088 (defmacro imap-message-envelope-subject (uid &optional buffer)
1089   `(with-current-buffer (or ,buffer (current-buffer))
1090      (elt (imap-message-get ,uid 'ENVELOPE) 1)))
1091
1092 (defmacro imap-message-envelope-from (uid &optional buffer)
1093   `(with-current-buffer (or ,buffer (current-buffer))
1094      (elt (imap-message-get ,uid 'ENVELOPE) 2)))
1095
1096 (defmacro imap-message-envelope-sender (uid &optional buffer)
1097   `(with-current-buffer (or ,buffer (current-buffer))
1098      (elt (imap-message-get ,uid 'ENVELOPE) 3)))
1099
1100 (defmacro imap-message-envelope-reply-to (uid &optional buffer)
1101   `(with-current-buffer (or ,buffer (current-buffer))
1102      (elt (imap-message-get ,uid 'ENVELOPE) 4)))
1103
1104 (defmacro imap-message-envelope-to (uid &optional buffer)
1105   `(with-current-buffer (or ,buffer (current-buffer))
1106      (elt (imap-message-get ,uid 'ENVELOPE) 5)))
1107
1108 (defmacro imap-message-envelope-cc (uid &optional buffer)
1109   `(with-current-buffer (or ,buffer (current-buffer))
1110      (elt (imap-message-get ,uid 'ENVELOPE) 6)))
1111
1112 (defmacro imap-message-envelope-bcc (uid &optional buffer)
1113   `(with-current-buffer (or ,buffer (current-buffer))
1114      (elt (imap-message-get ,uid 'ENVELOPE) 7)))
1115
1116 (defmacro imap-message-envelope-in-reply-to (uid &optional buffer)
1117   `(with-current-buffer (or ,buffer (current-buffer))
1118      (elt (imap-message-get ,uid 'ENVELOPE) 8)))
1119
1120 (defmacro imap-message-envelope-message-id (uid &optional buffer)
1121   `(with-current-buffer (or ,buffer (current-buffer))
1122      (elt (imap-message-get ,uid 'ENVELOPE) 9)))
1123
1124 (defmacro imap-message-body (uid &optional buffer)
1125   `(with-current-buffer (or ,buffer (current-buffer))
1126      (imap-message-get ,uid 'BODY)))
1127
1128 (defun imap-search (predicate &optional buffer)
1129   (with-current-buffer (or buffer (current-buffer))
1130     (imap-mailbox-put 'search 'dummy)
1131     (when (imap-ok-p (imap-send-command-wait (concat "UID SEARCH " predicate)))
1132       (if (eq (imap-mailbox-get-1 'search imap-current-mailbox) 'dummy)
1133           (error "Missing SEARCH response to a SEARCH command")
1134         (imap-mailbox-get-1 'search imap-current-mailbox)))))
1135
1136 (defun imap-message-flag-permanent-p (flag &optional mailbox buffer)
1137   "Return t iff FLAG can be permanently (between IMAP sessions) saved
1138 on articles, in MAILBOX on server in BUFFER."
1139   (with-current-buffer (or buffer (current-buffer))
1140     (or (member "\\*" (imap-mailbox-get 'permanentflags mailbox))
1141         (member flag (imap-mailbox-get 'permanentflags mailbox)))))
1142
1143 (defun imap-message-flags-set (articles flags &optional silent buffer)
1144   (when (and articles flags)
1145     (with-current-buffer (or buffer (current-buffer))
1146       (imap-ok-p (imap-send-command-wait
1147                   (concat "UID STORE " articles
1148                           " FLAGS" (if silent ".SILENT") " (" flags ")"))))))
1149
1150 (defun imap-message-flags-del (articles flags &optional silent buffer)
1151   (when (and articles flags)
1152     (with-current-buffer (or buffer (current-buffer))
1153       (imap-ok-p (imap-send-command-wait
1154                   (concat "UID STORE " articles
1155                           " -FLAGS" (if silent ".SILENT") " (" flags ")"))))))
1156
1157 (defun imap-message-flags-add (articles flags &optional silent buffer)
1158   (when (and articles flags)
1159     (with-current-buffer (or buffer (current-buffer))
1160       (imap-ok-p (imap-send-command-wait
1161                   (concat "UID STORE " articles
1162                           " +FLAGS" (if silent ".SILENT") " (" flags ")"))))))
1163
1164 (defun imap-message-copyuid-1 (mailbox)
1165   (if (imap-capability 'UIDPLUS)
1166       (list (nth 0 (imap-mailbox-get-1 'copyuid mailbox))
1167             (string-to-number (nth 2 (imap-mailbox-get-1 'copyuid mailbox))))
1168     (let ((old-mailbox imap-current-mailbox)
1169           (state imap-state)
1170           (imap-message-data (make-vector 2 0)))
1171       (when (imap-mailbox-examine mailbox)
1172         (prog1
1173             (and (imap-fetch "*" "UID")
1174                  (list (imap-mailbox-get-1 'uidvalidity mailbox)
1175                        (apply 'max (imap-message-map
1176                                     (lambda (uid prop) uid) 'UID))))
1177           (if old-mailbox
1178               (imap-mailbox-select old-mailbox (eq state 'examine))
1179             (imap-mailbox-unselect)))))))
1180
1181 (defun imap-message-copyuid (mailbox &optional buffer)
1182   (with-current-buffer (or buffer (current-buffer))
1183     (imap-message-copyuid-1 (imap-utf7-decode mailbox))))
1184
1185 (defun imap-message-copy (articles mailbox
1186                                    &optional dont-create no-copyuid buffer)
1187   "Copy ARTICLES (a string message set) to MAILBOX on server in
1188 BUFFER, creating mailbox if it doesn't exist. If dont-create is
1189 non-nil, it will not create a mailbox. On success, return a list with
1190 the UIDVALIDITY of the mailbox the article(s) was copied to as the
1191 first element, rest of list contain the saved articles' UIDs."
1192   (when articles
1193     (with-current-buffer (or buffer (current-buffer))
1194       (let ((mailbox (imap-utf7-encode mailbox)))
1195         (if (let ((cmd (concat "UID COPY " articles " \"" mailbox "\""))
1196                   (imap-current-target-mailbox mailbox))
1197               (if (imap-ok-p (imap-send-command-wait cmd))
1198                   t
1199                 (when (and (not dont-create)
1200                            (imap-mailbox-get-1 'trycreate mailbox))
1201                   (imap-mailbox-create-1 mailbox)
1202                   (imap-ok-p (imap-send-command-wait cmd)))))
1203             (or no-copyuid
1204                 (imap-message-copyuid-1 mailbox)))))))
1205       
1206 (defun imap-message-appenduid-1 (mailbox)
1207   (if (imap-capability 'UIDPLUS)
1208       (imap-mailbox-get-1 'appenduid mailbox)
1209     (let ((old-mailbox imap-current-mailbox)
1210           (state imap-state)
1211           (imap-message-data (make-vector 2 0)))
1212       (when (imap-mailbox-examine mailbox)
1213         (prog1
1214             (and (imap-fetch "*" "UID")
1215                  (list (imap-mailbox-get-1 'uidvalidity mailbox)
1216                        (apply 'max (imap-message-map
1217                                     (lambda (uid prop) uid) 'UID))))
1218           (if old-mailbox
1219               (imap-mailbox-select old-mailbox (eq state 'examine))
1220             (imap-mailbox-unselect)))))))
1221
1222 (defun imap-message-appenduid (mailbox &optional buffer)
1223   (with-current-buffer (or buffer (current-buffer))
1224     (imap-message-appenduid-1 (imap-utf7-encode mailbox))))
1225
1226 (defun imap-message-append (mailbox article &optional flags date-time buffer)
1227   "Append ARTICLE (a buffer) to MAILBOX on server in BUFFER. FLAGS and
1228 DATE-TIME is currently not used. Return a cons holding uidvalidity of
1229 MAILBOX and UID the newly created article got, or nil on failure."
1230   (let ((mailbox (imap-utf7-encode mailbox)))
1231     (with-current-buffer (or buffer (current-buffer))
1232       (and (let ((imap-current-target-mailbox mailbox))
1233              (imap-ok-p 
1234               (imap-send-command-wait 
1235                (list "APPEND \"" mailbox "\" "  article))))
1236            (imap-message-appenduid-1 mailbox)))))
1237   
1238 (defun imap-body-lines (body)
1239   "Return number of lines in article by looking at the mime bodystructure
1240 BODY."
1241   (if (listp body)
1242       (if (stringp (car body))
1243           (cond ((and (string= (car body) "TEXT")
1244                       (numberp (nth 7 body)))
1245                  (nth 7 body))
1246                 ((and (string= (car body) "MESSAGE")
1247                       (numberp (nth 9 body)))
1248                  (nth 9 body))
1249                 (t 0))
1250         (apply '+ (mapcar 'imap-body-lines body)))
1251     0))
1252
1253 (defun imap-envelope-from (from)
1254   "Return a from string line."
1255   (and from
1256        (concat (aref from 0)
1257                (if (aref from 0) " <")
1258                (aref from 2) 
1259                "@" 
1260                (aref from 3)
1261                (if (aref from 0) ">"))))
1262
1263 \f
1264 ;; Internal functions.
1265
1266 (defun imap-send-command-1 (cmdstr)
1267   (setq cmdstr (concat cmdstr imap-client-eol))
1268   (and imap-log
1269        (with-current-buffer (get-buffer-create imap-log)
1270          (buffer-disable-undo)
1271          (goto-char (point-max))
1272          (insert cmdstr)))
1273   (process-send-string imap-process cmdstr))
1274
1275 (defun imap-send-command (command &optional buffer)
1276   (with-current-buffer (or buffer (current-buffer))
1277     (if (not (listp command)) (setq command (list command)))
1278     (let ((tag (setq imap-tag (1+ imap-tag)))
1279           cmd cmdstr)
1280       (setq cmdstr (concat (number-to-string imap-tag) " "))
1281       (while (setq cmd (pop command))
1282         (cond ((stringp cmd)
1283                (setq cmdstr (concat cmdstr cmd)))
1284               ((bufferp cmd)
1285                (setq cmdstr 
1286                      (concat cmdstr (format "{%d}" (with-current-buffer cmd
1287                                                      (buffer-size)))))
1288                (unwind-protect
1289                    (progn
1290                      (imap-send-command-1 cmdstr)
1291                      (setq cmdstr nil)
1292                      (if (not (eq (imap-wait-for-tag tag) 'INCOMPLETE))
1293                          (setq command nil) ;; abort command if no cont-req
1294                        (let ((process imap-process)
1295                              (stream imap-stream))
1296                          (with-current-buffer cmd
1297                            (when (eq stream 'kerberos4)
1298                              ;; XXX modifies buffer!
1299                              (goto-char (point-min))
1300                              (while (search-forward "\r\n" nil t)
1301                                (replace-match "\n")))
1302                            (and imap-log
1303                                 (with-current-buffer (get-buffer-create
1304                                                       imap-log)
1305                                   (buffer-disable-undo)
1306                                   (goto-char (point-max))
1307                                   (insert-buffer-substring cmd)))
1308                            (process-send-region process (point-min)
1309                                                 (point-max)))
1310                          (process-send-string process imap-client-eol))))
1311                  (setq imap-continuation nil)))
1312               ((functionp cmd)
1313                (imap-send-command-1 cmdstr)
1314                (setq cmdstr nil)
1315                (unwind-protect
1316                    (if (not (eq (imap-wait-for-tag tag) 'INCOMPLETE))
1317                        (setq command nil) ;; abort command if no cont-req
1318                      (setq command (cons (funcall cmd imap-continuation)
1319                                          command)))
1320                  (setq imap-continuation nil)))
1321               (t
1322                (error "Unknown command type"))))
1323       (if cmdstr
1324           (imap-send-command-1 cmdstr))
1325       tag)))
1326
1327 (defun imap-wait-for-tag (tag &optional buffer)
1328   (with-current-buffer (or buffer (current-buffer))
1329     (while (and (null imap-continuation)
1330                 (< imap-reached-tag tag))
1331       (or (and (not (memq (process-status imap-process) '(open run)))
1332                (sit-for 1))
1333           (accept-process-output imap-process 1)))
1334     (or (assq tag imap-failed-tags)
1335         (if imap-continuation
1336             'INCOMPLETE
1337           'OK))))
1338
1339 (defun imap-sentinel (process string)
1340   (delete-process process))
1341
1342 (defun imap-find-next-line ()
1343   "Return point at end of current line, taking into account
1344 literals. Return nil if no complete line has arrived."
1345   (when (re-search-forward (concat imap-server-eol "\\|{\\([0-9]+\\)}"
1346                                    imap-server-eol)
1347                            nil t)
1348     (if (match-string 1)
1349         (if (< (point-max) (+ (point) (string-to-number (match-string 1))))
1350             nil
1351           (goto-char (+ (point) (string-to-number (match-string 1))))
1352           (imap-find-next-line))
1353       (point))))
1354
1355 (defun imap-arrival-filter (proc string)
1356   "IMAP process filter."
1357   (with-current-buffer (process-buffer proc)
1358     (goto-char (point-max))
1359     (insert string)
1360     (and imap-log
1361          (with-current-buffer (get-buffer-create imap-log)
1362            (buffer-disable-undo)
1363            (goto-char (point-max))
1364            (insert string)))
1365     (let (end)
1366       (goto-char (point-min))
1367       (while (setq end (imap-find-next-line))
1368         (save-restriction
1369           (narrow-to-region (point-min) end)
1370           (delete-backward-char (length imap-server-eol))
1371           (goto-char (point-min))
1372           (unwind-protect
1373               (cond ((eq imap-state 'initial)
1374                      (imap-parse-greeting))
1375                     ((or (eq imap-state 'auth)
1376                          (eq imap-state 'nonauth)
1377                          (eq imap-state 'selected)
1378                          (eq imap-state 'examine))
1379                      (imap-parse-response))
1380                     (t
1381                      (message "Unknown state %s in arrival filter" 
1382                               imap-state)))
1383             (delete-region (point-min) (point-max))))))))
1384
1385 \f
1386 ;; Imap parser.
1387
1388 (defsubst imap-forward ()
1389   (or (eobp) (forward-char)))
1390
1391 ;;   number          = 1*DIGIT
1392 ;;                       ; Unsigned 32-bit integer
1393 ;;                       ; (0 <= n < 4,294,967,296)
1394
1395 (defsubst imap-parse-number ()
1396   (when (looking-at "[0-9]+")
1397     (prog1
1398         (string-to-number (match-string 0))
1399       (goto-char (match-end 0)))))
1400
1401 ;;   literal         = "{" number "}" CRLF *CHAR8
1402 ;;                       ; Number represents the number of CHAR8s
1403
1404 (defsubst imap-parse-literal ()
1405   (when (looking-at "{\\([0-9]+\\)}\r\n")
1406     (let ((pos (match-end 0))
1407           (len (string-to-number (match-string 1))))
1408       (if (< (point-max) (+ pos len))
1409           nil
1410         (goto-char (+ pos len))
1411         (buffer-substring-no-properties pos (+ pos len))))))
1412
1413 ;;   string          = quoted / literal
1414 ;;
1415 ;;   quoted          = DQUOTE *QUOTED-CHAR DQUOTE
1416 ;;
1417 ;;   QUOTED-CHAR     = <any TEXT-CHAR except quoted-specials> /
1418 ;;                     "\" quoted-specials
1419 ;;
1420 ;;   quoted-specials = DQUOTE / "\"
1421 ;;
1422 ;;   TEXT-CHAR       = <any CHAR except CR and LF>
1423
1424 (defsubst imap-parse-string ()
1425   (let (strstart strend)
1426     (cond ((and (eq (char-after (point)) ?\")
1427                 (setq strstart (point))
1428                 (setq strend (search-forward "\"" nil t 2)))
1429            (buffer-substring-no-properties (1+ strstart) (1- strend)))
1430           ((eq (char-after) ?{)
1431            (imap-parse-literal)))))
1432
1433 ;;   nil             = "NIL"
1434
1435 (defsubst imap-parse-nil ()
1436   (if (looking-at "NIL")
1437       (goto-char (match-end 0))))
1438
1439 ;;   nstring         = string / nil
1440
1441 (defsubst imap-parse-nstring ()
1442   (or (imap-parse-string)
1443       (and (imap-parse-nil)
1444            nil)))
1445
1446 ;;   astring         = atom / string
1447 ;;
1448 ;;   atom            = 1*ATOM-CHAR
1449 ;;
1450 ;;   ATOM-CHAR       = <any CHAR except atom-specials>
1451 ;;
1452 ;;   atom-specials   = "(" / ")" / "{" / SP / CTL / list-wildcards /
1453 ;;                     quoted-specials
1454 ;;
1455 ;;   list-wildcards  = "%" / "*"
1456 ;;
1457 ;;   quoted-specials = DQUOTE / "\"
1458
1459 (defsubst imap-parse-astring ()
1460   (or (imap-parse-string)
1461       (buffer-substring (point) 
1462                         (if (re-search-forward "[(){ \r\n%*\"\\]" nil t)
1463                             (goto-char (1- (match-end 0)))
1464                           (end-of-line)
1465                           (point)))))
1466
1467 ;;   address         = "(" addr-name SP addr-adl SP addr-mailbox SP
1468 ;;                      addr-host ")"
1469 ;;
1470 ;;   addr-adl        = nstring
1471 ;;                       ; Holds route from [RFC-822] route-addr if
1472 ;;                       ; non-NIL
1473 ;;
1474 ;;   addr-host       = nstring
1475 ;;                       ; NIL indicates [RFC-822] group syntax.
1476 ;;                       ; Otherwise, holds [RFC-822] domain name
1477 ;;
1478 ;;   addr-mailbox    = nstring
1479 ;;                       ; NIL indicates end of [RFC-822] group; if
1480 ;;                       ; non-NIL and addr-host is NIL, holds
1481 ;;                       ; [RFC-822] group name.
1482 ;;                       ; Otherwise, holds [RFC-822] local-part
1483 ;;                       ; after removing [RFC-822] quoting
1484 ;;
1485 ;;   addr-name       = nstring
1486 ;;                       ; If non-NIL, holds phrase from [RFC-822]
1487 ;;                       ; mailbox after removing [RFC-822] quoting
1488 ;;
1489
1490 (defsubst imap-parse-address ()
1491   (let (address)
1492     (when (eq (char-after) ?\()
1493       (imap-forward)
1494       (setq address (vector (prog1 (imap-parse-nstring)
1495                               (imap-forward))
1496                             (prog1 (imap-parse-nstring)
1497                               (imap-forward))
1498                             (prog1 (imap-parse-nstring)
1499                               (imap-forward))
1500                             (imap-parse-nstring)))
1501       (when (eq (char-after) ?\))
1502         (imap-forward)
1503         address))))
1504
1505 ;;   address-list    = "(" 1*address ")" / nil
1506 ;;
1507 ;;   nil             = "NIL"
1508
1509 (defsubst imap-parse-address-list ()
1510   (if (eq (char-after) ?\()
1511       (let (address addresses)
1512         (imap-forward)
1513         (while (and (not (eq (char-after) ?\)))
1514                     ;; next line for MS Exchange bug
1515                     (progn (and (eq (char-after) ? ) (imap-forward)) t)
1516                     (setq address (imap-parse-address)))
1517           (setq addresses (cons address addresses)))
1518         (when (eq (char-after) ?\))
1519           (imap-forward)
1520           (nreverse addresses)))
1521     (assert (imap-parse-nil))))
1522
1523 ;;   mailbox         = "INBOX" / astring
1524 ;;                       ; INBOX is case-insensitive.  All case variants of
1525 ;;                       ; INBOX (e.g. "iNbOx") MUST be interpreted as INBOX
1526 ;;                       ; not as an astring.  An astring which consists of
1527 ;;                       ; the case-insensitive sequence "I" "N" "B" "O" "X"
1528 ;;                       ; is considered to be INBOX and not an astring.
1529 ;;                       ;  Refer to section 5.1 for further
1530 ;;                       ; semantic details of mailbox names.
1531
1532 (defsubst imap-parse-mailbox ()
1533   (let ((mailbox (imap-parse-astring)))
1534     (if (string-equal "INBOX" (upcase mailbox))
1535         "INBOX"
1536       mailbox)))
1537
1538 ;;   greeting        = "*" SP (resp-cond-auth / resp-cond-bye) CRLF
1539 ;;
1540 ;;   resp-cond-auth  = ("OK" / "PREAUTH") SP resp-text
1541 ;;                       ; Authentication condition
1542 ;;
1543 ;;   resp-cond-bye   = "BYE" SP resp-text
1544
1545 (defun imap-parse-greeting ()
1546   "Parse a IMAP greeting."
1547   (cond ((looking-at "\\* OK ")
1548          (setq imap-state 'nonauth))
1549         ((looking-at "\\* PREAUTH ")
1550          (setq imap-state 'auth))
1551         ((looking-at "\\* BYE ")
1552          (setq imap-state 'closed))))
1553
1554 ;;   response        = *(continue-req / response-data) response-done
1555 ;;
1556 ;;   continue-req    = "+" SP (resp-text / base64) CRLF
1557 ;;
1558 ;;   response-data   = "*" SP (resp-cond-state / resp-cond-bye /
1559 ;;                     mailbox-data / message-data / capability-data) CRLF
1560 ;;
1561 ;;   response-done   = response-tagged / response-fatal
1562 ;;
1563 ;;   response-fatal  = "*" SP resp-cond-bye CRLF
1564 ;;                       ; Server closes connection immediately
1565 ;;
1566 ;;   response-tagged = tag SP resp-cond-state CRLF
1567 ;;
1568 ;;   resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text
1569 ;;                       ; Status condition
1570 ;;
1571 ;;   resp-cond-bye   = "BYE" SP resp-text
1572 ;;
1573 ;;   mailbox-data    =  "FLAGS" SP flag-list /
1574 ;;                      "LIST" SP mailbox-list /
1575 ;;                      "LSUB" SP mailbox-list /
1576 ;;                      "SEARCH" *(SP nz-number) /
1577 ;;                      "STATUS" SP mailbox SP "("
1578 ;;                            [status-att SP number *(SP status-att SP number)] ")" /
1579 ;;                      number SP "EXISTS" /
1580 ;;                      number SP "RECENT"
1581 ;;
1582 ;;   message-data    = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att))
1583 ;;
1584 ;;   capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1"
1585 ;;                     *(SP capability)
1586 ;;                       ; IMAP4rev1 servers which offer RFC 1730
1587 ;;                       ; compatibility MUST list "IMAP4" as the first
1588 ;;                       ; capability.
1589
1590 (defun imap-parse-response ()
1591   "Parse a IMAP command response."
1592   (let (token)
1593     (case (setq token (read (current-buffer)))
1594       (+ (setq imap-continuation
1595                (or (buffer-substring (min (point-max) (1+ (point)))
1596                                      (point-max))
1597                    t)))
1598       (* (case (prog1 (setq token (read (current-buffer)))
1599                  (imap-forward))
1600            (OK         (imap-parse-resp-text))
1601            (NO         (imap-parse-resp-text))
1602            (BAD        (imap-parse-resp-text))
1603            (BYE        (imap-parse-resp-text))
1604            (FLAGS      (imap-mailbox-put 'flags (imap-parse-flag-list)))
1605            (LIST       (imap-parse-data-list 'list))
1606            (LSUB       (imap-parse-data-list 'lsub))
1607            (SEARCH     (imap-mailbox-put 
1608                         'search 
1609                         (read (concat "(" (buffer-substring (point) (point-max)) ")"))))
1610            (STATUS     (imap-parse-status))
1611            (CAPABILITY (setq imap-capability 
1612                              (read (concat "(" (upcase (buffer-substring
1613                                                         (point) (point-max)))
1614                                            ")"))))
1615            (ACL        (imap-parse-acl))
1616            (t       (case (prog1 (read (current-buffer))
1617                             (imap-forward))
1618                       (EXISTS  (imap-mailbox-put 'exists token))
1619                       (RECENT  (imap-mailbox-put 'recent token))
1620                       (EXPUNGE t)
1621                       (FETCH   (imap-parse-fetch token))
1622                       (t       (message "Garbage: %s" (buffer-string)))))))
1623       (t (let (status)
1624            (if (not (integerp token))
1625                (message "Garbage: %s" (buffer-string))
1626              (case (prog1 (setq status (read (current-buffer)))
1627                      (imap-forward))
1628                (OK  (progn
1629                       (setq imap-reached-tag (max imap-reached-tag token))
1630                       (imap-parse-resp-text)))
1631                (NO  (progn
1632                       (setq imap-reached-tag (max imap-reached-tag token))
1633                       (save-excursion
1634                         (imap-parse-resp-text))
1635                       (let (code text)
1636                         (when (eq (char-after) ?\[)
1637                           (setq code (buffer-substring (point)
1638                                                        (search-forward "]")))
1639                           (imap-forward))
1640                         (setq text (buffer-substring (point) (point-max)))
1641                         (push (list token status code text) 
1642                               imap-failed-tags))))
1643                (BAD (progn
1644                       (setq imap-reached-tag (max imap-reached-tag token))
1645                       (save-excursion
1646                         (imap-parse-resp-text))
1647                       (let (code text)
1648                         (when (eq (char-after) ?\[)
1649                           (setq code (buffer-substring (point)
1650                                                        (search-forward "]")))
1651                           (imap-forward))
1652                         (setq text (buffer-substring (point) (point-max)))
1653                         (push (list token status code text) imap-failed-tags)
1654                         (error "Internal error, tag %s status %s code %s text %s"
1655                                token status code text))))
1656                (t   (message "Garbage: %s" (buffer-string))))))))))
1657
1658 ;;   resp-text       = ["[" resp-text-code "]" SP] text
1659 ;;
1660 ;;   text            = 1*TEXT-CHAR
1661 ;;
1662 ;;   TEXT-CHAR       = <any CHAR except CR and LF>
1663
1664 (defun imap-parse-resp-text ()
1665   (imap-parse-resp-text-code))
1666
1667 ;;   resp-text-code  = "ALERT" /
1668 ;;                     "BADCHARSET [SP "(" astring *(SP astring) ")" ] /
1669 ;;                     "NEWNAME" SP string SP string / 
1670 ;;                     "PARSE" /
1671 ;;                     "PERMANENTFLAGS" SP "(" 
1672 ;;                               [flag-perm *(SP flag-perm)] ")" /
1673 ;;                     "READ-ONLY" / 
1674 ;;                     "READ-WRITE" / 
1675 ;;                     "TRYCREATE" /
1676 ;;                     "UIDNEXT" SP nz-number / 
1677 ;;                     "UIDVALIDITY" SP nz-number /
1678 ;;                     "UNSEEN" SP nz-number /
1679 ;;                     resp-text-atom [SP 1*<any TEXT-CHAR except "]">]
1680 ;;
1681 ;;   resp_code_apnd  = "APPENDUID" SPACE nz_number SPACE uniqueid
1682 ;;
1683 ;;   resp_code_copy  = "COPYUID" SPACE nz_number SPACE set SPACE set
1684 ;;
1685 ;;   set             = sequence-num / (sequence-num ":" sequence-num) /
1686 ;;                        (set "," set)
1687 ;;                          ; Identifies a set of messages.  For message
1688 ;;                          ; sequence numbers, these are consecutive
1689 ;;                          ; numbers from 1 to the number of messages in
1690 ;;                          ; the mailbox
1691 ;;                          ; Comma delimits individual numbers, colon
1692 ;;                          ; delimits between two numbers inclusive.
1693 ;;                          ; Example: 2,4:7,9,12:* is 2,4,5,6,7,9,12,13,
1694 ;;                          ; 14,15 for a mailbox with 15 messages.
1695 ;; 
1696 ;;   sequence-num    = nz-number / "*"
1697 ;;                          ; * is the largest number in use.  For message
1698 ;;                          ; sequence numbers, it is the number of messages
1699 ;;                          ; in the mailbox.  For unique identifiers, it is
1700 ;;                          ; the unique identifier of the last message in
1701 ;;                          ; the mailbox.
1702 ;;
1703 ;;   flag-perm       = flag / "\*"
1704 ;;
1705 ;;   flag            = "\Answered" / "\Flagged" / "\Deleted" /
1706 ;;                     "\Seen" / "\Draft" / flag-keyword / flag-extension
1707 ;;                       ; Does not include "\Recent"
1708 ;;
1709 ;;   flag-extension  = "\" atom
1710 ;;                       ; Future expansion.  Client implementations
1711 ;;                       ; MUST accept flag-extension flags.  Server
1712 ;;                       ; implementations MUST NOT generate
1713 ;;                       ; flag-extension flags except as defined by
1714 ;;                       ; future standard or standards-track
1715 ;;                       ; revisions of this specification.
1716 ;;
1717 ;;   flag-keyword    = atom
1718 ;;
1719 ;;   resp-text-atom  = 1*<any ATOM-CHAR except "]">
1720
1721 (defun imap-parse-resp-text-code ()
1722   (when (eq (char-after) ?\[)
1723     (imap-forward)
1724     (cond ((search-forward "PERMANENTFLAGS " nil t)
1725            (imap-mailbox-put 'permanentflags (imap-parse-flag-list)))
1726           ((search-forward "UIDNEXT " nil t)
1727            (imap-mailbox-put 'uidnext (read (current-buffer))))
1728           ((search-forward "UNSEEN " nil t)
1729            (imap-mailbox-put 'unseen (read (current-buffer))))
1730           ((looking-at "UIDVALIDITY \\([0-9]+\\)")
1731            (imap-mailbox-put 'uidvalidity (match-string 1)))
1732           ((search-forward "READ-ONLY" nil t)
1733            (imap-mailbox-put 'read-only t))
1734           ((search-forward "NEWNAME " nil t)
1735            (let (oldname newname)
1736              (setq oldname (imap-parse-string))
1737              (imap-forward)
1738              (setq newname (imap-parse-string))
1739              (imap-mailbox-put 'newname newname oldname)))
1740           ((search-forward "TRYCREATE" nil t)
1741            (imap-mailbox-put 'trycreate t imap-current-target-mailbox))
1742           ((looking-at "APPENDUID \\([0-9]+\\) \\([0-9]+\\)")
1743            (imap-mailbox-put 'appenduid
1744                              (list (match-string 1)
1745                                    (string-to-number (match-string 2)))
1746                              imap-current-target-mailbox))
1747           ((looking-at "COPYUID \\([0-9]+\\) \\([0-9,:]+\\) \\([0-9,:]+\\)")
1748            (imap-mailbox-put 'copyuid (list (match-string 1)
1749                                             (match-string 2)
1750                                             (match-string 3))
1751                              imap-current-target-mailbox))
1752           ((search-forward "ALERT] " nil t)
1753            (message "Imap server %s information: %s" imap-server
1754                     (buffer-substring (point) (point-max)))))))
1755
1756 ;;   mailbox-list    = "(" [mbx-list-flags] ")" SP
1757 ;;                      (DQUOTE QUOTED-CHAR DQUOTE / nil) SP mailbox
1758 ;;
1759 ;;   mbx-list-flags  = *(mbx-list-oflag SP) mbx-list-sflag
1760 ;;                     *(SP mbx-list-oflag) /
1761 ;;                     mbx-list-oflag *(SP mbx-list-oflag)
1762 ;;
1763 ;;   mbx-list-oflag  = "\Noinferiors" / flag-extension
1764 ;;                       ; Other flags; multiple possible per LIST response
1765 ;;
1766 ;;   mbx-list-sflag  = "\Noselect" / "\Marked" / "\Unmarked"
1767 ;;                       ; Selectability flags; only one per LIST response
1768 ;;
1769 ;;   QUOTED-CHAR     = <any TEXT-CHAR except quoted-specials> /
1770 ;;                     "\" quoted-specials
1771 ;;
1772 ;;   quoted-specials = DQUOTE / "\"
1773
1774 (defun imap-parse-data-list (type)
1775   (let (flags delimiter mailbox)
1776     (setq flags (imap-parse-flag-list))
1777     (when (looking-at " NIL\\| \"\\\\?\\(.\\)\"")
1778       (setq delimiter (match-string 1))
1779       (goto-char (1+ (match-end 0)))
1780       (when (setq mailbox (imap-parse-mailbox))
1781         (imap-mailbox-put type t mailbox)
1782         (imap-mailbox-put 'list-flags flags mailbox)
1783         (imap-mailbox-put 'delimiter delimiter mailbox)))))
1784
1785 ;;  msg_att         ::= "(" 1#("ENVELOPE" SPACE envelope /
1786 ;;                      "FLAGS" SPACE "(" #(flag / "\Recent") ")" /
1787 ;;                      "INTERNALDATE" SPACE date_time /
1788 ;;                      "RFC822" [".HEADER" / ".TEXT"] SPACE nstring /
1789 ;;                      "RFC822.SIZE" SPACE number /
1790 ;;                      "BODY" ["STRUCTURE"] SPACE body /
1791 ;;                      "BODY" section ["<" number ">"] SPACE nstring /
1792 ;;                      "UID" SPACE uniqueid) ")"
1793 ;;  
1794 ;;  date_time       ::= <"> date_day_fixed "-" date_month "-" date_year
1795 ;;                      SPACE time SPACE zone <">
1796 ;;  
1797 ;;  section         ::= "[" [section_text / (nz_number *["." nz_number]
1798 ;;                      ["." (section_text / "MIME")])] "]"
1799 ;;  
1800 ;;  section_text    ::= "HEADER" / "HEADER.FIELDS" [".NOT"]
1801 ;;                      SPACE header_list / "TEXT"
1802 ;;  
1803 ;;  header_fld_name ::= astring
1804 ;;  
1805 ;;  header_list     ::= "(" 1#header_fld_name ")"
1806
1807 (defsubst imap-parse-header-list ()
1808   (when (eq (char-after) ?\()
1809     (let (strlist)
1810       (while (not (eq (char-after) ?\)))
1811         (imap-forward)
1812         (push (imap-parse-astring) strlist))
1813       (imap-forward)
1814       (nreverse strlist))))
1815
1816 (defsubst imap-parse-fetch-body-section ()
1817   (let ((section 
1818          (buffer-substring (point) (1- (re-search-forward "[] ]" nil t)))))
1819     (if (eq (char-before) ? )
1820         (prog1
1821             (mapconcat 'identity (cons section (imap-parse-header-list)) " ")
1822           (search-forward "]" nil t))
1823       section)))
1824
1825 (defun imap-parse-fetch (response)
1826   (when (eq (char-after) ?\()
1827     (let (uid flags envelope internaldate rfc822 rfc822header rfc822text 
1828               rfc822size body bodydetail bodystructure)
1829       (while (not (eq (char-after) ?\)))
1830         (imap-forward)
1831         (let ((token (read (current-buffer))))
1832           (imap-forward)
1833           (cond ((eq token 'UID)
1834                  (setq uid (ignore-errors (read (current-buffer)))))
1835                 ((eq token 'FLAGS)
1836                  (setq flags (imap-parse-flag-list)))
1837                 ((eq token 'ENVELOPE)
1838                  (setq envelope (imap-parse-envelope)))
1839                 ((eq token 'INTERNALDATE)
1840                  (setq internaldate (imap-parse-string)))
1841                 ((eq token 'RFC822)
1842                  (setq rfc822 (imap-parse-nstring)))
1843                 ((eq token 'RFC822.HEADER)
1844                  (setq rfc822header (imap-parse-nstring)))
1845                 ((eq token 'RFC822.TEXT)
1846                  (setq rfc822text (imap-parse-nstring)))
1847                 ((eq token 'RFC822.SIZE)
1848                  (setq rfc822size (read (current-buffer))))
1849                 ((eq token 'BODY)
1850                  (if (eq (char-before) ?\[)
1851                      (push (list
1852                             (upcase (imap-parse-fetch-body-section))
1853                             (and (eq (char-after) ?<)
1854                                  (buffer-substring (1+ (point))
1855                                                    (search-forward ">" nil t)))
1856                             (progn (imap-forward)
1857                                    (imap-parse-nstring)))
1858                            bodydetail)
1859                    (setq body (imap-parse-body))))
1860                 ((eq token 'BODYSTRUCTURE)
1861                  (setq bodystructure (imap-parse-body))))))
1862       (when uid
1863         (setq imap-current-message uid)
1864         (imap-message-put uid 'UID uid)
1865         (and flags (imap-message-put uid 'FLAGS flags))
1866         (and envelope (imap-message-put uid 'ENVELOPE envelope))
1867         (and internaldate (imap-message-put uid 'INTERNALDATE internaldate))
1868         (and rfc822 (imap-message-put uid 'RFC822 rfc822))
1869         (and rfc822header (imap-message-put uid 'RFC822.HEADER rfc822header))
1870         (and rfc822text (imap-message-put uid 'RFC822.TEXT rfc822text))
1871         (and rfc822size (imap-message-put uid 'RFC822.SIZE rfc822size))
1872         (and body (imap-message-put uid 'BODY body))
1873         (and bodydetail (imap-message-put uid 'BODYDETAIL bodydetail))
1874         (and bodystructure (imap-message-put uid 'BODYSTRUCTURE bodystructure))
1875         (run-hooks 'imap-fetch-data-hook)))))
1876
1877 ;;   mailbox-data    =  ...
1878 ;;                      "STATUS" SP mailbox SP "("
1879 ;;                            [status-att SP number 
1880 ;;                            *(SP status-att SP number)] ")"
1881 ;;                      ...
1882 ;;
1883 ;;   status-att      = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" /
1884 ;;                     "UNSEEN"
1885
1886 (defun imap-parse-status ()
1887   (let ((mailbox (imap-parse-mailbox)))
1888     (when (and mailbox (search-forward "(" nil t))
1889       (while (not (eq (char-after) ?\)))
1890         (let ((token (read (current-buffer))))
1891           (cond ((eq token 'MESSAGES)
1892                  (imap-mailbox-put 'messages (read (current-buffer)) mailbox))
1893                 ((eq token 'RECENT)
1894                  (imap-mailbox-put 'recent (read (current-buffer)) mailbox))
1895                 ((eq token 'UIDNEXT)
1896                  (imap-mailbox-put 'uidnext (read (current-buffer)) mailbox))
1897                 ((eq token 'UIDVALIDITY)
1898                  (and (looking-at " \\([0-9]+\\)")
1899                       (imap-mailbox-put 'uidvalidity (match-string 1) mailbox)
1900                       (goto-char (match-end 1))))
1901                 ((eq token 'UNSEEN)
1902                  (imap-mailbox-put 'unseen (read (current-buffer)) mailbox))
1903                 (t
1904                  (message "Unknown status data %s in mailbox %s ignored" 
1905                           token mailbox))))))))
1906
1907 ;;   acl_data        ::= "ACL" SPACE mailbox *(SPACE identifier SPACE
1908 ;;                        rights)
1909 ;;
1910 ;;   identifier      ::= astring
1911 ;;
1912 ;;   rights          ::= astring
1913
1914 (defun imap-parse-acl ()
1915   (let ((mailbox (imap-parse-mailbox))
1916         identifier rights acl)
1917     (while (eq (char-after) ?\ )
1918       (imap-forward)
1919       (setq identifier (imap-parse-astring))
1920       (imap-forward)
1921       (setq rights (imap-parse-astring))
1922       (setq acl (append acl (list (cons identifier rights)))))
1923     (imap-mailbox-put 'acl acl mailbox)))
1924
1925 ;;   flag-list       = "(" [flag *(SP flag)] ")"
1926 ;;
1927 ;;   flag            = "\Answered" / "\Flagged" / "\Deleted" /
1928 ;;                     "\Seen" / "\Draft" / flag-keyword / flag-extension
1929 ;;                       ; Does not include "\Recent"
1930 ;;
1931 ;;   flag-keyword    = atom
1932 ;;
1933 ;;   flag-extension  = "\" atom
1934 ;;                       ; Future expansion.  Client implementations
1935 ;;                       ; MUST accept flag-extension flags.  Server
1936 ;;                       ; implementations MUST NOT generate
1937 ;;                       ; flag-extension flags except as defined by
1938 ;;                       ; future standard or standards-track
1939 ;;                       ; revisions of this specification.
1940
1941 (defun imap-parse-flag-list ()
1942   (let ((str (buffer-substring-no-properties
1943               (point) (search-forward ")" nil t)))
1944         pos)
1945     (while (setq pos (string-match "\\\\" str (and pos (+ 2 pos))))
1946       (setq str (replace-match "\\\\" nil t str)))
1947     (mapcar 'symbol-name (read str))))
1948
1949 ;;   envelope        = "(" env-date SP env-subject SP env-from SP env-sender SP
1950 ;;                     env-reply-to SP env-to SP env-cc SP env-bcc SP
1951 ;;                     env-in-reply-to SP env-message-id ")"
1952 ;;
1953 ;;   env-bcc         = "(" 1*address ")" / nil
1954 ;;
1955 ;;   env-cc          = "(" 1*address ")" / nil
1956 ;;
1957 ;;   env-date        = nstring
1958 ;;
1959 ;;   env-from        = "(" 1*address ")" / nil
1960 ;;
1961 ;;   env-in-reply-to = nstring
1962 ;;
1963 ;;   env-message-id  = nstring
1964 ;;
1965 ;;   env-reply-to    = "(" 1*address ")" / nil
1966 ;;
1967 ;;   env-sender      = "(" 1*address ")" / nil
1968 ;;
1969 ;;   env-subject     = nstring
1970 ;;
1971 ;;   env-to          = "(" 1*address ")" / nil
1972
1973 (defun imap-parse-envelope ()
1974   (when (eq (char-after) ?\()
1975     (imap-forward)
1976     (vector (prog1 (imap-parse-nstring)      ;; date
1977               (imap-forward))
1978             (prog1 (imap-parse-nstring)      ;; subject
1979               (imap-forward))
1980             (prog1 (imap-parse-address-list) ;; from
1981               (imap-forward))
1982             (prog1 (imap-parse-address-list) ;; sender
1983               (imap-forward))
1984             (prog1 (imap-parse-address-list) ;; reply-to
1985               (imap-forward))
1986             (prog1 (imap-parse-address-list) ;; to
1987               (imap-forward))
1988             (prog1 (imap-parse-address-list) ;; cc
1989               (imap-forward))
1990             (prog1 (imap-parse-address-list) ;; bcc
1991               (imap-forward))
1992             (prog1 (imap-parse-nstring)      ;; in-reply-to
1993               (imap-forward))
1994             (prog1 (imap-parse-nstring)      ;; message-id
1995               (imap-forward)))))
1996
1997 ;;   body-fld-param  = "(" string SP string *(SP string SP string) ")" / nil
1998
1999 (defsubst imap-parse-string-list ()
2000   (cond ((eq (char-after) ?\()                      ;; body-fld-param
2001          (let (strlist str)
2002            (imap-forward)
2003            (while (setq str (imap-parse-string))
2004              (push str strlist)
2005              (imap-forward))
2006            (nreverse strlist)))
2007         ((imap-parse-nil)
2008          nil)))
2009
2010 ;;   body-extension  = nstring / number /
2011 ;;                      "(" body-extension *(SP body-extension) ")"
2012 ;;                       ; Future expansion.  Client implementations
2013 ;;                       ; MUST accept body-extension fields.  Server
2014 ;;                       ; implementations MUST NOT generate
2015 ;;                       ; body-extension fields except as defined by
2016 ;;                       ; future standard or standards-track
2017 ;;                       ; revisions of this specification.
2018
2019 (defun imap-parse-body-extension ()
2020   (if (eq (char-after) ?\()
2021       (let (b-e)
2022         (imap-forward)
2023         (push (imap-parse-body-extension) b-e)
2024         (while (eq (char-after) ?\ )
2025           (imap-forward)
2026           (push (imap-parse-body-extension) b-e))
2027         (assert (eq (char-after) ?\)))
2028         (imap-forward)
2029         (nreverse b-e))
2030     (or (imap-parse-number)
2031         (imap-parse-nstring))))
2032
2033 ;;   body-ext-1part  = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang
2034 ;;                     *(SP body-extension)]]
2035 ;;                       ; MUST NOT be returned on non-extensible
2036 ;;                       ; "BODY" fetch
2037 ;;
2038 ;;   body-ext-mpart  = body-fld-param [SP body-fld-dsp [SP body-fld-lang
2039 ;;                     *(SP body-extension)]]
2040 ;;                       ; MUST NOT be returned on non-extensible
2041 ;;                       ; "BODY" fetch
2042
2043 (defsubst imap-parse-body-ext ()
2044   (let (ext)
2045     (when (eq (char-after) ?\ )                   ;; body-fld-dsp
2046       (imap-forward)
2047       (let (dsp)
2048         (if (eq (char-after) ?\()
2049             (progn
2050               (imap-forward)
2051               (push (imap-parse-string) dsp)
2052               (imap-forward)
2053               (push (imap-parse-string-list) dsp)
2054               (imap-forward))
2055           (assert (imap-parse-nil)))
2056         (push (nreverse dsp) ext))
2057       (when (eq (char-after) ?\ )                ;; body-fld-lang
2058         (imap-forward)
2059         (if (eq (char-after) ?\()
2060             (push (imap-parse-string-list) ext)
2061           (push (imap-parse-nstring) ext))
2062         (while (eq (char-after) ?\ )             ;; body-extension
2063           (imap-forward)
2064           (setq ext (append (imap-parse-body-extension) ext)))))
2065     ext))
2066
2067 ;;   body            = "(" body-type-1part / body-type-mpart ")"
2068 ;;
2069 ;;   body-ext-1part  = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang
2070 ;;                     *(SP body-extension)]]
2071 ;;                       ; MUST NOT be returned on non-extensible
2072 ;;                       ; "BODY" fetch
2073 ;;
2074 ;;   body-ext-mpart  = body-fld-param [SP body-fld-dsp [SP body-fld-lang
2075 ;;                     *(SP body-extension)]]
2076 ;;                       ; MUST NOT be returned on non-extensible
2077 ;;                       ; "BODY" fetch
2078 ;;
2079 ;;   body-fields     = body-fld-param SP body-fld-id SP body-fld-desc SP
2080 ;;                     body-fld-enc SP body-fld-octets
2081 ;;
2082 ;;   body-fld-desc   = nstring
2083 ;;
2084 ;;   body-fld-dsp    = "(" string SP body-fld-param ")" / nil
2085 ;;
2086 ;;   body-fld-enc    = (DQUOTE ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
2087 ;;                     "QUOTED-PRINTABLE") DQUOTE) / string
2088 ;;
2089 ;;   body-fld-id     = nstring
2090 ;;
2091 ;;   body-fld-lang   = nstring / "(" string *(SP string) ")"
2092 ;;
2093 ;;   body-fld-lines  = number
2094 ;;
2095 ;;   body-fld-md5    = nstring
2096 ;;
2097 ;;   body-fld-octets = number
2098 ;;
2099 ;;   body-fld-param  = "(" string SP string *(SP string SP string) ")" / nil
2100 ;;
2101 ;;   body-type-1part = (body-type-basic / body-type-msg / body-type-text)
2102 ;;                     [SP body-ext-1part]
2103 ;;
2104 ;;   body-type-basic = media-basic SP body-fields
2105 ;;                       ; MESSAGE subtype MUST NOT be "RFC822"
2106 ;;
2107 ;;   body-type-msg   = media-message SP body-fields SP envelope
2108 ;;                     SP body SP body-fld-lines
2109 ;;
2110 ;;   body-type-text  = media-text SP body-fields SP body-fld-lines
2111 ;;
2112 ;;   body-type-mpart = 1*body SP media-subtype
2113 ;;                     [SP body-ext-mpart]
2114 ;;
2115 ;;   media-basic     = ((DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" /
2116 ;;                     "MESSAGE" / "VIDEO") DQUOTE) / string) SP media-subtype
2117 ;;                       ; Defined in [MIME-IMT]
2118 ;;
2119 ;;   media-message   = DQUOTE "MESSAGE" DQUOTE SP DQUOTE "RFC822" DQUOTE
2120 ;;                      ; Defined in [MIME-IMT]
2121 ;;
2122 ;;   media-subtype   = string
2123 ;;                       ; Defined in [MIME-IMT]
2124 ;;
2125 ;;   media-text      = DQUOTE "TEXT" DQUOTE SP media-subtype
2126 ;;                       ; Defined in [MIME-IMT]
2127
2128 (defun imap-parse-body ()
2129   (let (body)
2130     (when (eq (char-after) ?\()
2131       (imap-forward)
2132       (if (eq (char-after) ?\()
2133           (let (subbody)
2134             (while (and (eq (char-after) ?\()
2135                         (setq subbody (imap-parse-body)))
2136               (push subbody body))
2137             (imap-forward)
2138             (push (imap-parse-string) body)               ;; media-subtype
2139             (when (eq (char-after) ?\ )                   ;; body-ext-mpart:
2140               (imap-forward)
2141               (if (eq (char-after) ?\()                   ;; body-fld-param
2142                   (push (imap-parse-string-list) body)
2143                 (push (and (imap-parse-nil) nil) body))
2144               (setq body
2145                     (append (imap-parse-body-ext) body))) ;; body-ext-...
2146             (assert (eq (char-after) ?\)))
2147             (imap-forward)
2148             (nreverse body))
2149
2150         (push (imap-parse-string) body)                   ;; media-type
2151         (imap-forward)
2152         (push (imap-parse-string) body)                   ;; media-subtype
2153         (imap-forward)
2154         ;; next line for Sun SIMS bug
2155         (and (eq (char-after) ? ) (imap-forward))
2156         (if (eq (char-after) ?\()                         ;; body-fld-param
2157             (push (imap-parse-string-list) body)
2158           (push (and (imap-parse-nil) nil) body))
2159         (imap-forward)
2160         (push (imap-parse-nstring) body)                  ;; body-fld-id
2161         (imap-forward)
2162         (push (imap-parse-nstring) body)                  ;; body-fld-desc
2163         (imap-forward)
2164         (push (imap-parse-string) body)                   ;; body-fld-enc
2165         (imap-forward)
2166         (push (imap-parse-number) body)                   ;; body-fld-octets
2167
2168         ;; ok, we're done parsing the required parts, what comes now is one
2169         ;; of three things:
2170         ;;
2171         ;; envelope       (then we're parsing body-type-msg)
2172         ;; body-fld-lines (then we're parsing body-type-text)
2173         ;; body-ext-1part (then we're parsing body-type-basic)
2174         ;;
2175         ;; the problem is that the two first are in turn optionally followed
2176         ;; by the third. So we parse the first two here (if there are any)...
2177
2178         (when (eq (char-after) ?\ )
2179           (imap-forward)
2180           (let (lines)
2181             (cond ((eq (char-after) ?\()                  ;; body-type-msg:
2182                    (push (imap-parse-envelope) body)      ;; envelope
2183                    (imap-forward)
2184                    (push (imap-parse-body) body)          ;; body
2185                    (imap-forward)
2186                    (push (imap-parse-number) body))       ;; body-fld-lines
2187                   ((setq lines (imap-parse-number))       ;; body-type-text:
2188                    (push lines body))                     ;; body-fld-lines
2189                   (t
2190                    (backward-char)))))                    ;; no match...
2191
2192         ;; ...and then parse the third one here...
2193
2194         (when (eq (char-after) ?\ )                       ;; body-ext-1part:
2195           (imap-forward)
2196           (push (imap-parse-nstring) body)                ;; body-fld-md5
2197           (setq body (append (imap-parse-body-ext) body)));; body-ext-1part..
2198     
2199         (assert (eq (char-after) ?\)))
2200         (imap-forward)
2201         (nreverse body)))))
2202
2203 (when imap-debug ; (untrace-all)
2204   (require 'trace)
2205   (buffer-disable-undo (get-buffer-create imap-debug))
2206   (mapc (lambda (f) (trace-function-background f imap-debug)) 
2207         '(
2208 imap-read-passwd
2209 imap-utf7-encode
2210 imap-utf7-decode
2211 imap-error-text
2212 imap-kerberos4s-p
2213 imap-kerberos4-open
2214 imap-ssl-p
2215 imap-ssl-open-2
2216 imap-ssl-open-1
2217 imap-ssl-open
2218 imap-network-p
2219 imap-network-open
2220 imap-interactive-login
2221 imap-kerberos4a-p
2222 imap-kerberos4-auth
2223 imap-cram-md5-p
2224 imap-cram-md5-auth
2225 imap-login-p
2226 imap-login-auth
2227 imap-anonymous-p
2228 imap-anonymous-auth
2229 imap-open-1
2230 imap-open
2231 imap-opened
2232 imap-authenticate
2233 imap-close
2234 imap-capability
2235 imap-namespace
2236 imap-send-command-wait
2237 imap-mailbox-put
2238 imap-mailbox-get
2239 imap-mailbox-map-1
2240 imap-mailbox-map
2241 imap-current-mailbox
2242 imap-current-mailbox-p-1
2243 imap-current-mailbox-p
2244 imap-mailbox-select-1
2245 imap-mailbox-select
2246 imap-mailbox-examine
2247 imap-mailbox-unselect
2248 imap-mailbox-expunge
2249 imap-mailbox-close
2250 imap-mailbox-create-1
2251 imap-mailbox-create
2252 imap-mailbox-delete
2253 imap-mailbox-rename
2254 imap-mailbox-lsub
2255 imap-mailbox-list
2256 imap-mailbox-subscribe
2257 imap-mailbox-unsubscribe
2258 imap-mailbox-status
2259 imap-mailbox-acl-get
2260 imap-mailbox-acl-set
2261 imap-mailbox-acl-delete
2262 imap-current-message
2263 imap-list-to-message-set
2264 imap-fetch-asynch
2265 imap-fetch
2266 imap-message-put
2267 imap-message-get
2268 imap-message-map
2269 imap-search
2270 imap-message-flag-permanent-p
2271 imap-message-flags-set
2272 imap-message-flags-del
2273 imap-message-flags-add
2274 imap-message-copyuid-1
2275 imap-message-copyuid
2276 imap-message-copy
2277 imap-message-appenduid-1
2278 imap-message-appenduid
2279 imap-message-append
2280 imap-body-lines
2281 imap-envelope-from
2282 imap-send-command-1
2283 imap-send-command
2284 imap-wait-for-tag
2285 imap-sentinel
2286 imap-find-next-line
2287 imap-arrival-filter
2288 imap-parse-greeting
2289 imap-parse-response
2290 imap-parse-resp-text
2291 imap-parse-resp-text-code
2292 imap-parse-data-list
2293 imap-parse-fetch
2294 imap-parse-status
2295 imap-parse-acl
2296 imap-parse-flag-list
2297 imap-parse-envelope
2298 imap-parse-body-extension
2299 imap-parse-body
2300           )))
2301         
2302 (provide 'imap)
2303
2304 ;;; imap.el ends here