-; uri-download : String -> Void
-(define (uri-download uri)
- (define cache-file-path
- (expand-user-path (string-append "~/.tt/cache/" (hash-sha1 uri))))
- (log-info "uri-download ~a" uri)
- ; TODO Timeout. Currently hangs on slow connections.
- (let* ([resp (http-get uri)]
- [status (http-response-code resp)]
- [body (http-response-body resp)])
- (log-debug "finished GET ~a status:~a body length:~a"
- uri status (string-length body))
- ; TODO Handle redirects
- (if (= status 200)
- (display-to-file body cache-file-path #:exists 'replace)
- ; TODO A more-informative exception
- (raise status))))
+(: uri? (-> String Boolean))
+(define (uri? str)
+ (regexp-match? #rx"^[a-z]+://.*" (string-downcase str)))
+
+(: str->peer (String (Option Peer)))
+(define (str->peer str)
+ (log-debug "Parsing peer string: ~v" str)
+ (with-handlers*
+ ([exn:fail?
+ (λ (e)
+ (log-error "Invalid URI in string: ~v, exn: ~v" str e)
+ #f)])
+ (match (string-split str)
+ [(list u) #:when (uri? u) (Peer #f (string->url u))]
+ [(list n u) #:when (uri? u) (Peer n (string->url u))]
+ [_
+ (log-error "Invalid peer string: ~v" str)
+ #f])))
+
+
+(: filter-comments (-> (Listof String) (Listof String)))
+(define (filter-comments lines)
+ (filter-not (λ (line) (string-prefix? line "#")) lines))
+
+(: str->peers (-> String (Listof Peer)))
+(define (str->peers str)
+ (filter-map str->peer (filter-comments (str->lines str))))
+
+(: peers->file (-> (Listof Peers) Path-String Void))
+(define (peers->file peers path)
+ (display-lines-to-file
+ (map (match-lambda
+ [(Peer n u)
+ (format "~a~a" (if n (format "~a " n) "") (url->string u))])
+ peers)
+ path
+ #:exists 'replace))
+
+(: file->peers (-> Path-String (Listof Peer)))
+(define (file->peers file-path)
+ (if (file-exists? file-path)
+ (str->peers (file->string file-path))
+ (begin
+ (log-warning "File does not exist: ~v" (path->string file-path))
+ '())))
+
+(define re-rfc2822
+ #px"^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), ([0-9]{2}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([0-9]{4}) ([0-2][0-9]):([0-6][0-9]):([0-6][0-9]) GMT")
+
+(: b->n (-> Bytes (Option Number)))
+(define (b->n b)
+ (string->number (bytes->string/utf-8 b)))
+
+(: mon->num (-> Bytes Natural))
+(define/match (mon->num mon)
+ [(#"Jan") 1]
+ [(#"Feb") 2]
+ [(#"Mar") 3]
+ [(#"Apr") 4]
+ [(#"May") 5]
+ [(#"Jun") 6]
+ [(#"Jul") 7]
+ [(#"Aug") 8]
+ [(#"Sep") 9]
+ [(#"Oct") 10]
+ [(#"Nov") 11]
+ [(#"Dec") 12])
+
+(: rfc2822->epoch (-> Bytes (Option Nonnegative-Integer)))
+(define (rfc2822->epoch timestamp)
+ (match (regexp-match re-rfc2822 timestamp)
+ [(list _ _ dd mo yyyy HH MM SS)
+ #:when (and dd mo yyyy HH MM SS)
+ (find-seconds (b->n SS)
+ (b->n MM)
+ (b->n HH)
+ (b->n dd)
+ (mon->num mo)
+ (b->n yyyy)
+ #f)]
+ [_
+ #f]))
+
+(: user-agent String)
+(define user-agent
+ (let*
+ ([prog-name "tt"]
+ [prog-version (info:#%info-lookup 'version)]
+ [prog-uri "https://github.com/xandkar/tt"]
+ [user-peer-file (build-path tt-home-dir "me")]
+ [user
+ (if (file-exists? user-peer-file)
+ (match (first (file->peers user-peer-file))
+ [(Peer #f u) (format "+~a" (url->string u) )]
+ [(Peer n u) (format "+~a; @~a" (url->string u) n)])
+ (format "+~a" prog-uri))])
+ (format "~a/~a (~a)" prog-name prog-version user)))