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