@@ -29,10 +29,12 @@ package require platform package require pki # Functions specifically meant for users to replace as a part of configuration namespace eval ::appfs::user { + variable download_method "tcl" + # User-replacable function to convert a hostname/hash/method to an URL proc construct_url {hostname hash method} { return "http://$hostname/appfs/$method/$hash" } @@ -51,10 +53,57 @@ append perms $::appfs::user::add_perms($sha1) } return $perms } + + # User-replacable function to fetch a remote file + proc download_file {url {outputChannel ""}} { + switch -- $::appfs::user::download_method { + "curl" { + if {$outputChannel eq ""} { + return [exec curl -sS -L -- $url] + } else { + exec curl -sS -L -- $url >@ $outputChannel + + return "" + } + } + "tcl" { + catch { + if {$outputChannel eq ""} { + set token [http::geturl $url] + set retval [http::data $token] + } else { + set token [http::geturl $url -binary true -channel $outputChannel] + set retval "" + } + } err + + if {![info exists token]} { + return -code error "Unable to download \"$url\": $err" + } + + set tokenCode [http::ncode $token] + + http::cleanup $token + + if {$tokenCode != "200"} { + return -code error "Unable to download \"$url\": Site did not return a 200 (returned $tokenCode)" + } + + if {![info exists retval]} { + return -code error "Unable to download \"$url\": Site did not return proper data: $err" + } + + return $retval + } + + } + + return -code error "Unable to download" + } } namespace eval ::appfs { variable cachedir "/tmp/appfs-cache" variable ttl 3600 @@ -69,11 +118,15 @@ append retval "[string range $hash [expr {$idx * 2}] end]" return $retval } - proc _cachefile {url key {keyIsHash 1}} { + proc _cachefile {url key method {keyIsHash 1}} { + if {$keyIsHash && $method != "sha1"} { + return -code error "Only SHA1 hashing method is supported" + } + set filekey $key if {$keyIsHash} { set filekey [_hash_sep $filekey] } @@ -89,18 +142,11 @@ set fd [open $tmpfile "w"] fconfigure $fd -translation binary catch { - set token [::http::geturl $url -channel $fd -binary true] - } - - if {[info exists token]} { - set ncode [::http::ncode $token] - ::http::reset $token - } else { - set ncode "900" + ::appfs::user::download_file $url $fd } close $fd if {$keyIsHash} { @@ -107,11 +153,11 @@ set hash [string tolower [sha1::sha1 -hex -file $tmpfile]] } else { set hash $key } - if {$ncode == "200" && $hash == $key} { + if {$hash == $key} { file rename -force -- $tmpfile $file } else { file delete -force -- $tmpfile } @@ -276,11 +322,11 @@ db eval {CREATE INDEX IF NOT EXISTS files_index ON files (package_sha1, file_name, file_directory);} } proc download {hostname hash {method sha1}} { set url [::appfs::user::construct_url $hostname $hash $method] - set file [_cachefile $url $hash] + set file [_cachefile $url $hash $method] if {![file exists $file]} { return -code error "Unable to fetch (file does not exist: $file)" } @@ -308,16 +354,11 @@ } set url "http://$hostname/appfs/index" catch { - set token [::http::geturl $url] - if {[::http::ncode $token] == "200"} { - set indexhash_data [::http::data $token] - } - ::http::reset $token - ::http::cleanup $token + set indexhash_data [::appfs::user::download_file $url] } # Note that we attempted to fetch this index and do not try # again for a while db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::nttl);}