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