Indent.
[gnus] / lisp / imap.el
index 923880d..4413fb5 100644 (file)
@@ -1,5 +1,5 @@
 ;;; imap.el --- imap library
-;; Copyright (C) 1998, 1999, 2000, 2001
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002
 ;;        Free Software Foundation, Inc.
 
 ;; Author: Simon Josefsson <jas@pdc.kth.se>
@@ -390,7 +390,7 @@ human readable response text (a string).")
 
 (defvar imap-continuation nil
   "Non-nil indicates that the server emitted a continuation request.
-The actually value is really the text on the continuation line.")
+The actual value is really the text on the continuation line.")
 
 (defvar imap-callbacks nil
   "List of response tags and callbacks, on the form `(number . function)'.
@@ -497,11 +497,11 @@ If ARGS, PROMPT is used as an argument to `format'."
            (while (and (memq (process-status process) '(open run))
                        (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
                        (goto-char (point-min))
-                  ;; cyrus 1.6.x (13? < x <= 22) queries capabilities
+                       ;; cyrus 1.6.x (13? < x <= 22) queries capabilities
                        (or (while (looking-at "^C:")
                              (forward-line))
                            t)
-               ;; cyrus 1.6 imtest print "S: " before server greeting
+                       ;; cyrus 1.6 imtest print "S: " before server greeting
                        (or (not (looking-at "S: "))
                            (forward-char 3)
                            t)
@@ -528,7 +528,7 @@ If ARGS, PROMPT is used as an argument to `format'."
                                (not (string-match "failed" response))))
                (setq done process)
              (if (memq (process-status process) '(open run))
-                 (imap-send-command-wait "LOGOUT"))
+                 (imap-send-command "LOGOUT"))
              (delete-process process)
              nil)))))
     done))
@@ -561,11 +561,11 @@ If ARGS, PROMPT is used as an argument to `format'."
            (while (and (memq (process-status process) '(open run))
                        (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
                        (goto-char (point-min))
-                  ;; cyrus 1.6.x (13? < x <= 22) queries capabilities
+                       ;; cyrus 1.6.x (13? < x <= 22) queries capabilities
                        (or (while (looking-at "^C:")
                              (forward-line))
                            t)
-               ;; cyrus 1.6 imtest print "S: " before server greeting
+                       ;; cyrus 1.6 imtest print "S: " before server greeting
                        (or (not (looking-at "S: "))
                            (forward-char 3)
                            t)
@@ -588,7 +588,7 @@ If ARGS, PROMPT is used as an argument to `format'."
                                (not (string-match "failed" response))))
                (setq done process)
              (if (memq (process-status process) '(open run))
-                 (imap-send-command-wait "LOGOUT"))
+                 (imap-send-command "LOGOUT"))
              (delete-process process)
              nil)))))
     done))
