Implement handling of flags, expunging, and deletion/reloading of
authorChris Hanson <org/chris-hanson/cph>
Thu, 4 May 2000 22:21:27 +0000 (22:21 +0000)
committerChris Hanson <org/chris-hanson/cph>
Thu, 4 May 2000 22:21:27 +0000 (22:21 +0000)
cache.

v7/src/imail/imail-imap.scm

index d62cb7c5e21830022322bfaddd98a06aff2b4fb9..546ec8c180bdae6e59e15b645f6f54846c57c371 100644 (file)
@@ -1,6 +1,6 @@
 ;;; -*-Scheme-*-
 ;;;
-;;; $Id: imail-imap.scm,v 1.16 2000/05/04 17:40:04 cph Exp $
+;;; $Id: imail-imap.scm,v 1.17 2000/05/04 22:21:27 cph Exp $
 ;;;
 ;;; Copyright (c) 1999-2000 Massachusetts Institute of Technology
 ;;;
 (define memoized-imap-connections '())
 
 (define (guarantee-imap-connection-open connection)
-  (if (not (imap-connection-port connection))
+  (if (imap-connection-port connection)
+      #f
       (let ((host (imap-connection-host connection))
            (ip-port (imap-connection-ip-port connection))
            (user-id (imap-connection-user-id connection)))
          (if (not (memq 'IMAP4REV1 (imap:command:capability connection)))
              (begin
                (close-imap-connection connection)
-               (error "Server doesn't support IMAP4rev1:" host)))))))
+               (error "Server doesn't support IMAP4rev1:" host))))
+       #t)))
 
 (define (close-imap-connection connection)
   (let ((port (imap-connection-port connection)))
   (connection define accessor)
   (allowed-flags define standard)
   (permanent-flags define standard)
-  (uidvalidity define standard
-              initial-value #f)
-  (first-unseen define standard
-               initial-value #f)
-  (messages define standard
-           initializer (lambda () (make-vector 0))))
+  (permanent-keywords? define standard)
+  (uidvalidity define standard)
+  (first-unseen define standard)
+  (messages define standard initial-value '#()))
 
 (define-class <imap-message> (<message>)
   (uid define accessor)
       (constructor 'UNCACHED 'UNCACHED (map imap-flag->imail-flag flags)
                   '() uid length envelope))))
 
-(define (imap-flag->imail-flag flag)
-  (let ((s (symbol->string flag)))
-    (if (string-prefix? "\\" s)
-       (string-tail s 1)
-       s)))
-
-(define (imail-flag->imap-flag flag folder)
-  (intern
-   (if (flags-member? flag (imap-folder-allowed-flags folder))
-       (string-append "\\" flag)
-       flag)))
-
 (let ((demand-loader
-       (lambda (generic slot-name item-name transform)
+       (lambda (generic slot-name item-name noun transform)
         (let ((modifier (slot-modifier <imap-message> slot-name)))
           (define-method generic ((message <imap-message>))
             (if (eq? 'UNCACHED (call-next-method message))
                  (transform
                   (translate-string-line-endings
                    (car
-                    (imap:command:uid-fetch (imap-folder-connection
-                                             (message-folder message))
-                                            (imap-message-uid message)
-                                            (list item-name)))))))
+                    (let ((index (message-index message)))
+                      ((imail-message-wrapper "Reading " noun
+                                              " for message "
+                                              (number->string (+ index 1)))
+                       (lambda ()
+                         (imap:command:fetch (imap-folder-connection
+                                              (message-folder message))
+                                             index
+                                             (list item-name))))))))))
             (call-next-method message))))))
-  (demand-loader message-header-fields 'HEADER-FIELDS 'RFC822.HEADER
+  (demand-loader message-header-fields 'HEADER-FIELDS 'RFC822.HEADER "headers"
                 (lambda (string)
                   (lines->header-fields
                    (except-last-pair! (string->lines string)))))
-  (demand-loader message-body 'BODY 'RFC822.TEXT identity-procedure))
-
+  (demand-loader message-body 'BODY 'RFC822.TEXT "body" identity-procedure))
+\f
 (define-method set-message-flags! ((message <imap-message>) flags)
-  ;; **** synchronize here.
-  ???
-  (call-next-method message flags))
+  (call-next-method message flags)
+  (let ((old-flags (message-flags message))
+       (folder (message-folder message))
+       (index (message-index message)))
+    (let ((connection (imap-folder-connection folder))
+         (diff
+          (lambda (f1 f2)
+            (map imail-flag->imap-flag
+                 (list-transform-positive (flags-difference f1 f2)
+                   (let ((flags (imap-folder-permanent-flags folder))
+                         (keywords? (imap-folder-permanent-keywords? folder)))
+                     (lambda (flag)
+                       (if (string-prefix? "\\" flag)
+                           (flags-member? flag flags)
+                           keywords?))))))))
+      (imap:command:store-flags+ connection index (diff flags old-flags))
+      (imap:command:store-flags- connection index (diff old-flags flags)))))
+
+(define (flags-difference f1 f2)
+  (if (pair? f1)
+      (if (flags-member? (car f1) f2)
+         (flags-difference (cdr f1) f2)
+         (cons (car f1) (flags-difference (cdr f1) f2)))
+      '()))
+
+(define (imap-flag->imail-flag flag)
+  (case flag
+    ((\ANSWERED) "answered")
+    ((\DELETED) "deleted")
+    ((\SEEN) "seen")
+    (else (symbol->string flag))))
+
+(define (imail-flag->imap-flag flag)
+  (cond ((string-ci=? flag "answered") '\ANSWERED)
+       ((string-ci=? flag "deleted") '\DELETED)
+       ((string-ci=? flag "seen") '\SEEN)
+       (else (intern flag))))
 \f
+(define (reset-imap-folder! folder)
+  (without-interrupts
+   (lambda ()
+     (for-each-vector-element (imap-folder-messages folder) detach-message)
+     (set-imap-folder-allowed-flags! folder '())
+     (set-imap-folder-permanent-flags! folder '())
+     (set-imap-folder-permanent-keywords?! folder #f)
+     (set-imap-folder-uidvalidity! folder #f)
+     (set-imap-folder-first-unseen! folder #f)
+     (set-imap-folder-messages! folder '#()))))
+
 (define (set-imap-folder-length! folder count)
-  (let ((v (imap-folder-messages folder))
-       (connection (imap-folder-connection folder)))
+  (let ((v (imap-folder-messages folder)))
     (let ((v* (vector-grow v count #f)))
       (fill-messages-vector folder v* (vector-length v))
       (set-imap-folder-messages! folder v*)))
   (let ((connection (imap-folder-connection folder))
        (end (vector-length messages)))
     (do ((responses
-         (imap:command:fetch-range connection 0 end
-                                   '(UID FLAGS RFC822.SIZE ENVELOPE))
+         ((imail-message-wrapper "Reading message outlines")
+          (lambda ()
+            (imap:command:fetch-range connection 0 end
+                                      '(UID FLAGS RFC822.SIZE ENVELOPE))))
          (cdr responses))
         (index start (fix:+ index 1)))
        ((fix:= index end))
 ;;;; Folder operations
 
 (define-method %open-folder ((url <imap-url>))
-  (let ((connection (get-imap-connection url)))
-    (let ((folder (make-imap-folder url connection)))
-      (select-imap-folder connection folder)
-      (if (not (imap:command:select connection (imap-url-mailbox url)))
-         (select-imap-folder connection #f))
-      folder)))
+  (let ((folder (make-imap-folder url (get-imap-connection url))))
+    (guarantee-imap-folder-open folder)
+    folder))
+
+(define (guarantee-imap-folder-open folder)
+  (let ((connection (imap-folder-connection folder)))
+    (and (guarantee-imap-connection-open connection)
+        (begin
+          (reset-imap-folder! folder)
+          (select-imap-folder connection folder)
+          (if (not
+               (imap:command:select connection
+                                    (imap-url-mailbox (folder-url folder))))
+              (select-imap-folder connection #f))
+          #t))))
 
 (define-method close-folder ((folder <imap-folder>))
-  (close-imap-connection (imap-folder-connection folder)))
+  (close-imap-connection (imap-folder-connection folder))
+  (reset-imap-folder! folder))
 
 (define-method folder-presentation-name ((folder <imap-folder>))
   (imap-url-mailbox (folder-url folder)))
   #t)
 
 (define-method folder-length ((folder <imap-folder>))
+  (guarantee-imap-folder-open folder)
   (vector-length (imap-folder-messages folder)))
 
 (define-method %get-message ((folder <imap-folder>) index)
-  (let ((messages (imap-folder-messages folder)))
-    (or (vector-ref messages index)
-       (let ((message
-              (apply make-imap-message
-                     (imap:command:fetch (imap-folder-connection folder)
-                                         index
-                                         '(UID FLAGS RFC822.SIZE
-                                               ENVELOPE)))))
-         (vector-set! messages index message)
-         (set-message-index! message index)
-         message))))
+  (guarantee-imap-folder-open folder)
+  (vector-ref (imap-folder-messages folder) index))
 
 (define-method first-unseen-message ((folder <imap-folder>))
+  (guarantee-imap-folder-open folder)
   (let ((unseen (imap-folder-first-unseen folder)))
     (and unseen
         (get-message folder unseen))))
 
 (define-method append-message ((folder <imap-folder>) (message <message>))
+  (guarantee-imap-folder-open folder)
   ???)
 
 (define-method expunge-deleted-messages ((folder <imap-folder>))
-  ???)
+  (guarantee-imap-folder-open folder)
+  (imap:command:expunge (imap-folder-connection folder)))
 
 (define-method search-folder ((folder <imap-folder>) criteria)
+  (guarantee-imap-folder-open folder)
   ???)
 
 (define-method folder-sync-status ((folder <imap-folder>))
   unspecific)
 
 (define-method discard-folder-cache ((folder <imap-folder>))
-  (close-imap-connection (imap-folder-connection folder)))
+  (close-imap-connection (imap-folder-connection folder))
+  (reset-imap-folder! folder))
 \f
 ;;;; IMAP command invocation
 
                                 connection 'CAPABILITY)))
 
 (define (imap:command:login connection user-id passphrase)
-  (imap:command:no-response connection 'LOGIN user-id passphrase))
+  ((imail-message-wrapper "Logging in as " user-id)
+   (lambda ()
+     (imap:command:no-response connection 'LOGIN user-id passphrase))))
 
 (define (imap:command:select connection mailbox)
-  (imap:response:ok? (imap:command:no-response connection 'SELECT mailbox)))
+  ((imail-message-wrapper "Select mailbox " mailbox)
+   (lambda ()
+     (imap:response:ok?
+      (imap:command:no-response connection 'SELECT mailbox)))))
 
 (define (imap:command:fetch connection index items)
   (let ((response
           (imap:response:fetch-attribute response item))
         items)))
 
+(define (imap:command:store-flags+ connection index flags)
+  (if (pair? flags)
+      (imap:command:no-response connection 'STORE index '+FLAGS.SILENT flags)))
+
+(define (imap:command:store-flags- connection index flags)
+  (if (pair? flags)
+      (imap:command:no-response connection 'STORE index '-FLAGS.SILENT flags)))
+
+(define (imap:command:expunge connection)
+  ((imail-message-wrapper "Expunging messages")
+   (lambda ()
+     (imap:command:no-response connection 'EXPUNGE))))
+
 (define (imap:command:noop connection)
   (imap:command:no-response connection 'NOOP))
 \f
            (- (imap:response-code:unseen code) 1))
           (folder-modified! folder)))
        ((imap:response-code:permanentflags? code)
-        (let ((folder (selected-imap-folder connection)))
+        (let ((pflags (imap:response-code:permanentflags code))
+              (folder (selected-imap-folder connection)))
+          (set-imap-folder-permanent-keywords?!
+           folder
+           (if (memq '\* pflags) #t #f))
           (set-imap-folder-permanent-flags!
            folder
-           (map (lambda (flag)
-                  (if (eq? '\* flag)
-                      'USER-DEFINED
-                      (imap-flag->imail-flag flag)))
-                (imap:response-code:permanentflags code)))
+           (map imap-flag->imail-flag (delq '\* pflags)))
           (folder-modified! folder)))
        ((imap:response-code:alert? code)
         (imail-present-user-alert