@@ -2,11 +2,10 @@ package require http 2.7 package require sqlite3 namespace eval ::appfs { - variable sites [list] variable cachedir "/tmp/appfs-cache" proc _hash_sep {hash {seps 4}} { for {set idx 0} {$idx < $seps} {incr idx} { append retval "[string range $hash [expr {$idx * 2}] [expr {($idx * 2) + 1}]]/" @@ -42,10 +41,31 @@ } } return $file } + + proc _db {args} { + return [uplevel 1 [list ::appfs::db {*}$args]] + } + + proc init {} { + if {[info exists ::appfs::init_called]} { + return + } + + set ::appfs::init_called 1 + + if {![info exists ::appfs::db]} { + file mkdir $::appfs::cachedir + + sqlite3 ::appfs::db [file join $::appfs::cachedir cache.db] + } + + _db eval {CREATE TABLE IF NOT EXISTS packages(hostname, sha1, package, version, os, cpuArch, isLatest);} + _db eval {CREATE TABLE IF NOT EXISTS files(package_sha1, type, time, source, size, file_sha1, file_name, file_directory);} + } proc getindex {hostname} { if {[string match "*\[/~\]*" $hostname]} { return -code error "Invalid hostname" } @@ -98,13 +118,66 @@ if {![regexp {^[0-9a-f]*$} $pkgInfo(hash)]} { continue } set packages($pkgInfo(package)) [array get pkgInfo] + + # Do not do any additional work if we already have this package + set existing_packages [_db eval {SELECT package FROM packages WHERE hostname = $hostname AND sha1 = $pkgInfo(hash);}] + if {[lsearch -exact $existing_packages $pkgInfo(package)] != -1} { + continue + } + + if {$pkgInfo(isLatest)} { + _db eval {UPDATE packages SET isLatest = 0 WHERE hostname = $hostname AND package = $pkgInfo($package) AND os = $pkgInfo($package) AND cpuArch = $pkgInfo(cpuArch);} + } + + _db eval {INSERT INTO packages (hostname, sha1, package, version, os, cpuArch, isLatest) VALUES ($hostname, $pkgInfo(hash), $pkgInfo(package), $pkgInfo(version), $pkgInfo(os), $pkgInfo(cpuArch), $pkgInfo(isLatest) );} + + set file [download $hostname $pkgInfo(hash)] + set fd [open $file] + set pkgdata [read $fd] + close $fd + + foreach line [split $pkgdata "\n"] { + set line [string trim $line] + + if {[string match "*/*" $line]} { + continue + } + + if {$line == ""} { + continue + } + + set work [split $line ","] + + unset -nocomplain fileInfo + set fileInfo(type) [lindex $work 0] + set fileInfo(time) [lindex $work 1] + set fileInfo(name) [lindex $work end] + + set fileInfo(name) [split [string trim $fileInfo(name) "/"] "/"] + set fileInfo(directory) [join [lrange $fileInfo(name) 0 end-1] "/"] + set fileInfo(name) [lindex $fileInfo(name) end] + + set work [lrange $work 2 end-1] + switch -- $fileInfo(type) { + "file" { + set fileInfo(size) [lindex $work 0] + set fileInfo(sha1) [lindex $work 1] + } + "symlink" { + set fileInfo(source) [lindex $work 0] + } + } + + _db eval {INSERT INTO files (package_sha1, type, time, source, size, file_sha1, file_name, file_directory) VALUES ($pkgInfo(hash), $fileInfo(type), $fileInfo(time), $fileInfo(source), $fileInfo(size), $fileInfo(sha1), $fileInfo(name), $fileInfo(directory) );} + } } - return [array get packages] + return COMPLETE } proc download {hostname hash {method sha1}} { set url "http://$hostname/appfs/$method/$hash" set file [_cachefile $url $hash]