;;; -*-Scheme-*-
;;;
-;;; $Id: imail-file.scm,v 1.64 2001/05/13 03:45:52 cph Exp $
+;;; $Id: imail-file.scm,v 1.65 2001/05/15 19:46:51 cph Exp $
;;;
;;; Copyright (c) 1999-2001 Massachusetts Institute of Technology
;;;
(define pathname-url-constructors
(make-eq-hash-table))
-(define-method url-body ((url <pathname-url>))
- (pathname->url-body (pathname-url-pathname url)))
-
-(define (pathname->url-body pathname)
- (string-append (let ((device (pathname-device pathname)))
- (if (string? device)
- (string-append "/" device ":")
- ""))
- (let ((directory (pathname-directory pathname)))
- (if (pair? directory)
- (string-append
- (if (eq? (car directory) 'ABSOLUTE) "/" "")
- (decorated-string-append
- "" "" "/"
- (map (lambda (string)
- (url:encode-string
- (if (eq? string 'UP) ".." string)))
- (cdr directory))))
- ""))
- (url:encode-string (file-namestring pathname))))
-
(define-method url-container ((url <pathname-url>))
(make-directory-url
(directory-pathname
(directory-pathname-as-file (pathname-url-pathname url)))))
-\f
-(define (define-pathname-url-predicate class predicate)
+
+(define (define-pathname-url-predicates class
+ file-predicate
+ directory-predicate
+ pathname-predicate)
(let ((constructor (get-pathname-url-constructor class)))
(let loop ((entries pathname-url-predicates))
(if (pair? entries)
(if (eq? class (vector-ref (car entries) 0))
(begin
- (vector-set! (car entries) 1 predicate)
- (vector-set! (car entries) 2 constructor))
+ (vector-set! (car entries) 1 file-predicate)
+ (vector-set! (car entries) 2 directory-predicate)
+ (vector-set! (car entries) 3 pathname-predicate)
+ (vector-set! (car entries) 4 constructor))
(loop (cdr entries)))
(begin
(set! pathname-url-predicates
- (cons (vector class predicate constructor)
+ (cons (vector class
+ file-predicate
+ directory-predicate
+ pathname-predicate
+ constructor)
pathname-url-predicates))
unspecific)))))
+(define (find-pathname-url-constructor pathname must-exist? if-not-found)
+ (let ((type (file-type-indirect pathname))
+ (search
+ (lambda (index)
+ (let loop ((entries pathname-url-predicates))
+ (and (pair? entries)
+ (if ((vector-ref (car entries) index) pathname)
+ (vector-ref (car entries) 4)
+ (loop (cdr entries))))))))
+ (or (case type
+ ((REGULAR) (search 1))
+ ((DIRECTORY) (search 2))
+ ((#F) (and (not must-exist?) (search 3)))
+ (else #f))
+ (and if-not-found
+ (if-not-found pathname type)))))
+
(define pathname-url-predicates '())
+\f
+(define-method url-is-selectable? ((url <pathname-url>))
+ (and (find-pathname-url-constructor (pathname-url-pathname url) #t #f) #t))
(define-method parse-url-body ((string <string>) (default-url <pathname-url>))
(let ((pathname
(parse-pathname-url-body string (pathname-url-pathname default-url))))
- ((let loop ((entries pathname-url-predicates))
- (if (pair? entries)
- (if ((vector-ref (car entries) 1) pathname)
- (vector-ref (car entries) 2)
- (loop (cdr entries)))
- (if (or (directory-pathname? pathname)
- (file-directory? pathname))
- make-directory-url
- make-file-url)))
+ ((find-pathname-url-constructor pathname #f
+ (lambda (pathname type)
+ (case type
+ ((REGULAR) make-file-url)
+ ((DIRECTORY) make-directory-url)
+ ((#F)
+ (if (directory-pathname? pathname)
+ make-directory-url
+ make-file-url))
+ (else
+ (error "Pathname refers to illegal file type:" pathname)))))
pathname)))
(define (parse-pathname-url-body string default-pathname)
(error:bad-range-argument string 'PARSE-PATHNAME-URL-BODY))
(else
(finish string)))))
+
+(define-method url-body ((url <pathname-url>))
+ (pathname->url-body (pathname-url-pathname url)))
+
+(define (pathname->url-body pathname)
+ (string-append (let ((device (pathname-device pathname)))
+ (if (string? device)
+ (string-append "/" device ":")
+ ""))
+ (let ((directory (pathname-directory pathname)))
+ (if (pair? directory)
+ (string-append
+ (if (eq? (car directory) 'ABSOLUTE) "/" "")
+ (decorated-string-append
+ "" "" "/"
+ (map (lambda (string)
+ (url:encode-string
+ (if (eq? string 'UP) ".." string)))
+ (cdr directory))))
+ ""))
+ (url:encode-string (file-namestring pathname))))
\f
;;;; File folders
(define-method url-exists? ((url <file-url>))
(file-exists? (pathname-url-pathname url)))
-(define-method url-is-selectable? ((url <file-url>))
- (file-regular? (pathname-url-pathname url)))
-
(define-method url-presentation-name ((url <file-url>))
(file-namestring (pathname-url-pathname url)))
\f
;;;; Server operations
+(define-method container-url-contents ((url <directory-url>))
+ (simple-directory-read (pathname-url-pathname url)
+ (lambda (name directory result)
+ (if (or (string=? name ".") (string=? name ".."))
+ result
+ (let* ((pathname
+ (parse-namestring (string-append directory name) #f #f))
+ (constructor (pathname-url-filter pathname)))
+ (if constructor
+ (cons (constructor pathname) result)
+ result))))))
+
(define-method %url-complete-string
((string <string>) (default-url <pathname-url>)
if-unique if-not-unique if-not-found)
(parse-pathname-url-body
string
(directory-pathname (pathname-url-pathname default-url)))
- (lambda (pathname) pathname #t)
+ pathname-url-filter
(lambda (string)
(if-unique (pathname->url-body string)))
(lambda (prefix get-completions)
(parse-pathname-url-body
string
(directory-pathname (pathname-url-pathname default-url)))
- (lambda (pathname) pathname #t))))
+ pathname-url-filter)))
+
+(define (pathname-url-filter pathname)
+ (find-pathname-url-constructor pathname #t
+ (lambda (pathname type)
+ pathname
+ (and (eq? type 'DIRECTORY)
+ make-directory-url))))
(define-method %delete-folder ((url <file-url>))
(delete-file (pathname-url-pathname url)))
;;; -*-Scheme-*-
;;;
-;;; $Id: imail-imap.scm,v 1.150 2001/05/13 03:46:01 cph Exp $
+;;; $Id: imail-imap.scm,v 1.151 2001/05/15 19:46:54 cph Exp $
;;;
;;; Copyright (c) 1999-2001 Massachusetts Institute of Technology
;;;
mailbox))
(define-method make-peer-url ((url <imap-url>) base-name)
- (imap-url-new-mailbox
- url
- (string-append (imap-mailbox-container-string url (imap-url-mailbox url))
- base-name)))
+ (let ((url (url-container url)))
+ (imap-url-new-mailbox
+ url
+ (string-append (imap-url-mailbox url) "/" base-name))))
-(define-method url-container ((url <imap-url>))
- (imap-url-new-mailbox
- url
- (let ((mailbox (imap-mailbox-container-string url (imap-url-mailbox url))))
- (if (string-suffix? "/" mailbox)
- (string-head mailbox (fix:- (string-length mailbox) 1))
- mailbox))))
-
-(define (imap-mailbox-container-string url mailbox)
- (let ((index (string-search-backward "/" mailbox)))
- (if index
- (string-head mailbox index)
- (or (let ((response
- (let ((connection
- (search-imap-connections
- (lambda (connection)
- (and (compatible-imap-urls?
- (imap-connection-url connection)
- url)
- (not
- (eq? (imap-connection-delimiter connection)
- 'UNKNOWN)))))))
- (and connection
- (imap-connection-namespace connection)))))
- (and response
- (let ((namespace
- (imap:response:namespace-personal response)))
- (and (pair? namespace)
- (car namespace)
- (let ((prefix
- (imap:decode-mailbox-name (caar namespace)))
- (delimiter (cadar namespace)))
- (cond ((not delimiter)
- prefix)
- ((and (fix:= (string-length prefix) 6)
- (string-prefix-ci? "inbox" prefix)
- (string-suffix? delimiter prefix))
- "inbox/")
- (else
- (string-replace prefix
- (string-ref delimiter 0)
- #\/))))))))
- ""))))
-\f
(define-method parse-url-body (string (default-url <imap-url>))
(call-with-values (lambda () (parse-imap-url-body string default-url))
(lambda (user-id host port mailbox)
(imap-url-mailbox default-url)))
(values #f #f #f #f))))))
\f
+(define-method url-container ((url <imap-url>))
+ (imap-url-new-mailbox
+ url
+ (let ((mailbox (imap-url-mailbox url)))
+ (let ((index (string-find-previous-char mailbox #\/)))
+ (if index
+ (string-head mailbox index)
+ (or (get-personal-namespace url) ""))))))
+
+(define (get-personal-namespace url)
+ (let ((response
+ (let ((connection
+ (search-imap-connections
+ (lambda (connection)
+ (and (compatible-imap-urls? (imap-connection-url connection)
+ url)
+ (not (eq? (imap-connection-namespace connection)
+ 'UNKNOWN)))))))
+ (and connection
+ (imap-connection-namespace connection)))))
+ (and response
+ (let ((namespace (imap:response:namespace-personal response)))
+ (and (pair? namespace)
+ (car namespace)
+ (let ((prefix (imap:decode-mailbox-name (caar namespace)))
+ (delimiter (cadar namespace)))
+ (if delimiter
+ (let ((base
+ (if (string-suffix? delimiter prefix)
+ (string-head prefix
+ (fix:- (string-length prefix) 1))
+ prefix)))
+ (if (string-ci=? "inbox" base)
+ "inbox"
+ (string-replace base
+ (string-ref delimiter 0)
+ #\/)))
+ prefix)))))))
+
+(define-method container-url-contents ((url <imap-url>))
+ (with-open-imap-connection url
+ (lambda (connection)
+ (map (lambda (response)
+ (imap-url-new-mailbox
+ url
+ (let ((delimiter (imap:response:list-delimiter response))
+ (mailbox
+ (imap:decode-mailbox-name
+ (imap:response:list-mailbox response))))
+ (if delimiter
+ (string-replace mailbox (string-ref delimiter 0) #\/)
+ mailbox))))
+ (imap:command:list connection
+ ""
+ (string-append
+ (imap-mailbox/url->server
+ url
+ (let ((mailbox (imap-url-mailbox url)))
+ (if (or (string-null? mailbox)
+ (string-suffix? "/" mailbox))
+ mailbox
+ (string-append mailbox "/"))))
+ "%"))))))
+\f
(define-method %url-complete-string
((string <string>) (default-url <imap-url>)
if-unique if-not-unique if-not-found)
(define (imap-mailbox-completions prefix url)
(with-open-imap-connection url
(lambda (connection)
- (let ((get-list
- (lambda (prefix)
- (imap:command:list connection "" (string-append prefix "%")))))
- (append-map!
- (lambda (response)
- (let ((flags (imap:response:list-flags response))
- (delimiter (imap:response:list-delimiter response))
- (mailbox
- (imap:decode-mailbox-name
- (imap:response:list-mailbox response))))
- (let ((mailbox*
- (if delimiter
- (string-replace mailbox (string-ref delimiter 0) #\/)
- mailbox)))
- (let ((tail
- (if (and delimiter
- (or (memq '\NOSELECT flags)
- (and (not (memq '\NOINFERIORS flags))
- (pair?
- (get-list
- (string-append mailbox
- delimiter))))))
- (list (string-append mailbox* "/"))
- '())))
- (if (memq '\NOSELECT flags)
- tail
- (cons mailbox* tail))))))
- (get-list (imap-mailbox/url->server url prefix)))))))
+ (map (lambda (response)
+ (let ((flags (imap:response:list-flags response))
+ (delimiter (imap:response:list-delimiter response))
+ (mailbox
+ (imap:decode-mailbox-name
+ (imap:response:list-mailbox response))))
+ (let ((mailbox
+ (if delimiter
+ (string-replace mailbox (string-ref delimiter 0) #\/)
+ mailbox)))
+ (if (and delimiter
+ (memq '\NOSELECT flags)
+ (not (memq '\NOINFERIORS flags)))
+ (string-append mailbox "/")
+ mailbox))))
+ (imap:command:list
+ connection
+ ""
+ (string-append (imap-mailbox/url->server url prefix) "%"))))))
\f
-;;;; URL/server delimiter conversion
+;;;; URL->server delimiter conversion
(define (imap-url-server-mailbox url)
(imap-mailbox/url->server url (imap-url-mailbox url)))
(string-replace mailbox #\/ delimiter)
mailbox)))
-(define (imap-mailbox/server->url url mailbox)
- (let ((delimiter (imap-mailbox-delimiter url mailbox)))
- (if (and delimiter (not (char=? delimiter #\/)))
- (string-replace mailbox delimiter #\/)
- mailbox)))
-
(define (imap-mailbox-delimiter url mailbox)
- (or (let ((entry (find-imap-namespace-entry url mailbox)))
- (and entry
- (cadr entry)))
- (let ((delimiter (imap-url-delimiter url)))
- (and delimiter
- (string-ref delimiter 0)))))
-
-(define (find-imap-namespace-entry url mailbox)
- (let ((response (imap-url-namespace url)))
- (and response
- (let ((try
- (lambda (namespace)
- (let loop ((entries namespace))
- (and (pair? entries)
- (or (let ((prefix
- (imap:decode-mailbox-name (caar entries)))
- (delimiter (cadar entries)))
- (if (and delimiter
- (fix:= (string-length prefix) 6)
- (string-prefix-ci? "inbox" prefix)
- (string-suffix? delimiter prefix))
- (and (string-prefix-ci? prefix mailbox)
- (list (string-append "inbox" delimiter)
- (string-ref delimiter 0)))
- (and (string-prefix? prefix mailbox)
- (list prefix
- (and delimiter
- (string-ref delimiter
- 0))))))
- (loop (cdr entries))))))))
- (or (try (imap:response:namespace-personal response))
- (try (imap:response:namespace-shared response))
- (try (imap:response:namespace-other response)))))))
+ (let* ((slash (string-find-next-char mailbox #\/))
+ (root
+ (if slash
+ (string-head mailbox (fix:+ slash 1))
+ mailbox))
+ (key (imap-url-new-mailbox url (if slash root ""))))
+ (let ((delimiter (hash-table/get imap-delimiters-table key 'UNKNOWN)))
+ (if (eq? delimiter 'UNKNOWN)
+ (let ((delimiter
+ (imap:response:list-delimiter
+ (with-open-imap-connection url
+ (lambda (connection)
+ (imap:command:get-delimiter connection root))))))
+ (let ((delimiter
+ (and delimiter
+ (string-ref delimiter 0))))
+ (hash-table/put! imap-delimiters-table key delimiter)
+ delimiter))
+ delimiter))))
+
+(define imap-delimiters-table
+ (make-equal-hash-table))
\f
;;;; Server connection
(port define standard initial-value #f)
(greeting define standard initial-value #f)
(capabilities define standard initial-value '())
- (delimiter define standard initial-value 'UNKNOWN)
- (namespace define standard initial-value #f)
+ (namespace define standard initial-value 'UNKNOWN)
(sequence-number define standard initial-value 0)
(response-queue define accessor initializer (lambda () (cons '() '())))
(folder define standard initial-value #f)
(imail-ui:delete-stored-pass-phrase url)
(error "Unable to log in:"
(imap:response:response-text-string response))))))
- (if (eq? (imap-connection-delimiter connection) 'UNKNOWN)
- (begin
- (set-imap-connection-delimiter!
- connection
- (imap:response:list-delimiter
- (car (imap:command:list connection "" "inbox"))))
- (if (memq 'NAMESPACE (imap-connection-capabilities connection))
- (set-imap-connection-namespace!
- connection
- (imap:command:namespace connection)))))
+ (if (eq? (imap-connection-namespace connection) 'UNKNOWN)
+ (set-imap-connection-namespace!
+ connection
+ (and (memq 'NAMESPACE (imap-connection-capabilities connection))
+ (imap:command:namespace connection))))
#t)))
\f
(define (close-imap-connection connection)
(if (imap-connection-port connection)
(imap:command:logout connection))
(close-imap-connection connection))))
-
-(define (imap-url-delimiter url)
- (let ((connection (get-imap-connection url)))
- (let ((delimiter (imap-connection-delimiter connection)))
- (if (eq? delimiter 'UNKNOWN)
- (with-open-imap-connection url imap-connection-delimiter)
- delimiter))))
-
-(define (imap-url-namespace url)
- (let ((connection (get-imap-connection url)))
- (if (eq? (imap-connection-delimiter connection) 'UNKNOWN)
- (with-open-imap-connection url imap-connection-namespace)
- (imap-connection-namespace connection))))
\f
;;;; Folder datatype
'SEARCH key-plist))
(define (imap:command:list connection reference pattern)
- (imap:command:multiple-response imap:response:list? connection
- 'LIST reference pattern))
+ (imap:command:multiple-response imap:response:list? connection 'LIST
+ (imap:encode-mailbox-name reference)
+ (imap:encode-mailbox-name pattern)))
+
+(define (imap:command:get-delimiter connection reference)
+ (imap:command:single-response imap:response:list? connection 'LIST
+ (imap:encode-mailbox-name reference)
+ (imap:encode-mailbox-name "")))
\f
(define (imap:command:no-response connection command . arguments)
(let ((responses (apply imap:command connection command arguments)))