;;; imap.el --- imap library
-;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
-;; Free Software Foundation, Inc.
+
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;; 2005, 2006 Free Software Foundation, Inc.
;; Author: Simon Josefsson <jas@pdc.kth.se>
;; Keywords: mail
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; imap-message-append, imap-envelope-from
;; imap-body-lines
;;
-;; It is my hope that theese commands should be pretty self
+;; It is my hope that these commands should be pretty self
;; explanatory for someone that know IMAP. All functions have
;; additional documentation on how to invoke them.
;;
(eval-when-compile (require 'cl))
(eval-and-compile
- (autoload 'base64-decode-string "base64")
- (autoload 'base64-encode-string "base64")
(autoload 'starttls-open-stream "starttls")
(autoload 'starttls-negotiate "starttls")
+ (autoload 'sasl-find-mechanism "sasl")
(autoload 'digest-md5-parse-digest-challenge "digest-md5")
(autoload 'digest-md5-digest-response "digest-md5")
(autoload 'digest-md5-digest-uri "digest-md5")
(autoload 'digest-md5-challenge "digest-md5")
(autoload 'rfc2104-hash "rfc2104")
- (autoload 'md5 "md5")
(autoload 'utf7-encode "utf7")
(autoload 'utf7-decode "utf7")
(autoload 'format-spec "format-spec")
(autoload 'format-spec-make "format-spec")
- (autoload 'open-tls-stream "tls")
- ;; Avoid use gnus-point-at-eol so we're independent of Gnus. These
- ;; days we have point-at-eol anyhow.
- (if (fboundp 'point-at-eol)
- (defalias 'imap-point-at-eol 'point-at-eol)
- (defun imap-point-at-eol ()
- (save-excursion
- (end-of-line)
- (point)))))
+ (autoload 'open-tls-stream "tls"))
;; User variables.
:type '(repeat string))
(defcustom imap-gssapi-program (list
- (concat "gsasl --client --connect %s:%p "
- "--imap --application-data "
+ (concat "gsasl %s %p "
"--mechanism GSSAPI "
"--authentication-id %l")
"imtest -m gssapi -u %l -p %p %s")
pipe, or t or `pty' to use a pty. The value has no effect if the
system has no ptys or if all ptys are busy: then a pipe is used
in any case. The value takes effect when a IMAP server is
-opened, changing it after that has no effect.."
+opened, changing it after that has no effect."
+ :version "22.1"
:group 'imap
:type 'boolean)
:type 'boolean)
(defcustom imap-log nil
- "If non-nil, a imap session trace is placed in *imap-log* buffer."
+ "If non-nil, a imap session trace is placed in *imap-log* buffer.
+Note that username, passwords and other privacy sensitive
+information (such as e-mail) may be stored in the *imap-log*
+buffer. It is not written to disk, however. Do not enable this
+variable unless you are comfortable with that."
:group 'imap
:type 'boolean)
(defcustom imap-debug nil
- "If non-nil, random debug spews are placed in *imap-debug* buffer."
+ "If non-nil, random debug spews are placed in *imap-debug* buffer.
+Note that username, passwords and other privacy sensitive
+information (such as e-mail) may be stored in the *imap-debug*
+buffer. It is not written to disk, however. Do not enable this
+variable unless you are comfortable with that."
:group 'imap
:type 'boolean)
:type 'number
:group 'imap)
+(defcustom imap-store-password nil
+ "If non-nil, store session password without promting."
+ :group 'imap
+ :type 'boolean)
+
;; Various variables.
(defvar imap-fetch-data-hook nil
kerberos4
digest-md5
cram-md5
+ ;;sasl
login
anonymous)
"Priority of authenticators to consider when authenticating to server.")
(defvar imap-authenticator-alist
'((gssapi imap-gssapi-auth-p imap-gssapi-auth)
(kerberos4 imap-kerberos4-auth-p imap-kerberos4-auth)
+ (sasl imap-sasl-auth-p imap-sasl-auth)
(cram-md5 imap-cram-md5-p imap-cram-md5-auth)
(login imap-login-p imap-login-auth)
(anonymous imap-anonymous-p imap-anonymous-auth)
(defvar imap-error nil
"Error codes from the last command.")
-;; Internal constants. Change theese and die.
+;; Internal constants. Change these and die.
(defconst imap-default-port 143)
(defconst imap-default-ssl-port 993)
(while (and (memq (process-status process) '(open run))
(set-buffer buffer) ;; XXX "blue moon" nntp.el bug
(goto-char (point-min))
+ ;; Athena IMTEST can output SSL verify errors
+ (or (while (looking-at "^verify error:num=")
+ (forward-line))
+ t)
+ (or (while (looking-at "^TLS connection established")
+ (forward-line))
+ t)
;; cyrus 1.6.x (13? < x <= 22) queries capabilities
(or (while (looking-at "^C:")
(forward-line))
(or (not (looking-at "S: "))
(forward-char 3)
t)
+ ;; GNU SASL may print 'Trying ...' first.
+ (or (not (looking-at "Trying "))
+ (forward-line)
+ t)
(not (and (imap-parse-greeting)
;; success in imtest 1.6:
(re-search-forward
(let* ((port (or port imap-default-ssl-port))
(coding-system-for-read imap-coding-system-for-read)
(coding-system-for-write imap-coding-system-for-write)
- (process-connection-type nil)
+ (process-connection-type imap-process-connection-type)
+ (set-process-query-on-exit-flag
+ (if (fboundp 'set-process-query-on-exit-flag)
+ 'set-process-query-on-exit-flag
+ 'process-kill-without-query))
process)
(when (progn
(setq process (start-process
(format-spec-make
?s server
?p (number-to-string port)))))
- (process-kill-without-query process)
+ (funcall set-process-query-on-exit-flag process nil)
process)
(with-current-buffer buffer
(goto-char (point-min))
(progn
(setq ret t
imap-username user)
- (if (and (not imap-password)
- (y-or-n-p "Store password for this session? "))
- (setq imap-password passwd)))
+ (when (and (not imap-password)
+ (or imap-store-password
+ (y-or-n-p "Store password for this session? ")))
+ (setq imap-password passwd)))
(message "Login failed...")
(setq passwd nil)
(setq imap-password nil)
(and (not (imap-capability 'LOGINDISABLED buffer))
(not (imap-capability 'X-LOGIN-CMD-DISABLED buffer))))
+(defun imap-quote-specials (string)
+ (with-temp-buffer
+ (insert string)
+ (goto-char (point-min))
+ (while (re-search-forward "[\\\"]" nil t)
+ (forward-char -1)
+ (insert "\\")
+ (forward-char 1))
+ (buffer-string)))
+
(defun imap-login-auth (buffer)
"Login to server using the LOGIN command."
(message "imap: Plaintext authentication...")
(imap-interactive-login buffer
(lambda (user passwd)
(imap-ok-p (imap-send-command-wait
- (concat "LOGIN \"" user "\" \""
- passwd "\""))))))
+ (concat "LOGIN \""
+ (imap-quote-specials user)
+ "\" \""
+ (imap-quote-specials passwd)
+ "\""))))))
(defun imap-anonymous-p (buffer)
t)
(concat "LOGIN anonymous \"" (concat (user-login-name) "@"
(system-name)) "\"")))))
+;;; Compiler directives.
+
+(defvar imap-sasl-client)
+(defvar imap-sasl-step)
+
+(defun imap-sasl-make-mechanisms (buffer)
+ (let ((mecs '()))
+ (mapc (lambda (sym)
+ (let ((name (symbol-name sym)))
+ (if (and (> (length name) 5)
+ (string-equal "AUTH=" (substring name 0 5 )))
+ (setq mecs (cons (substring name 5) mecs)))))
+ (imap-capability nil buffer))
+ mecs))
+
+(defun imap-sasl-auth-p (buffer)
+ (and (condition-case ()
+ (require 'sasl)
+ (error nil))
+ (sasl-find-mechanism (imap-sasl-make-mechanisms buffer))))
+
+(defun imap-sasl-auth (buffer)
+ "Login to server using the SASL method."
+ (message "imap: Authenticating using SASL...")
+ (with-current-buffer buffer
+ (make-local-variable 'imap-username)
+ (make-local-variable 'imap-sasl-client)
+ (make-local-variable 'imap-sasl-step)
+ (let ((mechanism (sasl-find-mechanism (imap-sasl-make-mechanisms buffer)))
+ logged user)
+ (while (not logged)
+ (setq user (or imap-username
+ (read-from-minibuffer
+ (concat "IMAP username for " imap-server " using SASL "
+ (sasl-mechanism-name mechanism) ": ")
+ (or user imap-default-user))))
+ (when user
+ (setq imap-sasl-client (sasl-make-client mechanism user "imap2" imap-server)
+ imap-sasl-step (sasl-next-step imap-sasl-client nil))
+ (let ((tag (imap-send-command
+ (if (sasl-step-data imap-sasl-step)
+ (format "AUTHENTICATE %s %s"
+ (sasl-mechanism-name mechanism)
+ (sasl-step-data imap-sasl-step))
+ (format "AUTHENTICATE %s" (sasl-mechanism-name mechanism)))
+ buffer)))
+ (while (eq (imap-wait-for-tag tag) 'INCOMPLETE)
+ (sasl-step-set-data imap-sasl-step (base64-decode-string imap-continuation))
+ (setq imap-continuation nil
+ imap-sasl-step (sasl-next-step imap-sasl-client imap-sasl-step))
+ (imap-send-command-1 (if (sasl-step-data imap-sasl-step)
+ (base64-encode-string (sasl-step-data imap-sasl-step) t)
+ "")))
+ (if (imap-ok-p (imap-wait-for-tag tag))
+ (setq imap-username user
+ logged t)
+ (message "Login failed...")
+ (sit-for 1)))))
+ logged)))
+
(defun imap-digest-md5-p (buffer)
(and (imap-capability 'AUTH=DIGEST-MD5 buffer)
(condition-case ()
stream))
;; We're done, kill the first connection
(imap-close buffer)
- (kill-buffer buffer)
- (rename-buffer buffer)
+ (let ((name (if (stringp buffer)
+ buffer
+ (buffer-name buffer))))
+ (kill-buffer buffer)
+ (rename-buffer name))
(message "imap: Reconnecting with stream `%s'...done"
stream)
(setq imap-stream stream)
(with-current-buffer (or buffer (current-buffer))
(if (not (eq imap-state 'nonauth))
(or (eq imap-state 'auth)
- (eq imap-state 'select)
+ (eq imap-state 'selected)
(eq imap-state 'examine))
(make-local-variable 'imap-username)
(make-local-variable 'imap-password)
(defun imap-id (&optional list-of-values buffer)
"Identify client to server in BUFFER, and return server identity.
-LIST-OF-VALUES is `nil', or a plist with identifier and value
+LIST-OF-VALUES is nil, or a plist with identifier and value
strings to send to the server to identify the client.
Return a list of identifiers which server in BUFFER support, or
(defun imap-fetch (uids props &optional receive nouidfetch buffer)
"Fetch properties PROPS from message set UIDS from server in BUFFER.
UIDS can be a string, number or a list of numbers. If RECEIVE
-is non-nil return theese properties."
+is non-nil return these properties."
(with-current-buffer (or buffer (current-buffer))
(when (imap-ok-p (imap-send-command-wait
(format "%sFETCH %s %s" (if nouidfetch "" "UID ")
(when (eq (char-after) ?\))
(imap-forward)
(nreverse addresses)))
- (assert (imap-parse-nil) t "In imap-parse-address-list")))
+ ;; With assert, the code might not be eval'd.
+ ;; (assert (imap-parse-nil) t "In imap-parse-address-list")
+ (imap-parse-nil)))
;; mailbox = "INBOX" / astring
;; ; INBOX is case-insensitive. All case variants of
(defun imap-parse-flag-list ()
(let (flag-list start)
- (assert (eq (char-after) ?\() t "In imap-parse-flag-list")
+ (assert (eq (char-after) ?\() nil "In imap-parse-flag-list")
(while (and (not (eq (char-after) ?\)))
(setq start (progn
(imap-forward)
;; next line for Courier IMAP bug.
(skip-chars-forward " ")
(point)))
- (> (skip-chars-forward "^ )" (imap-point-at-eol)) 0))
+ (> (skip-chars-forward "^ )" (point-at-eol)) 0))
(push (buffer-substring start (point)) flag-list))
- (assert (eq (char-after) ?\)) t "In imap-parse-flag-list")
+ (assert (eq (char-after) ?\)) nil "In imap-parse-flag-list")
(imap-forward)
(nreverse flag-list)))
(while (eq (char-after) ?\ )
(imap-forward)
(push (imap-parse-body-extension) b-e))
- (assert (eq (char-after) ?\)) t "In imap-parse-body-extension")
+ (assert (eq (char-after) ?\)) nil "In imap-parse-body-extension")
(imap-forward)
(nreverse b-e))
(or (imap-parse-number)
(imap-forward)
(push (imap-parse-string-list) dsp)
(imap-forward))
- (assert (imap-parse-nil) t "In imap-parse-body-ext"))
+ ;; With assert, the code might not be eval'd.
+ ;; (assert (imap-parse-nil) t "In imap-parse-body-ext")
+ (imap-parse-nil))
(push (nreverse dsp) ext))
(when (eq (char-after) ?\ ) ;; body-fld-lang
(imap-forward)
(push (and (imap-parse-nil) nil) body))
(setq body
(append (imap-parse-body-ext) body))) ;; body-ext-...
- (assert (eq (char-after) ?\)) t "In imap-parse-body")
+ (assert (eq (char-after) ?\)) nil "In imap-parse-body")
(imap-forward)
(nreverse body))
(push (imap-parse-nstring) body) ;; body-fld-md5
(setq body (append (imap-parse-body-ext) body))) ;; body-ext-1part..
- (assert (eq (char-after) ?\)) t "In imap-parse-body 2")
+ (assert (eq (char-after) ?\)) nil "In imap-parse-body 2")
(imap-forward)
(nreverse body)))))
(provide 'imap)
+;;; arch-tag: 27369ed6-33e4-482f-96f1-8bb906ba70f7
;;; imap.el ends here