Index: client/1.0/pkgd.eagle ================================================================== --- client/1.0/pkgd.eagle +++ client/1.0/pkgd.eagle @@ -23,11 +23,21 @@ # # NOTE: This procedure sets up the default values for all configuration # parameters used by the package downloader client. There are no # arguments. # - proc setupDownloadVars {} { + proc setupDownloadVars { script } { + # + # NOTE: What is the fully qualified path to the directory containing the + # package downloader client? + # + variable clientDirectory + + if {![info exists clientDirectory]} then { + set clientDirectory [file dirname $script] + } + # # NOTE: Prevent progress messages from being displayed while downloading # from the repository, etc? By default, this is enabled. # variable quiet; # DEFAULT: true @@ -76,10 +86,56 @@ # directory. # return [file join [file dirname [info library]] pkgd] } + # + # NOTE: This procedure returns non-zero if the specified file seems to be + # an OpenPGP signature file. The fileName argument is the name of + # the file to check, which may or may not exist. + # + proc isPgpSignatureFileName { fileName } { + if {[string length $fileName] == 0} then { + return false + } + + set extension [file extension] + + if {$extension eq ".asc"} then { + if {[file exists $fileName]} then { + return [::PackageRepository::isPgpSignature [readFile $fileName]] + } else { + return true + } + } else { + return false + } + } + + # + # NOTE: This procedure returns non-zero if the specified file seems to be + # a Harpy script certificate file. The fileName argument is the name + # of the file to check, which may or may not exist. + # + proc isHarpyCertificateFileName { fileName } { + if {[string length $fileName] == 0} then { + return false + } + + set extension [file extension] + + if {$extension eq ".harpy"} then { + if {[file exists $fileName]} then { + return [::PackageRepository::isHarpyCertificate [readFile $fileName]] + } else { + return true + } + } else { + return false + } + } + # # NOTE: This procedure adds a directory to the auto-path of the specified # language (i.e. native Tcl or Eagle). The directory will not be # added if it is already present. The language argument must be the # literal string "eagle" or the literal string "tcl". The directory @@ -125,71 +181,133 @@ } else { error "unsupported language, no idea how to modify auto-path" } } + # + # NOTE: This procedure downloads a single file from the package file server, + # writing its contents to the specified local file name. It can also + # verify the PGP signatures. When a PGP signature file is + # downloaded, this procedure assumes the corresponding data file was + # already downloaded (i.e. since OpenPGP needs both to perform the + # signature checks). The language argument must be one of the + # literal strings "eagle", "tcl", or "client". The version argument + # must be one of the literal strings "8.4", "8.5", or "8.6" when the + # language is "tcl" -OR- the literal string "1.0" when the language + # is either "eagle" or "client". The fileName argument is a file + # name relative to the language and version-specific directory on the + # package file server. The localFileName argument is the file name + # where the downloaded file should be written. The usePgp argument + # should be non-zero when an OpenPGP signature file needs to be + # downloaded and verified for the downloaded file. + # + proc downloadOneFile { language version fileName localFileName usePgp } { + variable baseUri + variable downloadUri + variable quiet + + # + # NOTE: First, build the full relative file name to download from the + # remote package repository. + # + set fileName [file join $language $version $fileName] + set uri [subst $downloadUri] + + if {[isEagle]} then { + writeFile $localFileName [interp readorgetscriptfile -- "" $uri] + } else { + writeFile $localFileName \ + [::PackageRepository::getFileViaHttp $uri 10 stdout $quiet] + } + + # + # + # + if {$usePgp && [isPgpSignatureFileName $localFileName]} then { + # + # + # + if {![::PackageRepository::verifyPgpSignature $localFileName]} then { + error [appendArgs \ + "bad PGP signature \"" $localFileName \"] + } + } + } + # # NOTE: This procedure attempts to download a list of files, optionally # persistening them for subsequent uses by the target language. - # The language argument must be the literal string "eagle" or the - # literal string "tcl". The version argument must be the literal - # string "8.4", "8.5", or "8.6" when the language is "tcl" -OR- - # the literal string "1.0" when the language is "eagle". The - # fileNames argument must be a well-formed list of file names to - # download, each one relative to the language/version-specific - # directory on the package file server. The persistent argument - # should be non-zero if the downloaded files should be saved to - # permanent storage for subsequent use. The usePgp argument - # should be non-zero when an OpenPGP signature file needs to be - # downloaded and verified for each downloaded file. The + # The language argument must be one of the literal strings "eagle", + # "tcl", or "client". The version argument must be one of the + # literal strings "8.4", "8.5", or "8.6" when the language is "tcl" + # -OR- the literal string "1.0" when the language is either "eagle" + # or "client". The fileNames argument must be a well-formed list + # of file names to download, each one relative to the language and + # version-specific directory on the package file server. The + # persistent argument should be non-zero if the downloaded files + # should be saved to permanent storage for subsequent use. The + # usePgp argument should be non-zero when an OpenPGP signature file + # needs to be downloaded and verified for each downloaded file. The # useAutoPath argument should be non-zero to modify the auto-path # to include the temporary or persistent directories containing # the downloaded files. # # proc downloadFiles { language version fileNames persistent usePgp useAutoPath } { + global env variable baseUri + variable clientDirectory variable downloadUri variable persistentDirectory variable quiet + + set client false if {[string length $language] == 0 || $language eq "eagle"} then { if {$version ne "1.0"} then { error "unsupported Eagle version" } } elseif {$language eq "tcl"} then { if {$version ne "8.4" && $version ne "8.5" && $version ne "8.6"} then { error "unsupported Tcl version" } + } elseif {$language eq "client"} then { + if {$version ne "1.0"} then { + error "unsupported client version" + } + + set client true } else { error "unsupported language" } - if {$persistent} then { - set downloadRootDirectory $persistentDirectory - } else { - set directoryNameOnly [appendArgs \ - pkgd_ [string trim [pid] -] _ [string trim [clock seconds] -]] - - global env - - if {[info exists env(PKGD_TEMP)]} then { - set downloadRootDirectory $env(PKGD_TEMP) - } elseif {[info exists env(TEMP)]} then { - set downloadRootDirectory $env(TEMP) - } elseif {[info exists env(TMP)]} then { - set downloadRootDirectory $env(TMP) - } else { - error "please set PKGD_TEMP (via environment) to temporary directory" - } - - set downloadRootDirectory [file join \ - $downloadRootDirectory $directoryNameOnly] - } - - set downloadDirectories [list] + if {[info exists env(PKGD_TEMP)]} then { + set temporaryRootDirectory $env(PKGD_TEMP) + } elseif {[info exists env(TEMP)]} then { + set temporaryRootDirectory $env(TEMP) + } elseif {[info exists env(TMP)]} then { + set temporaryRootDirectory $env(TMP) + } else { + error "please set PKGD_TEMP (via environment) to temporary directory" + } + + set temporaryDirectory [file join $temporaryRootDirectory \ + [appendArgs pkgd_ [string trim [pid] -] _ [string trim \ + [clock seconds] -]]] + + if {$persistent} then { + if {$client} then { + set downloadRootDirectory $clientDirectory + } else { + set downloadRootDirectory $persistentDirectory + } + } else { + set downloadRootDirectory $temporaryDirectory + } + + set downloadedFileNames [list] foreach fileName $fileNames { if {[string length $fileName] == 0 || \ [file pathtype $fileName] ne "relative"} then { error [appendArgs \ @@ -201,61 +319,51 @@ if {[llength $directoryParts] == 0} then { error [appendArgs \ "bad file name \"" $fileName "\", no directory parts"] } - set downloadDirectory [file normalize [eval file join \ - [list $downloadRootDirectory] $directoryParts]] + set downloadDirectory(temporary) [file normalize [eval \ + file join [list $temporaryDirectory] $directoryParts]] + + set downloadDirectory(persistent) [file normalize [eval \ + file join [list $downloadRootDirectory] $directoryParts]] set downloadFileName [file normalize [file join \ - $downloadDirectory [file tail $fileName]]] + $downloadDirectory(temporary) [file tail $fileName]]] - if {!$persistent} then { - catch {file delete $downloadFileName} + if {[file exists $downloadFileName]} then { + error [appendArgs \ + "temporary file name \"" $downloadFileName \ + "\" already exists"] } file mkdir [file dirname $downloadFileName] - set savedFileName $fileName - set fileName [file join $language $version $fileName] - set uri [subst $downloadUri] - set fileName $savedFileName - - if {[isEagle]} then { - writeFile $downloadFileName \ - [interp readorgetscriptfile -- "" $uri] - } else { - writeFile $downloadFileName \ - [::PackageRepository::getFileViaHttp $uri 10 stdout $quiet] - } - - if {$usePgp} then { - set downloadSignatureFileName [appendArgs $downloadFileName .asc] - - set savedFileName $fileName - set fileName [file join \ - $language $version [appendArgs $fileName .asc]] - - set uri [subst $downloadUri] - set fileName $savedFileName - - if {[isEagle]} then { - writeFile $downloadSignatureFileName \ - [interp readorgetscriptfile -- "" $uri] - } else { - writeFile $downloadSignatureFileName \ - [::PackageRepository::getFileViaHttp $uri 10 stdout $quiet] - } - - if {![::PackageRepository::verifyPgpSignature \ - $downloadSignatureFileName]} then { - error [appendArgs \ - "bad PGP signature \"" $downloadSignatureFileName \"] - } - } - - lappend downloadDirectories $downloadDirectory + downloadOneFile $language $version $fileName $downloadFileName $usePgp + + lappend downloadedFileNames $downloadFileName + + if {$usePgp && ![isPgpSignatureFileName $downloadFileName] && \ + ![isHarpyCertificateFileName $downloadFileName]} then { + downloadOneFile $language $version [appendArgs $fileName .asc] \ + [appendArgs $downloadFileName .asc] $usePgp + + lappend downloadedFileNames [appendArgs $downloadFileName .asc] + } + } + + set downloadDirectories [list] + + foreach downloadedFileName $downloadedFileNames { + if {$persistent} then { + + + + + } else { + lappend downloadDirectories [file dirname $downloadedFileName] + } } set downloadDirectories [lsort -unique $downloadDirectories] if {$useAutoPath} then { @@ -279,13 +387,13 @@ ::PackageRepository::maybeReadSettingsFile [info script] # # NOTE: Setup the variables, within this namespace, used by this script. # - setupDownloadVars + setupDownloadVars [info script] # # NOTE: Provide the package to the interpreter. # package provide Eagle.Package.Downloader \ [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] }