Index: client/pkgd.eagle ================================================================== --- client/pkgd.eagle +++ client/pkgd.eagle @@ -18,10 +18,15 @@ # NOTE: Use our own namespace here because even though we do not directly # support namespaces ourselves, we do not want to pollute the global # namespace if this script actually ends up being evaluated in Tcl. # namespace eval ::PackageDownloader { + # + # NOTE: This procedure sets up the default values for all configuration + # parameters used by the package downloader client. There are no + # arguments. + # proc setupDownloadVars {} { # # NOTE: Prevent progress messages from being displayed while downloading # from the repository, etc? By default, this is enabled. # @@ -58,18 +63,31 @@ if {![info exists persistentDirectory]} then { set persistentDirectory [getPersistentRootDirectory] } } + # + # NOTE: This procedure returns the root directory where any packages that + # are downloaded should be saved to permanent storage for subsequent + # use. There are no arguments. + # proc getPersistentRootDirectory {} { # # NOTE: Return a directory parallel to the one containing the library # directory. # return [file join [file dirname [info library]] pkgr] } + # + # 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 + # argument is the fully qualified path for the directory to add to + # the auto-path. + # proc addToAutoPath { language directory } { # # NOTE: Add the specified directory to the auto-path if not already # present. # @@ -106,15 +124,40 @@ } else { error "unsupported language, no idea how to modify auto-path" } } + # + # 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. + # proc downloadFiles { language version fileNames persistent } { variable baseUri variable downloadUri variable persistentDirectory variable quiet + + 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" + } + } else { + error "unsupported language" + } if {$persistent} then { set downloadRootDirectory [file join $persistentDirectory] } else { global env Index: client/pkgd.eagle.harpy ================================================================== --- client/pkgd.eagle.harpy +++ client/pkgd.eagle.harpy @@ -19,32 +19,32 @@ None Mistachkin Systems - 36e6e8e4-39ac-4f29-928e-5c10e32cb1eb + 2c09ab50-08d8-47a0-8d50-96529e4cf6fd SHA512 Script - 2016-08-16T23:11:46.2517109Z + 2016-08-17T22:38:23.1247578Z -1.00:00:00 0x2c322765603b5278 - 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 Index: client/pkgr.eagle ================================================================== --- client/pkgr.eagle +++ client/pkgr.eagle @@ -27,15 +27,25 @@ # be modified to include the "Eagle1.0" directory (i.e. the one # containing the Eagle core script library file "init.eagle"). # package require Eagle.Library + # + # NOTE: This procedure returns a formatted, possibly version-specific, + # package name, for use in logging. + # proc formatPackageName { package version } { return [string trim [appendArgs \ $package " " [getLookupVersion $version]]] } + # + # NOTE: This procedure returns a formatted script result. If the string + # result is empty, only the return code is used. The code argument + # must be an integer Tcl return code (e.g. from [catch]) and the + # result argument is the script result or error message. + # proc formatResult { code result } { switch -exact -- $code { 0 {set codeString ok} 1 {set codeString error} 2 {set codeString return} @@ -49,16 +59,26 @@ } else { return $codeString } } + # + # NOTE: This procedure emits a message to the package repository client + # log. The string argument is the content of the message to emit. + # + # proc pkgLog { string } { catch { tclLog [appendArgs [pid] " : " [clock seconds] " : pkgr : " $string] } } + # + # NOTE: This procedure attempts to determine if a string is a valid list + # and returns non-zero when that is true. The value argument is + # the string to check. + # proc stringIsList { value } { if {[isEagle]} then { return [string is list $value] } else { global tcl_version @@ -71,10 +91,16 @@ return false } } } + # + # NOTE: This procedure returns non-zero if the specified string value + # looks like a Harpy (script) certificate. The value argument + # is the string to check. + # + # proc isHarpyCertificate { value } { if {[string length $value] == 0 || [string first [string trim { proc isPgpSignature { value } { if {[string length $value] == 0 || [string first [string trim { -----BEGIN PGP SIGNATURE----- }] $value] != -1} then { return true @@ -91,10 +123,15 @@ } else { return false } } + # + # NOTE: This procedure returns a unique temporary file name. A script + # error is raised if this task cannot be accomplished. There are + # no arguments. + # proc getFileTempName {} { if {[isEagle]} then { return [file tempname] } else { global env @@ -122,10 +159,18 @@ incr counter } } } + # + # NOTE: This procedure attempts to verify the PGP signature contained in + # the specified (named) file. Non-zero is only returned if the PGP + # signature is verified successfully. A script error should not be + # raised by this procedure. The fileName argument must be the fully + # qualified path and file name of the PGP signature file to verify. + # + # proc verifyPgpSignature { fileName } { variable pgpCommand if {[isEagle]} then { set fileName [appendArgs \" $fileName \"] @@ -144,20 +189,38 @@ } return false } + # + # NOTE: This procedure returns the prefix for fully qualified variable + # names that MAY be present in the global namespace. There are + # no arguments. + # proc getLookupVarNamePrefix {} { return ::pkgr_; # TODO: Make non-global? } + # + # NOTE: This procedure returns a unique suffix for a fully qualified + # variable name that MAY be present in the global namespace. + # It is used (internally) to avoid any name collisions with + # variables and commands in the global namespace. There are + # no arguments. + # proc getLookupVarNameSuffix {} { return [appendArgs \ [string trim [pid] -] _ [string trim [clock seconds] -] _ \ [string trim [clock clicks -milliseconds] -]]; # TODO: Bad? } + # + # NOTE: This procedure returns the list of API keys to use when looking + # up packages via the package repository server. An empty list + # is returned if no API keys are currently configured. There are + # no arguments. + # proc getLookupApiKeys {} { set varName [appendArgs [getLookupVarNamePrefix] api_keys] if {[info exists $varName]} then { return [set $varName] @@ -188,10 +251,20 @@ } return https://urn.to/r/pkg; # NOTE: System default. } + # + # NOTE: This procedure returns the full URI to use when looking up a + # specific package via the package repository server. The apiKey + # argument is the API key to use -OR- an empty string if a public + # package is being looked up. The package argument is the name + # of the package being looked up, it cannot be an empty string. + # The version argument is the specific version being looked up + # -OR- an empty string for any available version. No HTTP request + # is issued by this procedure; it just returns the URI to use. + # proc getLookupUri { apiKey package version } { set baseUri [getLookupBaseUri] if {[string length $baseUri] == 0} then { return "" @@ -214,18 +287,33 @@ $baseUri ? [http::formatQuery raw 1 method lookup apiKey $apiKey \ package $package version $version]] } } + # + # NOTE: This procedure returns the version of the package that should be + # used to lookup the associated [package ifneeded] script -OR- an + # empty string if no such version exists. The package argument is + # the name of the package, it cannot be an empty string. The + # version argument is the specific version being looked up -OR- an + # empty string for any available version. + # proc getIfNeededVersion { package version } { if {[string length $version] > 0} then { return $version } return [lindex [package versions $package] 0] } + # + # NOTE: This procedure accepts a package requirement (spec) and returns + # a simple package version, if possible. An empty string will be + # returned, if appropriate (i.e. any version should be allowed). + # The requirement argument must be a package specification that + # conforms to TIP #268. + # proc getLookupVersion { requirement } { if {[set index [string first - $requirement]] != -1} then { incr index -1; set requirement [string range $requirement 0 $index] } @@ -241,10 +329,20 @@ } return $requirement } + # + # NOTE: This procedure issues an HTTP request that should return metadata + # that can be used to load and/or provide the specified package. + # The apiKey argument is the API key to use -OR- an empty string if + # a public package is being looked up. The package argument is the + # name of the package, it cannot be an empty string. The version + # argument is the specific version being looked up -OR- an empty + # string for any available version. This procedure may raise script + # errors. + # proc getLookupData { apiKey package version } { variable verboseUriDownload set uri [getLookupUri $apiKey $package $version] @@ -258,12 +356,11 @@ } if {[isEagle]} then { set data [uri download -inline $uri] } else { - variable quiet - + set quiet [expr {!$verboseUriDownload}] set data [getFileViaHttp $uri 10 stdout $quiet] } if {$verboseUriDownload} then { pkgLog [appendArgs \ @@ -276,34 +373,57 @@ set data [string trim $data] return $data } + # + # NOTE: This procedure attempts to extract the lookup code from the raw + # HTTP response data. The data argument is the raw HTTP response + # data. An empty string is returned if no lookup code is available. + # proc getLookupCodeFromData { data } { if {![stringIsList $data] || [llength $data] < 1} then { return "" } return [lindex $data 0] } + # + # NOTE: This procedure attempts to extract the lookup result from the raw + # HTTP response data. The data argument is the raw HTTP response + # data. An empty string is returned if no lookup result is available. + # proc getLookupResultFromData { data } { if {![stringIsList $data] || [llength $data] < 2} then { return "" } return [lindex $data 1] } + # + # NOTE: This procedure returns non-zero if the specified lookup response + # code indicates success. The code argument is the extracted HTTP + # lookup response code. + # proc isLookupCodeOk { code } { # # NOTE: The code must be the literal string "OK" for the package lookup # request to be considered successful. # return [expr {$code eq "OK"}] } + # + # NOTE: This procedure attempts to extract the package lookup metadata from + # the lookup result. The result argument is the lookup result. The + # varName argument is the name of an array variable, in the call frame + # of the immediate caller, that should receive the extracted package + # lookup metadata. The caller argument must be an empty string -OR- + # the literal string "handler". + # proc extractAndVerifyLookupMetadata { result varName caller } { variable strictUnknownLanguage # # NOTE: Grab the language for the package script. It must be an empty @@ -358,12 +478,12 @@ } } } # - # NOTE: If the caller wants the package metadata, use their array - # variable name. + # NOTE: If the caller wants the package lookup metadata, use their + # array variable name. # if {[string length $varName] > 0} then { upvar 1 $varName metadata set metadata(language) $language @@ -370,10 +490,15 @@ set metadata(script) $script set metadata(certificate) $certificate } } + # + # NOTE: This procedure, which may only be used from an Eagle script, checks + # if a native Tcl library is loaded and ready. If not, a script error + # is raised. + # proc tclMustBeReady {} { # # NOTE: This procedure is not allowed to actually load a native Tcl # library; therefore, one must already be loaded. # @@ -384,10 +509,15 @@ if {![tcl ready]} then { error "cannot use Tcl language, supporting library is not loaded" } } + # + # NOTE: This procedure, which may only be used from a native Tcl script, + # checks if Garuda and Eagle are loaded and ready. If not, a script + # error is raised. + # proc eagleMustBeReady {} { # # NOTE: This procedure is not allowed to actually load Garuda (and # Eagle); therefore, they must already be loaded. # @@ -398,10 +528,15 @@ if {[llength [info commands eagle]] == 0} then { error "cannot use Eagle language, supporting package is not loaded" } } + # + # NOTE: This procedure returns non-zero if the current script is being + # evaluated in Eagle with signed-only script security enabled. + # There are no arguments. + # proc eagleHasSecurity {} { # # NOTE: If possible, check if the current interpreter has security # enabled. # @@ -414,10 +549,26 @@ } return false } + # + # NOTE: This procedure uses the package lookup metadata. If the package + # script is properly signed, an attempt will be made to evaluate it + # in the target language. If the script was signed using PGP, then + # a conforming implementation of the OpenPGP specification (e.g. + # gpg2) must be installed locally. If the script was signed using + # Harpy then Garuda, Eagle, and Harpy must be installed locally. + # This procedure is designed to work for both native Tcl and Eagle + # packages. Additionally, it is designed to work when evaluated + # using either native Tcl or Eagle; however, it is up to the package + # script itself to either add the package or provide the package to + # the language(s) supported by that package. The varName argument + # is the name of an array variable in the call frame of the + # immediate caller, that contains the package lookup metadata. This + # procedure may raise script errors. + # proc processLookupMetadata { varName } { # # NOTE: If the metadata variable name appears to be invalid, fail. # if {[string length $varName] == 0} then { @@ -738,10 +889,17 @@ } else { error "unsupported script certificate" } } + # + # NOTE: This procedure performs initial setup of the package repository + # client, using the current configuration parameters. There are + # no arguments. It may load the Garuda package when evaluated in + # native Tcl. It may load a native Tcl library when evaluated in + # Eagle. It may install the [package unknown] hook. + # proc setupPackageUnknownHandler {} { variable autoHook variable autoLoadTcl variable autoRequireGaruda @@ -768,15 +926,27 @@ # hookPackageUnknownHandler } } + # + # NOTE: This procedure returns non-zero if the [package unknown] handler + # has already been hooked by the package repository client. There + # are no arguments. + # proc isPackageUnknownHandlerHooked {} { return [info exists [appendArgs \ [getLookupVarNamePrefix] saved_package_unknown]] } + # + # NOTE: This procedure attempts to hook the [package unknown] handler. It + # will raise a script error if this has already been done. The old + # [package unknown] handler is saved and will be used by the new one + # as part of the overall package loading process. There are no + # arguments. + # proc hookPackageUnknownHandler {} { set varName [appendArgs [getLookupVarNamePrefix] saved_package_unknown] if {[info exists $varName]} then { error "package unknown handler already hooked" @@ -784,10 +954,17 @@ set $varName [package unknown] package unknown [appendArgs [namespace current] ::packageUnknownHandler] } + # + # NOTE: This procedure attempts to unhook the [package unknown] handler. + # It will raise a script error if the [package unknown] handler is + # not hooked. The old [package unknown] handler is restored and + # the saved [package unknown] handler is cleared. There are no + # arguments. + # proc unhookPackageUnknownHandler {} { set varName [appendArgs [getLookupVarNamePrefix] saved_package_unknown] if {![info exists $varName]} then { error "package unknown handler is not hooked" @@ -795,10 +972,15 @@ package unknown [set $varName] unset $varName } + # + # NOTE: The procedure runs the saved [package unknown] handler. Any script + # errors are raised to the caller. The package and version arguments + # are passed in from the current [package unknown] handler verbatim. + # proc runSavedPackageUnknownHandler { package version } { # # NOTE: See if there is a saved [package unknown] handler. If so, then # attempt to use it. # @@ -809,13 +991,17 @@ lappend oldHandler $package $version; uplevel #0 $oldHandler } } # - # NOTE: This version argument to this procedure must be optional, because - # Eagle does not add a version argument when one is not supplied to - # the [package require] sub-command itself. + # NOTE: This procedure is the [package unknown] handler entry point called + # by native Tcl and Eagle. The package argument is the name of the + # package being sought, it cannot be an empty string. The version + # argument must be a specific version -OR- a package specification + # that conforms to TIP #268. This version argument must be optional + # here, because Eagle does not add a version argument when one is + # not explicitly supplied to the [package require] sub-command. # proc packageUnknownHandler { package {version ""} } { variable verboseUnknownResult # @@ -881,10 +1067,17 @@ "\" was not loaded"] } } } + # + # NOTE: This procedure evaluates the package repository client settings + # script file, if it exists. Any script errors raised are not + # masked. The script argument must be the fully qualified path + # and file name for the primary package repository client script + # file. + # proc maybeReadSettingsFile { script } { if {[string length $script] == 0 || \ ![file exists $script] || ![file isfile $script]} then { return } @@ -895,21 +1088,16 @@ if {[file exists $fileName] && [file isfile $fileName]} then { uplevel 1 [list source $fileName] } } + # + # NOTE: This procedure sets up the default values for all configuration + # parameters used by the package repository client. There are no + # arguments. + # proc setupPackageUnknownVars {} { - # - # NOTE: Prevent progress messages from being displayed while downloading - # from the repository, etc? By default, this is enabled. - # - variable quiet; # DEFAULT: true - - if {![info exists quiet]} then { - set quiet true - } - # # NOTE: Automatically install our [package unknown] handler when this # package is loaded? # variable autoHook; # DEFAULT: true @@ -976,10 +1164,20 @@ if {![info exists verboseUriDownload]} then { set verboseUriDownload false } } + # + # NOTE: This procedure is the primary entry point to the package repository + # client. It attempts to lookup the specified package using the + # currently configured package repository server. The package + # argument is the name of the package being sought, it cannot be an + # empty string. The version argument must be a specific version -OR- + # a package specification that conforms to TIP #268. The caller + # argument must be an empty string -OR- the literal string "handler". + # + # proc main { package version caller } { # # NOTE: Get the list of API keys and try each one, in order, until # the package is found. # @@ -1050,31 +1248,58 @@ ########################################################################### ############################# BEGIN Tcl ONLY ############################## ########################################################################### # - # NOTE: This procedure was stolen from the "getEagle.tcl" script. + # NOTE: This procedure was stolen from the "getEagle.tcl" script. It is + # designed to emit a progress indicator while an HTTP request is + # being processed. The channel argument is the Tcl channel where + # the progress indicator should be emitted. The type argument is + # the single-character progress indicator. The milliseconds + # argument is the number of milliseconds to wait until the next + # periodic progress indicator should be emitted. This procedure + # reschedules its own execution. # proc pageProgress { channel type milliseconds } { + # + # NOTE: This variable is used to keep track of the currently scheduled + # (i.e. pending) [after] event. + # + variable afterForPageProgress + # # NOTE: Show that something is happening... # catch {puts -nonewline $channel $type; flush $channel} # - # NOTE: Make sure that we are scheduled to run again. + # NOTE: Make sure that we are scheduled to run again, if requested. # if {$milliseconds > 0} then { - after $milliseconds [namespace code [list pageProgress \ - $channel $type $milliseconds]] + set afterForPageProgress [after $milliseconds \ + [namespace code [list pageProgress $channel $type \ + $milliseconds]]] + } else { + unset afterForPageProgress } } # - # NOTE: This procedure was stolen from the "getEagle.tcl" script. + # NOTE: This procedure was stolen from the "getEagle.tcl" script. It is + # designed to process a single HTTP request, including any HTTP + # 3XX redirects (up to the specified limit), and return the raw + # HTTP response data. It does not contain special code to handle + # HTTP status codes other than 3XX (e.g. 4XX, 5XX, etc). # + # proc getFileViaHttp { uri redirectLimit channel quiet args } { + # + # NOTE: This variable is used to keep track of the currently scheduled + # (i.e. pending) [after] event. + # + variable afterForPageProgress + # # NOTE: This procedure requires the modern version of the HTTP package, # which is typically included with the Tcl core distribution. # package require http 2.0 @@ -1204,10 +1429,17 @@ set data [::http::data $token] ::http::cleanup $token; break } } } + + # + # NOTE: If there is a currently scheduled [after] event, cancel it. + # + if {[info exists afterForPageProgress]} then { + catch {after cancel $afterForPageProgress} + } # # NOTE: If progress messages were emitted, start a fresh line. # if {!$quiet} then { Index: client/pkgr.eagle.harpy ================================================================== --- client/pkgr.eagle.harpy +++ client/pkgr.eagle.harpy @@ -19,32 +19,32 @@ None Mistachkin Systems - 3568d473-0bb6-4f90-be5f-8b450f8736d7 + 46bd3e67-77bb-4fa4-a28a-7c83625d9f52 SHA512 Script - 2016-08-17T20:05:49.0964375Z + 2016-08-17T22:22:41.5866719Z -1.00:00:00 0x2c322765603b5278 - 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 Index: client/pkgr.settings.eagle ================================================================== --- client/pkgr.settings.eagle +++ client/pkgr.settings.eagle @@ -12,8 +12,11 @@ # # RCS: @(#) $Id: $ # ############################################################################### +# TODO: Set this to your list of API keys. +# set ::pkgr_api_keys [list 0000000000000000000000000000000000000000] + variable strictUnknownLanguage false variable verboseUnknownResult true variable verboseUriDownload true Index: client/pkgr.settings.eagle.harpy ================================================================== --- client/pkgr.settings.eagle.harpy +++ client/pkgr.settings.eagle.harpy @@ -19,32 +19,32 @@ None Mistachkin Systems - 32fce62e-9899-4d81-b1d8-2fac61bb26fb + b44b4ef2-76ad-4786-b4b7-e1d604e15e8b SHA512 Script - 2016-08-17T19:24:49.1950703Z + 2016-08-17T22:22:56.6608906Z -1.00:00:00 0x2c322765603b5278 - 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