1 ;;; imap.el --- imap library
3 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 ;; 2005, 2006, 2007 Free Software Foundation, Inc.
6 ;; Author: Simon Josefsson <jas@pdc.kth.se>
9 ;; This file is part of GNU Emacs.
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
28 ;; imap.el is a elisp library providing an interface for talking to
31 ;; imap.el is roughly divided in two parts, one that parses IMAP
32 ;; responses from the server and storing data into buffer-local
33 ;; variables, and one for utility functions which send commands to
34 ;; server, waits for an answer, and return information. The latter
35 ;; part is layered on top of the previous.
37 ;; The imap.el API consist of the following functions, other functions
38 ;; in this file should not be called directly and the result of doing
39 ;; so are at best undefined.
43 ;; imap-open, imap-opened, imap-authenticate, imap-close,
44 ;; imap-capability, imap-namespace, imap-error-text
48 ;; imap-mailbox-get, imap-mailbox-map, imap-current-mailbox,
49 ;; imap-current-mailbox-p, imap-search, imap-mailbox-select,
50 ;; imap-mailbox-examine, imap-mailbox-unselect, imap-mailbox-expunge
51 ;; imap-mailbox-close, imap-mailbox-create, imap-mailbox-delete
52 ;; imap-mailbox-rename, imap-mailbox-lsub, imap-mailbox-list
53 ;; imap-mailbox-subscribe, imap-mailbox-unsubscribe, imap-mailbox-status
54 ;; imap-mailbox-acl-get, imap-mailbox-acl-set, imap-mailbox-acl-delete
58 ;; imap-fetch-asynch, imap-fetch,
59 ;; imap-current-message, imap-list-to-message-set,
60 ;; imap-message-get, imap-message-map
61 ;; imap-message-envelope-date, imap-message-envelope-subject,
62 ;; imap-message-envelope-from, imap-message-envelope-sender,
63 ;; imap-message-envelope-reply-to, imap-message-envelope-to,
64 ;; imap-message-envelope-cc, imap-message-envelope-bcc
65 ;; imap-message-envelope-in-reply-to, imap-message-envelope-message-id
66 ;; imap-message-body, imap-message-flag-permanent-p
67 ;; imap-message-flags-set, imap-message-flags-del
68 ;; imap-message-flags-add, imap-message-copyuid
69 ;; imap-message-copy, imap-message-appenduid
70 ;; imap-message-append, imap-envelope-from
73 ;; It is my hope that these commands should be pretty self
74 ;; explanatory for someone that know IMAP. All functions have
75 ;; additional documentation on how to invoke them.
77 ;; imap.el support RFC1730/2060/RFC3501 (IMAP4/IMAP4rev1), implemented
78 ;; IMAP extensions are RFC2195 (CRAM-MD5), RFC2086 (ACL), RFC2342
79 ;; (NAMESPACE), RFC2359 (UIDPLUS), the IMAP-part of RFC2595 (STARTTLS,
80 ;; LOGINDISABLED) (with use of external library starttls.el and
81 ;; program starttls), and the GSSAPI / kerberos V4 sections of RFC1731
82 ;; (with use of external program `imtest'), RFC2971 (ID). It also
83 ;; takes advantage of the UNSELECT extension in Cyrus IMAPD.
85 ;; Without the work of John McClary Prevost and Jim Radford this library
86 ;; would not have seen the light of day. Many thanks.
88 ;; This is a transcript of short interactive session for demonstration
91 ;; (imap-open "my.mail.server")
92 ;; => " *imap* my.mail.server:0"
94 ;; The rest are invoked with current buffer as the buffer returned by
95 ;; `imap-open'. It is possible to do all without this, but it would
96 ;; look ugly here since `buffer' is always the last argument for all
97 ;; imap.el API functions.
99 ;; (imap-authenticate "myusername" "mypassword")
102 ;; (imap-mailbox-lsub "*")
103 ;; => ("INBOX.sentmail" "INBOX.private" "INBOX.draft" "INBOX.spam")
105 ;; (imap-mailbox-list "INBOX.n%")
106 ;; => ("INBOX.namedroppers" "INBOX.nnimap" "INBOX.ntbugtraq")
108 ;; (imap-mailbox-select "INBOX.nnimap")
111 ;; (imap-mailbox-get 'exists)
114 ;; (imap-mailbox-get 'uidvalidity)
117 ;; (imap-search "FLAGGED SINCE 18-DEC-98")
120 ;; (imap-fetch 235 "RFC822.PEEK" 'RFC822)
121 ;; => "X-Sieve: cmu-sieve 1.3^M\nX-Username: <jas@pdc.kth.se>^M\r...."
125 ;; o Parse UIDs as strings? We need to overcome the 28 bit limit somehow.
126 ;; o Don't use `read' at all (important places already fixed)
127 ;; o Accept list of articles instead of message set string in most
128 ;; imap-message-* functions.
129 ;; o Send strings as literal if they contain, e.g., ".
133 ;; - 19991218 added starttls/digest-md5 patch,
134 ;; by Daiki Ueno <ueno@ueda.info.waseda.ac.jp>
135 ;; NB! you need SLIM for starttls.el and digest-md5.el
136 ;; - 19991023 commited to pgnus
141 (eval-when-compile (require 'cl))
143 (autoload 'starttls-open-stream "starttls")
144 (autoload 'starttls-negotiate "starttls")
145 (autoload 'sasl-find-mechanism "sasl")
146 (autoload 'digest-md5-parse-digest-challenge "digest-md5")
147 (autoload 'digest-md5-digest-response "digest-md5")
148 (autoload 'digest-md5-digest-uri "digest-md5")
149 (autoload 'digest-md5-challenge "digest-md5")
150 (autoload 'rfc2104-hash "rfc2104")
151 (autoload 'utf7-encode "utf7")
152 (autoload 'utf7-decode "utf7")
153 (autoload 'format-spec "format-spec")
154 (autoload 'format-spec-make "format-spec")
155 (autoload 'open-tls-stream "tls"))
160 "Low-level IMAP issues."
164 (defcustom imap-kerberos4-program '("imtest -m kerberos_v4 -u %l -p %p %s"
166 "List of strings containing commands for Kerberos 4 authentication.
167 %s is replaced with server hostname, %p with port to connect to, and
168 %l with the value of `imap-default-user'. The program should accept
169 IMAP commands on stdin and return responses to stdout. Each entry in
170 the list is tried until a successful connection is made."
172 :type '(repeat string))
174 (defcustom imap-gssapi-program (list
175 (concat "gsasl %s %p "
176 "--mechanism GSSAPI "
177 "--authentication-id %l")
178 "imtest -m gssapi -u %l -p %p %s")
179 "List of strings containing commands for GSSAPI (krb5) authentication.
180 %s is replaced with server hostname, %p with port to connect to, and
181 %l with the value of `imap-default-user'. The program should accept
182 IMAP commands on stdin and return responses to stdout. Each entry in
183 the list is tried until a successful connection is made."
185 :type '(repeat string))
187 (defcustom imap-ssl-program '("openssl s_client -quiet -ssl3 -connect %s:%p"
188 "openssl s_client -quiet -ssl2 -connect %s:%p"
189 "s_client -quiet -ssl3 -connect %s:%p"
190 "s_client -quiet -ssl2 -connect %s:%p")
191 "A string, or list of strings, containing commands for SSL connections.
192 Within a string, %s is replaced with the server address and %p with
193 port number on server. The program should accept IMAP commands on
194 stdin and return responses to stdout. Each entry in the list is tried
195 until a successful connection is made."
197 :type '(choice string
200 (defcustom imap-shell-program '("ssh %s imapd"
202 "ssh %g ssh %s imapd"
203 "rsh %g rsh %s imapd")
204 "A list of strings, containing commands for IMAP connection.
205 Within a string, %s is replaced with the server address, %p with port
206 number on server, %g with `imap-shell-host', and %l with
207 `imap-default-user'. The program should read IMAP commands from stdin
208 and write IMAP response to stdout. Each entry in the list is tried
209 until a successful connection is made."
211 :type '(repeat string))
213 (defcustom imap-process-connection-type nil
214 "*Value for `process-connection-type' to use for Kerberos4, GSSAPI and SSL.
215 The `process-connection-type' variable control type of device
216 used to communicate with subprocesses. Values are nil to use a
217 pipe, or t or `pty' to use a pty. The value has no effect if the
218 system has no ptys or if all ptys are busy: then a pipe is used
219 in any case. The value takes effect when a IMAP server is
220 opened, changing it after that has no effect."
225 (defcustom imap-use-utf7 t
226 "If non-nil, do utf7 encoding/decoding of mailbox names.
227 Since the UTF7 decoding currently only decodes into ISO-8859-1
228 characters, you may disable this decoding if you need to access UTF7
229 encoded mailboxes which doesn't translate into ISO-8859-1."
233 (defcustom imap-log nil
234 "If non-nil, a imap session trace is placed in *imap-log* buffer.
235 Note that username, passwords and other privacy sensitive
236 information (such as e-mail) may be stored in the *imap-log*
237 buffer. It is not written to disk, however. Do not enable this
238 variable unless you are comfortable with that."
242 (defcustom imap-debug nil
243 "If non-nil, random debug spews are placed in *imap-debug* buffer.
244 Note that username, passwords and other privacy sensitive
245 information (such as e-mail) may be stored in the *imap-debug*
246 buffer. It is not written to disk, however. Do not enable this
247 variable unless you are comfortable with that."
251 (defcustom imap-shell-host "gateway"
252 "Hostname of rlogin proxy."
256 (defcustom imap-default-user (user-login-name)
257 "Default username to use."
261 (defcustom imap-read-timeout (if (string-match
262 "windows-nt\\|os/2\\|emx\\|cygwin"
263 (symbol-name system-type))
266 "*How long to wait between checking for the end of output.
267 Shorter values mean quicker response, but is more CPU intensive."
271 (defcustom imap-store-password nil
272 "If non-nil, store session password without promting."
276 ;; Various variables.
278 (defvar imap-fetch-data-hook nil
279 "Hooks called after receiving each FETCH response.")
281 (defvar imap-streams '(gssapi kerberos4 starttls tls ssl network shell)
282 "Priority of streams to consider when opening connection to server.")
284 (defvar imap-stream-alist
285 '((gssapi imap-gssapi-stream-p imap-gssapi-open)
286 (kerberos4 imap-kerberos4-stream-p imap-kerberos4-open)
287 (tls imap-tls-p imap-tls-open)
288 (ssl imap-ssl-p imap-ssl-open)
289 (network imap-network-p imap-network-open)
290 (shell imap-shell-p imap-shell-open)
291 (starttls imap-starttls-p imap-starttls-open))
292 "Definition of network streams.
296 NAME names the stream, CHECK is a function returning non-nil if the
297 server support the stream and OPEN is a function for opening the
300 (defvar imap-authenticators '(gssapi
307 "Priority of authenticators to consider when authenticating to server.")
309 (defvar imap-authenticator-alist
310 '((gssapi imap-gssapi-auth-p imap-gssapi-auth)
311 (kerberos4 imap-kerberos4-auth-p imap-kerberos4-auth)
312 (sasl imap-sasl-auth-p imap-sasl-auth)
313 (cram-md5 imap-cram-md5-p imap-cram-md5-auth)
314 (login imap-login-p imap-login-auth)
315 (anonymous imap-anonymous-p imap-anonymous-auth)
316 (digest-md5 imap-digest-md5-p imap-digest-md5-auth))
317 "Definition of authenticators.
319 \(NAME CHECK AUTHENTICATE)
321 NAME names the authenticator. CHECK is a function returning non-nil if
322 the server support the authenticator and AUTHENTICATE is a function
323 for doing the actual authentication.")
325 (defvar imap-error nil
326 "Error codes from the last command.")
328 ;; Internal constants. Change these and die.
330 (defconst imap-default-port 143)
331 (defconst imap-default-ssl-port 993)
332 (defconst imap-default-tls-port 993)
333 (defconst imap-default-stream 'network)
334 (defconst imap-coding-system-for-read 'binary)
335 (defconst imap-coding-system-for-write 'binary)
336 (defconst imap-local-variables '(imap-server
345 imap-current-target-mailbox
355 imap-calculate-literal-size-first
357 (defconst imap-log-buffer "*imap-log*")
358 (defconst imap-debug-buffer "*imap-debug*")
360 ;; Internal variables.
362 (defvar imap-stream nil)
363 (defvar imap-auth nil)
364 (defvar imap-server nil)
365 (defvar imap-port nil)
366 (defvar imap-username nil)
367 (defvar imap-password nil)
368 (defvar imap-calculate-literal-size-first nil)
369 (defvar imap-state 'closed
371 Valid states are `closed', `initial', `nonauth', `auth', `selected'
374 (defvar imap-server-eol "\r\n"
375 "The EOL string sent from the server.")
377 (defvar imap-client-eol "\r\n"
378 "The EOL string we send to the server.")
380 (defvar imap-current-mailbox nil
381 "Current mailbox name.")
383 (defvar imap-current-target-mailbox nil
384 "Current target mailbox for COPY and APPEND commands.")
386 (defvar imap-mailbox-data nil
387 "Obarray with mailbox data.")
389 (defvar imap-mailbox-prime 997
390 "Length of imap-mailbox-data.")
392 (defvar imap-current-message nil
393 "Current message number.")
395 (defvar imap-message-data nil
396 "Obarray with message data.")
398 (defvar imap-message-prime 997
399 "Length of imap-message-data.")
401 (defvar imap-capability nil
402 "Capability for server.")
408 (defvar imap-namespace nil
409 "Namespace for current server.")
411 (defvar imap-reached-tag 0
412 "Lower limit on command tags that have been parsed.")
414 (defvar imap-failed-tags nil
415 "Alist of tags that failed.
416 Each element is a list with four elements; tag (a integer), response
417 state (a symbol, `OK', `NO' or `BAD'), response code (a string), and
418 human readable response text (a string).")
421 "Command tag number.")
423 (defvar imap-process nil
426 (defvar imap-continuation nil
427 "Non-nil indicates that the server emitted a continuation request.
428 The actual value is really the text on the continuation line.")
430 (defvar imap-callbacks nil
431 "List of response tags and callbacks, on the form `(number . function)'.
432 The function should take two arguments, the first the IMAP tag and the
433 second the status (OK, NO, BAD etc) of the command.")
436 ;; Utility functions:
438 (defun imap-remassoc (key alist)
439 "Delete by side effect any elements of LIST whose car is `equal' to KEY.
440 The modified LIST is returned. If the first member
441 of LIST has a car that is `equal' to KEY, there is no way to remove it
442 by side effect; therefore, write `(setq foo (remassoc key foo))' to be
443 sure of changing the value of `foo'."
445 (if (equal key (caar alist))
447 (setcdr alist (imap-remassoc key (cdr alist)))
450 (defsubst imap-disable-multibyte ()
451 "Enable multibyte in the current buffer."
452 (when (fboundp 'set-buffer-multibyte)
453 (set-buffer-multibyte nil)))
455 (defsubst imap-utf7-encode (string)
459 (utf7-encode string t)
461 "imap: Could not UTF7 encode `%s', using it unencoded..."
466 (defsubst imap-utf7-decode (string)
470 (utf7-decode string t)
472 "imap: Could not UTF7 decode `%s', using it undecoded..."
477 (defsubst imap-ok-p (status)
480 (setq imap-error status)
483 (defun imap-error-text (&optional buffer)
484 (with-current-buffer (or buffer (current-buffer))
485 (nth 3 (car imap-failed-tags))))
488 ;; Server functions; stream stuff:
490 (defun imap-kerberos4-stream-p (buffer)
491 (imap-capability 'AUTH=KERBEROS_V4 buffer))
493 (defun imap-kerberos4-open (name buffer server port)
494 (let ((cmds imap-kerberos4-program)
496 (while (and (not done) (setq cmd (pop cmds)))
497 (message "Opening Kerberos 4 IMAP connection with `%s'..." cmd)
499 (let* ((port (or port imap-default-port))
500 (coding-system-for-read imap-coding-system-for-read)
501 (coding-system-for-write imap-coding-system-for-write)
502 (process-connection-type imap-process-connection-type)
503 (process (start-process
504 name buffer shell-file-name shell-command-switch
509 ?p (number-to-string port)
510 ?l imap-default-user))))
513 (with-current-buffer buffer
514 (setq imap-client-eol "\n"
515 imap-calculate-literal-size-first t)
516 (while (and (memq (process-status process) '(open run))
517 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
518 (goto-char (point-min))
519 ;; Athena IMTEST can output SSL verify errors
520 (or (while (looking-at "^verify error:num=")
523 (or (while (looking-at "^TLS connection established")
526 ;; cyrus 1.6.x (13? < x <= 22) queries capabilities
527 (or (while (looking-at "^C:")
530 ;; cyrus 1.6 imtest print "S: " before server greeting
531 (or (not (looking-at "S: "))
534 (not (and (imap-parse-greeting)
535 ;; success in imtest < 1.6:
536 (or (re-search-forward
537 "^__\\(.*\\)__\n" nil t)
538 ;; success in imtest 1.6:
540 "^\\(Authenticat.*\\)" nil t))
541 (setq response (match-string 1)))))
542 (accept-process-output process 1)
545 (with-current-buffer (get-buffer-create imap-log-buffer)
546 (imap-disable-multibyte)
547 (buffer-disable-undo)
548 (goto-char (point-max))
549 (insert-buffer-substring buffer)))
551 (message "Opening Kerberos 4 IMAP connection with `%s'...%s" cmd
552 (if response (concat "done, " response) "failed"))
553 (if (and response (let ((case-fold-search nil))
554 (not (string-match "failed" response))))
556 (if (memq (process-status process) '(open run))
557 (imap-send-command "LOGOUT"))
558 (delete-process process)
562 (defun imap-gssapi-stream-p (buffer)
563 (imap-capability 'AUTH=GSSAPI buffer))
565 (defun imap-gssapi-open (name buffer server port)
566 (let ((cmds imap-gssapi-program)
568 (while (and (not done) (setq cmd (pop cmds)))
569 (message "Opening GSSAPI IMAP connection with `%s'..." cmd)
571 (let* ((port (or port imap-default-port))
572 (coding-system-for-read imap-coding-system-for-read)
573 (coding-system-for-write imap-coding-system-for-write)
574 (process-connection-type imap-process-connection-type)
575 (process (start-process
576 name buffer shell-file-name shell-command-switch
581 ?p (number-to-string port)
582 ?l imap-default-user))))
585 (with-current-buffer buffer
586 (setq imap-client-eol "\n"
587 imap-calculate-literal-size-first t)
588 (while (and (memq (process-status process) '(open run))
589 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
590 (goto-char (point-min))
591 ;; Athena IMTEST can output SSL verify errors
592 (or (while (looking-at "^verify error:num=")
595 (or (while (looking-at "^TLS connection established")
598 ;; cyrus 1.6.x (13? < x <= 22) queries capabilities
599 (or (while (looking-at "^C:")
602 ;; cyrus 1.6 imtest print "S: " before server greeting
603 (or (not (looking-at "S: "))
606 ;; GNU SASL may print 'Trying ...' first.
607 (or (not (looking-at "Trying "))
610 (not (and (imap-parse-greeting)
611 ;; success in imtest 1.6:
613 (concat "^\\(\\(Authenticat.*\\)\\|\\("
614 "Client authentication "
617 (setq response (match-string 1)))))
618 (accept-process-output process 1)
621 (with-current-buffer (get-buffer-create imap-log-buffer)
622 (imap-disable-multibyte)
623 (buffer-disable-undo)
624 (goto-char (point-max))
625 (insert-buffer-substring buffer)))
627 (message "GSSAPI IMAP connection: %s" (or response "failed"))
628 (if (and response (let ((case-fold-search nil))
629 (not (string-match "failed" response))))
631 (if (memq (process-status process) '(open run))
632 (imap-send-command "LOGOUT"))
633 (delete-process process)
637 (defun imap-ssl-p (buffer)
640 (defun imap-ssl-open (name buffer server port)
641 "Open a SSL connection to server."
642 (let ((cmds (if (listp imap-ssl-program) imap-ssl-program
643 (list imap-ssl-program)))
645 (while (and (not done) (setq cmd (pop cmds)))
646 (message "imap: Opening SSL connection with `%s'..." cmd)
648 (let* ((port (or port imap-default-ssl-port))
649 (coding-system-for-read imap-coding-system-for-read)
650 (coding-system-for-write imap-coding-system-for-write)
651 (process-connection-type imap-process-connection-type)
652 (set-process-query-on-exit-flag
653 (if (fboundp 'set-process-query-on-exit-flag)
654 'set-process-query-on-exit-flag
655 'process-kill-without-query))
658 (setq process (start-process
659 name buffer shell-file-name
664 ?p (number-to-string port)))))
665 (funcall set-process-query-on-exit-flag process nil)
667 (with-current-buffer buffer
668 (goto-char (point-min))
669 (while (and (memq (process-status process) '(open run))
670 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
671 (goto-char (point-max))
673 (not (imap-parse-greeting)))
674 (accept-process-output process 1)
677 (with-current-buffer (get-buffer-create imap-log-buffer)
678 (imap-disable-multibyte)
679 (buffer-disable-undo)
680 (goto-char (point-max))
681 (insert-buffer-substring buffer)))
683 (when (memq (process-status process) '(open run))
684 (setq done process))))))
687 (message "imap: Opening SSL connection with `%s'...done" cmd)
689 (message "imap: Opening SSL connection with `%s'...failed" cmd)
692 (defun imap-tls-p (buffer)
695 (defun imap-tls-open (name buffer server port)
696 (let* ((port (or port imap-default-tls-port))
697 (coding-system-for-read imap-coding-system-for-read)
698 (coding-system-for-write imap-coding-system-for-write)
699 (process (open-tls-stream name buffer server port)))
701 (while (and (memq (process-status process) '(open run))
702 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
703 (goto-char (point-max))
705 (not (imap-parse-greeting)))
706 (accept-process-output process 1)
709 (with-current-buffer (get-buffer-create imap-log-buffer)
710 (imap-disable-multibyte)
711 (buffer-disable-undo)
712 (goto-char (point-max))
713 (insert-buffer-substring buffer)))
714 (when (memq (process-status process) '(open run))
717 (defun imap-network-p (buffer)
720 (defun imap-network-open (name buffer server port)
721 (let* ((port (or port imap-default-port))
722 (coding-system-for-read imap-coding-system-for-read)
723 (coding-system-for-write imap-coding-system-for-write)
724 (process (open-network-stream name buffer server port)))
726 (while (and (memq (process-status process) '(open run))
727 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
728 (goto-char (point-min))
729 (not (imap-parse-greeting)))
730 (accept-process-output process 1)
733 (with-current-buffer (get-buffer-create imap-log-buffer)
734 (imap-disable-multibyte)
735 (buffer-disable-undo)
736 (goto-char (point-max))
737 (insert-buffer-substring buffer)))
738 (when (memq (process-status process) '(open run))
741 (defun imap-shell-p (buffer)
744 (defun imap-shell-open (name buffer server port)
745 (let ((cmds (if (listp imap-shell-program) imap-shell-program
746 (list imap-shell-program)))
748 (while (and (not done) (setq cmd (pop cmds)))
749 (message "imap: Opening IMAP connection with `%s'..." cmd)
750 (setq imap-client-eol "\n")
751 (let* ((port (or port imap-default-port))
752 (coding-system-for-read imap-coding-system-for-read)
753 (coding-system-for-write imap-coding-system-for-write)
754 (process (start-process
755 name buffer shell-file-name shell-command-switch
761 ?p (number-to-string port)
762 ?l imap-default-user)))))
764 (while (and (memq (process-status process) '(open run))
765 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
766 (goto-char (point-max))
768 (not (imap-parse-greeting)))
769 (accept-process-output process 1)
772 (with-current-buffer (get-buffer-create imap-log-buffer)
773 (imap-disable-multibyte)
774 (buffer-disable-undo)
775 (goto-char (point-max))
776 (insert-buffer-substring buffer)))
778 (when (memq (process-status process) '(open run))
779 (setq done process)))))
782 (message "imap: Opening IMAP connection with `%s'...done" cmd)
784 (message "imap: Opening IMAP connection with `%s'...failed" cmd)
787 (defun imap-starttls-p (buffer)
788 (imap-capability 'STARTTLS buffer))
790 (defun imap-starttls-open (name buffer server port)
791 (let* ((port (or port imap-default-port))
792 (coding-system-for-read imap-coding-system-for-read)
793 (coding-system-for-write imap-coding-system-for-write)
794 (process (starttls-open-stream name buffer server port))
796 (message "imap: Connecting with STARTTLS...")
798 (while (and (memq (process-status process) '(open run))
799 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
800 (goto-char (point-max))
802 (not (imap-parse-greeting)))
803 (accept-process-output process 1)
805 (imap-send-command "STARTTLS")
806 (while (and (memq (process-status process) '(open run))
807 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
808 (goto-char (point-max))
810 (not (re-search-forward "[0-9]+ OK.*\r?\n" nil t)))
811 (accept-process-output process 1)
814 (with-current-buffer (get-buffer-create imap-log-buffer)
815 (buffer-disable-undo)
816 (goto-char (point-max))
817 (insert-buffer-substring buffer)))
818 (when (and (setq tls-info (starttls-negotiate process))
819 (memq (process-status process) '(open run)))
820 (setq done process)))
821 (if (stringp tls-info)
822 (message "imap: STARTTLS info: %s" tls-info))
823 (message "imap: Connecting with STARTTLS...%s" (if done "done" "failed"))
826 ;; Server functions; authenticator stuff:
828 (defun imap-interactive-login (buffer loginfunc)
829 "Login to server in BUFFER.
830 LOGINFUNC is passed a username and a password, it should return t if
831 it where successful authenticating itself to the server, nil otherwise.
832 Returns t if login was successful, nil otherwise."
833 (with-current-buffer buffer
834 (make-local-variable 'imap-username)
835 (make-local-variable 'imap-password)
836 (let (user passwd ret)
837 ;; (condition-case ()
838 (while (or (not user) (not passwd))
839 (setq user (or imap-username
840 (read-from-minibuffer
841 (concat "IMAP username for " imap-server
842 " (using stream `" (symbol-name imap-stream)
844 (or user imap-default-user))))
845 (setq passwd (or imap-password
847 (concat "IMAP password for " user "@"
848 imap-server " (using authenticator `"
849 (symbol-name imap-auth) "'): "))))
850 (when (and user passwd)
851 (if (funcall loginfunc user passwd)
855 (when (and (not imap-password)
856 (or imap-store-password
857 (y-or-n-p "Store password for this session? ")))
858 (setq imap-password passwd)))
859 (message "Login failed...")
861 (setq imap-password nil)
863 ;; (quit (with-current-buffer buffer
866 ;; (error (with-current-buffer buffer
871 (defun imap-gssapi-auth-p (buffer)
872 (eq imap-stream 'gssapi))
874 (defun imap-gssapi-auth (buffer)
875 (message "imap: Authenticating using GSSAPI...%s"
876 (if (eq imap-stream 'gssapi) "done" "failed"))
877 (eq imap-stream 'gssapi))
879 (defun imap-kerberos4-auth-p (buffer)
880 (and (imap-capability 'AUTH=KERBEROS_V4 buffer)
881 (eq imap-stream 'kerberos4)))
883 (defun imap-kerberos4-auth (buffer)
884 (message "imap: Authenticating using Kerberos 4...%s"
885 (if (eq imap-stream 'kerberos4) "done" "failed"))
886 (eq imap-stream 'kerberos4))
888 (defun imap-cram-md5-p (buffer)
889 (imap-capability 'AUTH=CRAM-MD5 buffer))
891 (defun imap-cram-md5-auth (buffer)
892 "Login to server using the AUTH CRAM-MD5 method."
893 (message "imap: Authenticating using CRAM-MD5...")
894 (let ((done (imap-interactive-login
896 (lambda (user passwd)
898 (imap-send-command-wait
900 "AUTHENTICATE CRAM-MD5"
902 (let* ((decoded (base64-decode-string challenge))
903 (hash (rfc2104-hash 'md5 64 16 passwd decoded))
904 (response (concat user " " hash))
905 (encoded (base64-encode-string response)))
908 (message "imap: Authenticating using CRAM-MD5...done")
909 (message "imap: Authenticating using CRAM-MD5...failed"))))
911 (defun imap-login-p (buffer)
912 (and (not (imap-capability 'LOGINDISABLED buffer))
913 (not (imap-capability 'X-LOGIN-CMD-DISABLED buffer))))
915 (defun imap-quote-specials (string)
918 (goto-char (point-min))
919 (while (re-search-forward "[\\\"]" nil t)
925 (defun imap-login-auth (buffer)
926 "Login to server using the LOGIN command."
927 (message "imap: Plaintext authentication...")
928 (imap-interactive-login buffer
929 (lambda (user passwd)
930 (imap-ok-p (imap-send-command-wait
932 (imap-quote-specials user)
934 (imap-quote-specials passwd)
937 (defun imap-anonymous-p (buffer)
940 (defun imap-anonymous-auth (buffer)
941 (message "imap: Logging in anonymously...")
942 (with-current-buffer buffer
943 (imap-ok-p (imap-send-command-wait
944 (concat "LOGIN anonymous \"" (concat (user-login-name) "@"
945 (system-name)) "\"")))))
947 ;;; Compiler directives.
949 (defvar imap-sasl-client)
950 (defvar imap-sasl-step)
952 (defun imap-sasl-make-mechanisms (buffer)
955 (let ((name (symbol-name sym)))
956 (if (and (> (length name) 5)
957 (string-equal "AUTH=" (substring name 0 5 )))
958 (setq mecs (cons (substring name 5) mecs)))))
959 (imap-capability nil buffer))
962 (defun imap-sasl-auth-p (buffer)
963 (and (condition-case ()
966 (sasl-find-mechanism (imap-sasl-make-mechanisms buffer))))
968 (defun imap-sasl-auth (buffer)
969 "Login to server using the SASL method."
970 (message "imap: Authenticating using SASL...")
971 (with-current-buffer buffer
972 (make-local-variable 'imap-username)
973 (make-local-variable 'imap-sasl-client)
974 (make-local-variable 'imap-sasl-step)
975 (let ((mechanism (sasl-find-mechanism (imap-sasl-make-mechanisms buffer)))
978 (setq user (or imap-username
979 (read-from-minibuffer
980 (concat "IMAP username for " imap-server " using SASL "
981 (sasl-mechanism-name mechanism) ": ")
982 (or user imap-default-user))))
984 (setq imap-sasl-client (sasl-make-client mechanism user "imap2" imap-server)
985 imap-sasl-step (sasl-next-step imap-sasl-client nil))
986 (let ((tag (imap-send-command
987 (if (sasl-step-data imap-sasl-step)
988 (format "AUTHENTICATE %s %s"
989 (sasl-mechanism-name mechanism)
990 (sasl-step-data imap-sasl-step))
991 (format "AUTHENTICATE %s" (sasl-mechanism-name mechanism)))
993 (while (eq (imap-wait-for-tag tag) 'INCOMPLETE)
994 (sasl-step-set-data imap-sasl-step (base64-decode-string imap-continuation))
995 (setq imap-continuation nil
996 imap-sasl-step (sasl-next-step imap-sasl-client imap-sasl-step))
997 (imap-send-command-1 (if (sasl-step-data imap-sasl-step)
998 (base64-encode-string (sasl-step-data imap-sasl-step) t)
1000 (if (imap-ok-p (imap-wait-for-tag tag))
1001 (setq imap-username user
1003 (message "Login failed...")