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