@@ -601,7 +601,9 @@ If ARGS, PROMPT is used as an argument to `format'."
   (let ((cmds (if (listp imap-ssl-program) imap-ssl-program
                (list imap-ssl-program)))
        cmd done)
-    (ignore-errors (require 'ssl))
+    (condition-case ()
+       (require 'ssl)
+      (error))
     (while (and (not done) (setq cmd (pop cmds)))
       (message "imap: Opening SSL connection with `%s'..." cmd)
       (let* ((port (or port imap-default-ssl-port))
@@ -614,8 +616,9 @@ If ARGS, PROMPT is used as an argument to `format'."
                                      ?s server
                                      ?p (number-to-string port)))))
             process)
-       (when (setq process (ignore-errors (open-ssl-stream
-                                           name buffer server port)))
+       (when (setq process (condition-case ()
+                               (open-ssl-stream name buffer server port)
+                             (error)))
          (with-current-buffer buffer
            (goto-char (point-min))
            (while (and (memq (process-status process) '(open run))
@@ -669,7 +672,8 @@ If ARGS, PROMPT is used as an argument to `format'."
   nil)
 
 (defun imap-shell-open (name buffer server port)
-  (let ((cmds imap-shell-program)
+  (let ((cmds (if (listp imap-shell-program) imap-shell-program
+               (list imap-shell-program)))
        cmd done)
     (while (and (not done) (setq cmd (pop cmds)))
       (message "imap: Opening IMAP connection with `%s'..." cmd)
@@ -689,7 +693,8 @@ If ARGS, PROMPT is used as an argument to `format'."
        (when process
          (while (and (memq (process-status process) '(open run))
                      (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
-                     (goto-char (point-min))
+                     (goto-char (point-max))
+                     (forward-line -1)
                      (not (imap-parse-greeting)))
            (accept-process-output process 1)
            (sit-for 1))
@@ -710,12 +715,7 @@ If ARGS, PROMPT is used as an argument to `format'."
       nil)))
 
 (defun imap-starttls-p (buffer)
-  (and (imap-capability 'STARTTLS buffer)
-       (condition-case ()
-          (progn
-            (require 'starttls)
-            (call-process "starttls"))
-        (error nil))))
+  (imap-capability 'STARTTLS buffer))
 
 (defun imap-starttls-open (name buffer server port)
   (let* ((port (or port imap-default-port))
@@ -758,7 +758,7 @@ If ARGS, PROMPT is used as an argument to `format'."
 (defun imap-interactive-login (buffer loginfunc)
   "Login to server in BUFFER.
 LOGINFUNC is passed a username and a password, it should return t if
-it where sucessful authenticating itself to the server, nil otherwise.
+it where successful authenticating itself to the server, nil otherwise.
 Returns t if login was successful, nil otherwise."
   (with-current-buffer buffer
     (make-local-variable 'imap-username)
@@ -768,12 +768,15 @@ Returns t if login was successful, nil otherwise."
       (while (or (not user) (not passwd))
        (setq user (or imap-username
                       (read-from-minibuffer
-                       (concat "IMAP username for " imap-server ": ")
+                       (concat "IMAP username for " imap-server
+                               " (using stream `" (symbol-name imap-stream)
+                               "'): ")
                        (or user imap-default-user))))
        (setq passwd (or imap-password
                         (imap-read-passwd
                          (concat "IMAP password for " user "@"
-                                 imap-server ": "))))
+                                 imap-server " (using authenticator `"
+                                 (symbol-name imap-auth) "'): "))))
        (when (and user passwd)
          (if (funcall loginfunc user passwd)
              (progn
@@ -795,13 +798,7 @@ Returns t if login was successful, nil otherwise."
 
 (defun imap-gssapi-auth-p (buffer)
   (and (imap-capability 'AUTH=GSSAPI buffer)
-       (catch 'imtest-found
-        (let (prg (prgs imap-gssapi-program))
-          (while (setq prg (pop prgs))
-            (condition-case ()
-                (and (call-process (substring prg 0 (string-match " " prg)))
-                     (throw 'imtest-found t))
-              (error nil)))))))
+       (eq imap-stream 'gssapi)))
 
 (defun imap-gssapi-auth (buffer)
   (message "imap: Authenticating using GSSAPI...%s"
@@ -810,13 +807,7 @@ Returns t if login was successful, nil otherwise."
 
 (defun imap-kerberos4-auth-p (buffer)
   (and (imap-capability 'AUTH=KERBEROS_V4 buffer)
-       (catch 'imtest-found
-        (let (prg (prgs imap-kerberos4-program))
-          (while (setq prg (pop prgs))
-            (condition-case ()
-                (and (call-process (substring prg 0 (string-match " " prg)))
-                     (throw 'imtest-found t))
-              (error nil)))))))
+       (eq imap-stream 'kerberos4)))
 
 (defun imap-kerberos4-auth (buffer)
   (message "imap: Authenticating using Kerberos 4...%s"
@@ -863,7 +854,7 @@ Returns t if login was successful, nil otherwise."
   t)
 
 (defun imap-anonymous-auth (buffer)
-  (message "imap: Loging in anonymously...")
+  (message "imap: Logging in anonymously...")
   (with-current-buffer buffer
     (imap-ok-p (imap-send-command-wait
                (concat "LOGIN anonymous \"" (concat (user-login-name) "@"
@@ -936,7 +927,7 @@ AUTH indicates authenticator to use, see `imap-authenticators' for
 available authenticators.  If nil, it choices the best stream the
 server is capable of.
 BUFFER can be a buffer or a name of a buffer, which is created if
-necessery.  If nil, the buffer name is generated."
+necessary.  If nil, the buffer name is generated."
   (setq buffer (or buffer (format " *imap* %s:%d" server (or port 0))))
   (with-current-buffer (get-buffer-create buffer)
     (if (imap-opened buffer)
@@ -949,46 +940,53 @@ necessery.  If nil, the buffer name is generated."
     (setq imap-auth (or auth imap-auth))
     (setq imap-stream (or stream imap-stream))
     (message "imap: Connecting to %s..." imap-server)
-    (if (let ((imap-stream (or imap-stream imap-default-stream)))
-         (imap-open-1 buffer))
-       ;; Choose stream.
-       (let (stream-changed)
-         (message "imap: Connecting to %s...done" imap-server)
-         (when (null imap-stream)
-           (let ((streams imap-streams))
-             (while (setq stream (pop streams))
-               (if (funcall (nth 1 (assq stream imap-stream-alist)) buffer)
-                   (setq stream-changed (not (eq (or imap-stream
-                                                     imap-default-stream)
-                                                 stream))
-                         imap-stream stream
-                         streams nil)))
-             (unless imap-stream
-               (error "Couldn't figure out a stream for server"))))
-         (when stream-changed
-           (message "imap: Reconnecting with stream `%s'..." imap-stream)
-           (imap-close buffer)
-           (if (imap-open-1 buffer)
-               (message "imap: Reconnecting with stream `%s'...done"
-                        imap-stream)
-             (message "imap: Reconnecting with stream `%s'...failed"
-                      imap-stream))
-           (setq imap-capability nil))
-         (if (imap-opened buffer)
-             ;; Choose authenticator
-             (when (and (null imap-auth) (not (eq imap-state 'auth)))
-               (let ((auths imap-authenticators))
-                 (while (setq auth (pop auths))
-                   (if (funcall (nth 1 (assq auth imap-authenticator-alist))
-                                buffer)
-                       (setq imap-auth auth
-                             auths nil)))
-                 (unless imap-auth
-                   (error "Couldn't figure out authenticator for server"))))))
-      (message "imap: Connecting to %s...failed" imap-server))
-    (when (imap-opened buffer)
-      (setq imap-mailbox-data (make-vector imap-mailbox-prime 0))
-      buffer)))
+    (if (null (let ((imap-stream (or imap-stream imap-default-stream)))
+               (imap-open-1 buffer)))
+       (progn
+         (message "imap: Connecting to %s...failed" imap-server)
+         nil)
+      (when (null imap-stream)
+       ;; Need to choose stream.
+       (let ((streams imap-streams))
+         (while (setq stream (pop streams))
+           ;; OK to use this stream?
+           (when (funcall (nth 1 (assq stream imap-stream-alist)) buffer)
+             ;; Stream changed?
+             (if (not (eq imap-default-stream stream))
+                 (with-current-buffer (get-buffer-create
+                                       (generate-new-buffer-name " *temp*"))
+                   (mapcar 'make-local-variable imap-local-variables)
+                   (imap-disable-multibyte)
+                   (buffer-disable-undo)
+                   (setq imap-server (or server imap-server))
+                   (setq imap-port (or port imap-port))
+                   (setq imap-auth (or auth imap-auth))
+                   (message "imap: Reconnecting with stream `%s'..." stream)
+                   (if (null (let ((imap-stream stream))
+                               (imap-open-1 (current-buffer))))
+                       (progn
+                         (kill-buffer (current-buffer))
+                         (message
+                          "imap: Reconnecting with stream `%s'...failed"
+                          stream))
+                     ;; We're done, kill the first connection
+                     (imap-close buffer)
+                     (kill-buffer buffer)
+                     (rename-buffer buffer)
+                     (message "imap: Reconnecting with stream `%s'...done"
+                              stream)
+                     (setq imap-stream stream)
+                     (setq imap-capability nil)
+                     (setq streams nil)))
+               ;; We're done
+               (message "imap: Connecting to %s...done" imap-server)
+               (setq imap-stream stream)
+               (setq imap-capability nil)
+               (setq streams nil))))))
+      (when (imap-opened buffer)
+       (setq imap-mailbox-data (make-vector imap-mailbox-prime 0)))
+      (when imap-stream
+       buffer))))
 
 (defun imap-opened (&optional buffer)
   "Return non-nil if connection to imap server in BUFFER is open.
@@ -1015,15 +1013,36 @@ password is remembered in the buffer."
       (make-local-variable 'imap-password)
       (if user (setq imap-username user))
       (if passwd (setq imap-password passwd))
-      (if (funcall (nth 2 (assq imap-auth imap-authenticator-alist)) buffer)
-         (setq imap-state 'auth)))))
+      (if imap-auth
+         (and (funcall (nth 2 (assq imap-auth
+                                    imap-authenticator-alist)) buffer)
+              (setq imap-state 'auth))
+       ;; Choose authenticator.
+       (let ((auths imap-authenticators)
+             auth)
+         (while (setq auth (pop auths))
+           ;; OK to use authenticator?
+           (when (funcall (nth 1 (assq auth imap-authenticator-alist)) buffer)
+             (message "imap: Authenticating to `%s' using `%s'..."
+                      imap-server auth)
+             (setq imap-auth auth)
+             (if (funcall (nth 2 (assq auth imap-authenticator-alist)) buffer)
+                 (progn
+                   (message "imap: Authenticating to `%s' using `%s'...done"
+                            imap-server auth)
+                   (setq auths nil))
+               (message "imap: Authenticating to `%s' using `%s'...failed"
+                        imap-server auth)))))
+       imap-state))))
 
 (defun imap-close (&optional buffer)
   "Close connection to server in BUFFER.
 If BUFFER is nil, the current buffer is used."
   (with-current-buffer (or buffer (current-buffer))
     (when (imap-opened)
-      (imap-send-command-wait "LOGOUT"))
+      (condition-case nil
+         (imap-send-command-wait "LOGOUT")
+       (quit nil)))
     (when (and imap-process
               (memq (process-status imap-process) '(open run)))
       (delete-process imap-process))
@@ -1301,6 +1320,20 @@ returned, if ITEMS is a symbol only it's value is returned."
                  items)
        (imap-mailbox-get items mailbox)))))
 
+(defun imap-mailbox-status-asynch (mailbox items &optional buffer)
+  "Send status item request ITEM on MAILBOX to server in BUFFER.
+ITEMS can be a symbol or a list of symbols, valid symbols are one of
+the STATUS data items -- ie 'messages, 'recent, 'uidnext, 'uidvalidity
+or 'unseen.  The IMAP command tag is returned."
+  (with-current-buffer (or buffer (current-buffer))
+    (imap-send-command (list "STATUS \""
+                            (imap-utf7-encode mailbox)
+                            "\" "
+                            (format "%s"
+                                    (if (listp items)
+                                        items
+                                      (list items)))))))
+
 (defun imap-mailbox-acl-get (&optional mailbox buffer)
   "Get ACL on mailbox from server in BUFFER."
   (let ((mailbox (imap-utf7-encode mailbox)))
@@ -1836,21 +1869,21 @@ Return nil if no complete line has arrived."
 ;;
 ;;   addr-adl        = nstring
 ;;                       ; Holds route from [RFC-822] route-addr if
-;;                       ; non-NIL
+;;                       ; non-nil
 ;;
 ;;   addr-host       = nstring
-;;                       ; NIL indicates [RFC-822] group syntax.
+;;                       ; nil indicates [RFC-822] group syntax.
 ;;                       ; Otherwise, holds [RFC-822] domain name
 ;;
 ;;   addr-mailbox    = nstring
-;;                       ; NIL indicates end of [RFC-822] group; if
-;;                       ; non-NIL and addr-host is NIL, holds
+;;                       ; nil indicates end of [RFC-822] group; if
+;;                       ; non-nil and addr-host is nil, holds
 ;;                       ; [RFC-822] group name.
 ;;                       ; Otherwise, holds [RFC-822] local-part
 ;;                       ; after removing [RFC-822] quoting
 ;;
 ;;   addr-name       = nstring
-;;                       ; If non-NIL, holds phrase from [RFC-822]
+;;                       ; If non-nil, holds phrase from [RFC-822]
 ;;                       ; mailbox after removing [RFC-822] quoting
 ;;
 
@@ -1938,7 +1971,7 @@ Return nil if no complete line has arrived."
 ;;   resp-cond-bye   = "BYE" SP resp-text
 ;;
 ;;   mailbox-data    =  "FLAGS" SP flag-list /
-;;                     "LIST" SP mailbox-list /
+;;                     "LIST" SP mailbox-list /
 ;;                      "LSUB" SP mailbox-list /
 ;;                     "SEARCH" *(SP nz-number) /
 ;;                      "STATUS" SP mailbox SP "("
@@ -2043,7 +2076,7 @@ Return nil if no complete line has arrived."
 ;;                               [flag-perm *(SP flag-perm)] ")" /
 ;;                     "READ-ONLY" /
 ;;                    "READ-WRITE" /
-;;                    "TRYCREATE" /
+;;                    "TRYCREATE" /
 ;;                     "UIDNEXT" SP nz-number /
 ;;                    "UIDVALIDITY" SP nz-number /
 ;;                     "UNSEEN" SP nz-number /
@@ -2097,10 +2130,10 @@ Return nil if no complete line has arrived."
     (imap-forward)
     (cond ((search-forward "PERMANENTFLAGS " nil t)
           (imap-mailbox-put 'permanentflags (imap-parse-flag-list)))
-         ((search-forward "UIDNEXT " nil t)
-          (imap-mailbox-put 'uidnext (read (current-buffer))))
+         ((search-forward "UIDNEXT \\([0-9]+\\)" nil t)
+          (imap-mailbox-put 'uidnext (match-string 1)))
          ((search-forward "UNSEEN " nil t)
-          (imap-mailbox-put 'unseen (read (current-buffer))))
+          (imap-mailbox-put 'first-unseen (read (current-buffer))))
          ((looking-at "UIDVALIDITY \\([0-9]+\\)")
           (imap-mailbox-put 'uidvalidity (match-string 1)))
          ((search-forward "READ-ONLY" nil t)
@@ -2199,15 +2232,19 @@ Return nil if no complete line has arrived."
 (defun imap-parse-fetch (response)
   (when (eq (char-after) ?\()
     (let (uid flags envelope internaldate rfc822 rfc822header rfc822text
-             rfc822size body bodydetail bodystructure)
+             rfc822size body bodydetail bodystructure flags-empty)
       (while (not (eq (char-after) ?\)))
        (imap-forward)
        (let ((token (read (current-buffer))))
          (imap-forward)
          (cond ((eq token 'UID)
-                (setq uid (ignore-errors (read (current-buffer)))))
+                (setq uid (condition-case ()
+                              (read (current-buffer))
+                            (error))))
                ((eq token 'FLAGS)
-                (setq flags (imap-parse-flag-list)))
+                (setq flags (imap-parse-flag-list))
+                (if (not flags)
+                    (setq flags-empty 't)))
                ((eq token 'ENVELOPE)
                 (setq envelope (imap-parse-envelope)))
                ((eq token 'INTERNALDATE)
@@ -2236,7 +2273,7 @@ Return nil if no complete line has arrived."
       (when uid
        (setq imap-current-message uid)
        (imap-message-put uid 'UID uid)
-       (and flags (imap-message-put uid 'FLAGS flags))
+       (and (or flags flags-empty) (imap-message-put uid 'FLAGS flags))
        (and envelope (imap-message-put uid 'ENVELOPE envelope))
        (and internaldate (imap-message-put uid 'INTERNALDATE internaldate))
        (and rfc822 (imap-message-put uid 'RFC822 rfc822))
@@ -2259,24 +2296,32 @@ Return nil if no complete line has arrived."
 
 (defun imap-parse-status ()
   (let ((mailbox (imap-parse-mailbox)))
-    (when (and mailbox (search-forward "(" nil t))
-      (while (not (eq (char-after) ?\)))
-       (let ((token (read (current-buffer))))
-         (cond ((eq token 'MESSAGES)
+    (if (eq (char-after) ? )
+       (forward-char))
+    (when (and mailbox (eq (char-after) ?\())
+      (while (and (not (eq (char-after) ?\)))
+                 (or (forward-char) t)
+                 (looking-at "\\([A-Za-z]+\\) "))
+       (let ((token (match-string 1)))
+         (goto-char (match-end 0))
+         (cond ((string= token "MESSAGES")
                 (imap-mailbox-put 'messages (read (current-buffer)) mailbox))
-               ((eq token 'RECENT)
+               ((string= token "RECENT")
                 (imap-mailbox-put 'recent (read (current-buffer)) mailbox))
-               ((eq token 'UIDNEXT)
-                (imap-mailbox-put 'uidnext (read (current-buffer)) mailbox))
-               ((eq token 'UIDVALIDITY)
-                (and (looking-at " \\([0-9]+\\)")
-                     (imap-mailbox-put 'uidvalidity (match-string 1) mailbox)
-                     (goto-char (match-end 1))))
-               ((eq token 'UNSEEN)
+               ((string= token "UIDNEXT")
+                (and (looking-at "[0-9]+")
+                     (imap-mailbox-put 'uidnext (match-string 0) mailbox)
+                     (goto-char (match-end 0))))
+               ((string= token "UIDVALIDITY")
+                (and (looking-at "[0-9]+")
+                     (imap-mailbox-put 'uidvalidity (match-string 0) mailbox)
+                     (goto-char (match-end 0))))
+               ((string= token "UNSEEN")
                 (imap-mailbox-put 'unseen (read (current-buffer)) mailbox))
                (t
                 (message "Unknown status data %s in mailbox %s ignored"
-                         token mailbox))))))))
+                         token mailbox)
+                (read (current-buffer)))))))))
 
 ;;   acl_data        ::= "ACL" SPACE mailbox *(SPACE identifier SPACE
 ;;                        rights)
@@ -2551,7 +2596,7 @@ Return nil if no complete line has arrived."
        (push (imap-parse-nstring) body) ;; body-fld-desc
        (imap-forward)
        ;; next `or' for Sun SIMS bug, it regard body-fld-enc as a
-       ;; nstring and return NIL instead of defaulting back to 7BIT
+       ;; nstring and return nil instead of defaulting back to 7BIT
        ;; as the standard says.
        (push (or (imap-parse-nstring) "7BIT") body) ;; body-fld-enc
        (imap-forward)