#lang mzscheme
(require srfi/13/string
"errors-and-warnings.rkt"
"myenv.ss"
"http.ss"
"srfi-12.ss"
"util.ss")
(define (resource-exists? req-uri)
(cond
((string-prefix? "http://" req-uri) (with-exception-handler
(lambda (x) #f) (lambda ()
(http-transaction
"HEAD"
req-uri
(list (cons 'logger (lambda (port message . other-messages) #t)))
(lambda (resp-code resp-headers resp-port)
(close-input-port resp-port)
(and (>= resp-code 200) (< resp-code 400)))))))
(else (file-exists? req-uri))))
(define (open-input-resource req-uri)
(with-exception-handler
(lambda (x)
(sxml:warn 'open-input-resource
"~a: ~a"
req-uri ((condition-property-accessor 'exn 'message) x))
#f)
(lambda ()
(cond
((string-prefix? "http://" req-uri) (http-transaction
"GET"
req-uri
(list (cons 'logger (lambda (port message . other-messages) #t)))
(lambda (resp-code resp-headers resp-port)
(cond
((and (>= resp-code 200) (< resp-code 400)) resp-port)
(else
(close-input-port resp-port)
(sxml:warn 'open-input-resource
"~a: resource not available: ~a"
req-uri resp-code)
#f)))))
(else (open-input-file req-uri))))))
(define (ar:file-extension filename)
(let loop ((src (reverse (string->list filename)))
(res '()))
(cond
((null? src) "")
((char=? (car src) #\.)
(list->string res))
(else
(loop (cdr src) (cons (car src) res))))))
(define (ar:resource-type req-uri)
(cond
((string-prefix? "http://" req-uri) (with-exception-handler
(lambda (x) #f) (lambda ()
(http-transaction
"HEAD"
req-uri
(list (cons 'logger (lambda (port message . other-messages) #t)))
(lambda (resp-code resp-headers resp-port)
(close-input-port resp-port)
(if
(or (< resp-code 200) (>= resp-code 400))
#f (let ((content-type (assq 'CONTENT-TYPE resp-headers)))
(cond
((not content-type) 'unknown)
((string-prefix? "text/xml" (cdr content-type))
'xml)
((string-prefix? "text/html" (cdr content-type))
'html)
((string-prefix? "text/plain" (cdr content-type))
'plain)
(else
'unknown)))))))))
(else (cond
((not (file-exists? req-uri)) #f)
((assoc (ar:file-extension req-uri)
'(("xml" . xml) ("html" . html) ("htm" . html)))
=> cdr)
(else 'unknown)))))
(define (ar:uri->components uri)
(call-with-values
(lambda () (cond
((string-rindex uri #\#)
=> (lambda (pos)
(values
(substring uri (+ pos 1) (string-length uri))
(substring uri 0 pos))))
(else
(values #f uri))))
(lambda (fragment uri)
(call-with-values
(lambda () (cond
((string-rindex uri #\?)
=> (lambda (pos)
(values
(substring uri (+ pos 1) (string-length uri))
(substring uri 0 pos))))
(else
(values #f uri))))
(lambda (query uri)
(call-with-values
(lambda ()
(cond
((substring? "://" uri)
=> (lambda (pos)
(values
(substring uri 0 (+ pos 3))
(substring uri (+ pos 3) (string-length uri)))))
((string-index uri #\:)
=> (lambda (pos)
(values
(substring uri 0 (+ pos 1))
(substring uri (+ pos 1) (string-length uri)))))
(else
(values #f uri))))
(lambda (scheme uri)
(call-with-values
(lambda ()
(cond
((not scheme)
(values #f uri))
((string-index uri #\/)
=> (lambda (pos)
(values
(substring uri 0 pos)
(substring uri pos (string-length uri)))))
(else
(values #f uri))))
(lambda (authority path)
(values scheme authority path query fragment))))))))))
(define (ar:components->uri scheme authority path query fragment)
(apply string-append
(append
(if scheme (list scheme) '())
(if authority (list authority) '())
(list path)
(if query (list "?" query) '())
(if fragment (list "#" fragment) '()))))
(define (ar:path->segments path)
(call-with-values
(lambda ()
(let ((lng (string-length path)))
(cond
((and (> lng 0) (char=? (string-ref path 0) #\/))
(values "/" (substring path 1 lng)))
((and (> lng 1)
(char=? (string-ref path 1) #\:)
(member (string-ref path 2) (list #\/ #\\)))
(values (substring path 0 3)
(substring path 3 lng)))
(else (values "" path)))))
(lambda (root rel-path)
(let ((lst (string-split rel-path (list #\/ #\\))))
(if (null? lst) (values root '() "")
(let ((lst (reverse lst)))
(values root (reverse (cdr lst)) (car lst))))))))
(define (ar:segments->path root dir-lst filename backslash?)
(let ((delim (if backslash? "\\" "/")))
(apply string-append
(append
(list root)
(apply append
(map
(lambda (directory-name)
(list directory-name delim))
dir-lst))
(list filename)))))
(define (ar:normalize-dir-lst dir-lst)
(cond
((null? dir-lst) dir-lst)
((string=? (car dir-lst) ".")
(ar:normalize-dir-lst (cdr dir-lst)))
((string=? (car dir-lst) "..")
(cons (car dir-lst) (ar:normalize-dir-lst (cdr dir-lst))))
(else
(let ((processed (ar:normalize-dir-lst (cdr dir-lst))))
(cond
((null? processed)
(list (car dir-lst)))
((string=? (car processed) "..")
(cdr processed))
(else
(cons (car dir-lst) processed)))))))
(define (ar:resolve-uri-according-base base-uri req-uri)
(call-with-values
(lambda () (ar:uri->components req-uri))
(lambda (req-scheme req-authority req-path req-query req-fragment)
(if
(or req-scheme req-authority) req-uri
(call-with-values
(lambda () (ar:path->segments req-path))
(lambda (req-root req-dir-lst req-filename)
(if
(> (string-length req-root) 1) req-uri
(call-with-values
(lambda () (ar:uri->components base-uri))
(lambda
(base-scheme base-authority base-path base-query base-fragment)
(if
(string=? req-root "/") (ar:components->uri base-scheme base-authority
req-path req-query req-fragment)
(call-with-values
(lambda () (ar:path->segments base-path))
(lambda (base-root base-dir-lst base-filename)
(ar:components->uri
base-scheme
base-authority
(ar:segments->path
base-root
(ar:normalize-dir-lst (append base-dir-lst req-dir-lst))
req-filename
(and (not (string-index base-path #\/))
(string-index req-path #\\)))
req-query
req-fragment)))))))))))))
(provide (all-defined))