ADDED client/1.0/pkgIndex.eagle Index: client/1.0/pkgIndex.eagle ================================================================== --- client/1.0/pkgIndex.eagle +++ client/1.0/pkgIndex.eagle @@ -0,0 +1,24 @@ +############################################################################### +# +# pkgIndex.eagle -- +# +# Extensible Adaptable Generalized Logic Engine (Eagle) +# Package Index File +# +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. +# +# See the file "license.terms" for information on usage and redistribution of +# this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# RCS: @(#) $Id: $ +# +############################################################################### + +if {![package vsatisfies [package provide Tcl] 8.4]} {return} +if {![package vsatisfies [package provide Eagle] 1.0]} {return} + +package ifneeded Eagle.Package.Repository 1.0 \ + [list source [file join $dir pkgr.eagle]] + +package ifneeded Eagle.Package.Downloader 1.0 \ + [list source [file join $dir pkgd.eagle]] ADDED client/1.0/pkgIndex.eagle.harpy Index: client/1.0/pkgIndex.eagle.harpy ================================================================== --- client/1.0/pkgIndex.eagle.harpy +++ client/1.0/pkgIndex.eagle.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + b0129e44-3b2c-42b0-a71f-1c07aa16afd2 + SHA512 + Script + 2016-08-16T02:51:50.7605000Z + -1.00:00:00 + 0x2c322765603b5278 + + nF615q+lJascFI+duD9pIrNyquMbrsLaX1Za3HFLitpOmFxsTcj+cKQ6d5y48SHsN6OlU4DMbyJX + egCciqoZqk8SEnkD6WgFjD+oKWQRsaRMnbmI3MmZOP/z1jSff6FeLwQQkDlkV9hhJRTo/rBBS6Vu + hfTmX7eVy4Uy+ifV8/i9T+/9dfS8DTifSWq1+6YSJuhETNDCmjnBxT+SrHeOdSZE0n65SPlb6Fde + yzVVPp/DfsA3cP3q9pMHDENyueXKAz0yXjunKJAXiGM1NNDLKkJ9YKpIxb4yV/uGMduK9KIy9hkQ + PlQQpn0Eh+fWst+DZlTl1PTL6sc9WJf9GSoAnNERIYvwA6z8eqRgRfXRhAsTEa+Om76Nt5so2Prz + jtUP39UldpoXhq87H2I4NkJfngcx0x3Jybv25nwMygQv0paXWAlaEFN49atMLXYO6F85ghi8dmps + zCY6O06FjnyI43RkBUYIvuDyU1q9HjVZbKiHr0YoFVhBCZDZp/lcxucIYG2sXpX5YKDT09WPMiUz + 1r/TO286Y2kJyzmgJMMjRmqTmOID6nTRSjvPSQCyFikJ8tCIWA78IiI2+/539J99VYfNmJoPRq3j + sBJ0IERsQCrp7baZkPiF/vzGEIScXtqAsxOKbep4oLEMt8mBmLOONkAwfo+mjjvtUy5s2ZYdnqQJ + 8EUbAKF7pRcgu7+w8LVMnGJr+dh5iJG0U8J6P2yrSeu+PRKr+aiX41/w1vJLRXZEljdqQ7PoIdPt + vsgFY+HoWs0pydJDcReMQDQtNczgn+88gv0IH5YL+7bp91yMqABR4RL7wekVv9IHSt2Da3l884Zn + WWb7Xso51BdH79a4zlSwHbbetu297ScvsmiNzCnyNYVa22Dx6UrVTl5OMao8219HJkjYlsXSkmQq + IZQwQXcutbp+alQ3v/uZMytujpL4+2wldzAFCuKWEzTOoBcroxnWfrFY80XyLmGODWU0j1Np+iJC + TAnErZYhU74BXFFHuRTLjjWhOqADSdCHk1K1zgs27k1uL6maIYU0wyYc4r/KuI+zKQ13JPYcauND + L1fTB6DTropeAZIZxlJAhZGadgs3UX8yx7JHIHgJQz4LjxCcRpxwvbz+o2PVcHoav9YJOLOzhfHK + 2+/XSxTalaF+XetBz2fDBYosj/JY61zy3YJPnaq/pc5r7TCDwF/hHPrAUZh84aMjsDNcOkU6DJSU + 0ykiZeFDm/A7XxVWANeBbfgci1UgK5VCdJr4A8kFDzwBWj+u1Q3bxmYxTJurpXBC/LRNh5s1/30B + KrG8eRNO4JlCGJbCqrgdVVF8gGa/MvuVKvsC2BWLtNsmVf2BUt3wkQEXxLtQdjv3yDMzxtBtTQ== + + ADDED client/1.0/pkgIndex.tcl Index: client/1.0/pkgIndex.tcl ================================================================== --- client/1.0/pkgIndex.tcl +++ client/1.0/pkgIndex.tcl @@ -0,0 +1,24 @@ +############################################################################### +# +# pkgIndex.tcl -- +# +# Extensible Adaptable Generalized Logic Engine (Eagle) +# Package Index File +# +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. +# +# See the file "license.terms" for information on usage and redistribution of +# this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# RCS: @(#) $Id: $ +# +############################################################################### + +if {![package vsatisfies [package provide Tcl] 8.4]} {return} +if {[string length [package provide Eagle]] > 0} then {return} + +package ifneeded Eagle.Package.Repository 1.0 \ + [list source [file join $dir pkgr.eagle]] + +package ifneeded Eagle.Package.Downloader 1.0 \ + [list source [file join $dir pkgd.eagle]] ADDED client/1.0/pkgIndex.tcl.harpy Index: client/1.0/pkgIndex.tcl.harpy ================================================================== --- client/1.0/pkgIndex.tcl.harpy +++ client/1.0/pkgIndex.tcl.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + 69ca970a-0c3d-447a-91cf-44fed99731ee + SHA512 + Script + 2016-08-16T02:54:51.9323750Z + -1.00:00:00 + 0x2c322765603b5278 + + HjolMDm2KhyxiFYnQ7OlS81iIPJnyriCCNo4ATggqUhZP1ag2J7e7j4EHbit8RSgAYuPUnT3SFQH + yOxLq410LfAGS0p+jTfua7JwfeXIBOGf5zbOADM79TS9iTI0qSyqHBrvSmHBhsyoPxrGs07wcrpe + hKYneaf+j9rjJpVL0bUasIEsBtoVhXUXEubuLuiLclcCT5w/VoHHa5IP6ogNdch2zPYv1wT0Vmo4 + RO3wHzvcMwCmyj0cV9z6QxoSVgmlHDRnODXRJP4kY+nzWsEkGlNy+Gy21qjL8h8fk+CWWU/AnqhC + M4VR4fJJYlfKBXFipq6SKCWe3dQV3skkE/ETVqPoTNW3I1/UcrH7CGZiWgCPoPGKFT1uVZt0aw5y + 9J9B1nuzNTxrPlyr3IM1UY91MBxyVO4sPUhvBYkhv36/CuWj7Nixlh9Mu7MyTGquj99l7K5/gThH + VkThdOn/Vn3+7X+8OfB6aDJmQotZzu1bqvywCqrtu/DbwE0QNRI/4VbVunZilAE0ULwwAEsCVqOQ + itILH2X2XVvNLFfRjK9BCVovyrzzmAj7bzj+furph1YY4XZmEDn/Sk+xbnHQencqAFjracL4kpOs + gZ7FaGgXcAuk8LmCfVD0HWGLiVVGMVMON35lu6FUfjiFkEZTuE104/ijZUce2KxWYQfbSwrv/Ywu + hUPSDRXnmaZwwYNSYVkR6zGinmWibVRWLWP6hLy0rf2xxU5bbRDhLddHoOVLdIV0s6SRMS87z1y4 + I7mNnql6OVVl55O/Ro/GQgIkp2+gaMkef9/FDGS6jMufZA6EuCEJD0yraARofd100tjnln3EVa/r + tn0ggpaqEXyXlqv9plG9PONcjPbBp+/zPnfihOuzUEsxgi+mkoxmSQh9UWQ9ELdgrL0m9zoVr5IV + jdyHqWkAIoVM4ILN7ORsRTIKGqxqHxSiier0ySvgjaPFxd7M1uzaAuvFeCjMx74XCmsDFGEHbI49 + jc++8u+rA+ZvCVL6nhv64QgP3ce4PYznUDhSDHkLGstaw5JOibPTgiUGvyPTFVJE1cwSmYUe6KuQ + 2oeD1DKFs4/U0n902tq2aly5H7plz4NLuV5kUy1EWCANQFi2A1LX0xMoztHdSagmdmvykIronkbP + kAawsH99A9ukY1Er09bw6WDMUX6aACZQw++/e0eQs/0hkWX7o+rFFQMgEbRz5g5q7+pXVkDd1T9x + WTHAMsXWaaDeB0hcA53HkB92APDnrUi04+YOu5F1Eh0+lIi7GA+Y5fFcItVaXzuog9bUIzUAvuPh + FJGnCHXQ+gKP2/Y6c2Kd/3+O8vHPUNwbQfH4vONk4j4WANYU7oqYczSkxejI7/FJ0qGO2pueoA== + + ADDED client/1.0/pkgd.eagle Index: client/1.0/pkgd.eagle ================================================================== --- client/1.0/pkgd.eagle +++ client/1.0/pkgd.eagle @@ -0,0 +1,291 @@ +############################################################################### +# +# pkgd.eagle -- +# +# Extensible Adaptable Generalized Logic Engine (Eagle) +# Package Downloader Client +# +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. +# +# See the file "license.terms" for information on usage and redistribution of +# this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# RCS: @(#) $Id: $ +# +############################################################################### + +# +# 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. + # + variable quiet; # DEFAULT: true + + if {![info exists quiet]} then { + set quiet true + } + + # + # NOTE: The base URI for the package distribution web site. + # + variable baseUri; # DEFAULT: https://urn.to/r/pkgd + + if {![info exists baseUri]} then { + set baseUri https://urn.to/r/pkgd + } + + # + # NOTE: The URI where a single package file may be found. This file will + # belong to a specific version of one package. + # + variable downloadUri; # DEFAULT: ${baseUri}?...&filename=${fileName} + + if {![info exists downloadUri]} then { + set downloadUri {${baseUri}?download&ci=trunk&filename=${fileName}} + } + + # + # NOTE: The root directory where any persistent packages will be saved. + # + variable persistentDirectory; # DEFAULT: [getPersistentRootDirectory] + + 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]] pkgd] + } + + # + # 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. + # + if {[string length $language] == 0 || $language eq "eagle"} then { + if {[isEagle]} then { + if {![info exists ::auto_path] || \ + [lsearch -exact $::auto_path $directory] == -1} then { + lappend ::auto_path $directory + } + } else { + ::PackageRepository::eagleMustBeReady + + eagle [string map [list %directory% $directory] { + if {![info exists ::auto_path] || \ + [lsearch -exact $::auto_path {%directory%}] == -1} then { + lappend ::auto_path {%directory%} + } + }] + } + } elseif {$language eq "tcl"} then { + if {[isEagle]} then { + tcl eval [tcl master] [string map [list %directory% $directory] { + if {![info exists ::auto_path] || \ + [lsearch -exact $::auto_path {%directory%}] == -1} then { + lappend ::auto_path {%directory%} + } + }] + } else { + if {![info exists ::auto_path] || \ + [lsearch -exact $::auto_path $directory] == -1} then { + lappend ::auto_path $directory + } + } + } 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. 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 } { + 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 $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] + + foreach fileName $fileNames { + if {[string length $fileName] == 0 || \ + [file pathtype $fileName] ne "relative"} then { + error [appendArgs \ + "bad file name \"" $fileName "\", not relative"] + } + + set directoryParts [file split [file dirname $fileName]] + + 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 downloadFileName [file normalize [file join \ + $downloadDirectory [file tail $fileName]]] + + if {!$persistent} then { + catch {file delete $downloadFileName} + } + + 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 + } + + set downloadDirectories [lsort -unique $downloadDirectories] + + if {$useAutoPath} then { + foreach downloadDirectory $downloadDirectories { + addToAutoPath $language $downloadDirectory + } + } + + return $downloadDirectories + } + + # + # NOTE: This package requires the package repository client package. + # + package require Eagle.Package.Repository + + # + # NOTE: Attempt to read optional settings file now. This may override + # one or more of the variable setup in the next step. + # + ::PackageRepository::maybeReadSettingsFile [info script] + + # + # NOTE: Setup the variables, within this namespace, used by this script. + # + setupDownloadVars + + # + # NOTE: Provide the package to the interpreter. + # + package provide Eagle.Package.Downloader \ + [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] +} ADDED client/1.0/pkgd.eagle.harpy Index: client/1.0/pkgd.eagle.harpy ================================================================== --- client/1.0/pkgd.eagle.harpy +++ client/1.0/pkgd.eagle.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + 53a7ee35-d269-407b-9849-062af5a64876 + SHA512 + Script + 2016-08-19T02:47:10.2744609Z + -1.00:00:00 + 0x2c322765603b5278 + + Yp/5NPzblsNKAItBjTMKYni6R3tyMPN2ZEGrwDcSZNi4/Y1QlXWD9BF/MdF0Glc93u7UpJw3Itwt + dYziRGX2yMsaddIx7gUawg5L7vc1eCz0BTohnSctzR1wU8My0BV5wCqDvAgJICtHWJM53H6XTLW3 + CgtsZjXN0GRbDVEos3D77uK1BmPdSWLyi2L0SMA/QiGtlVY1lnP5/X6hpvBfbEX0YmDK45mEO1VR + lh2Y+aN+Jf05jiKjJwdOR4ZK1wDuW313FByM9FgDlRDpCXlnZfAeSnTPmCIafwOILlK3EZdHDJCy + 0gvFySMslL0m177+Sfpr7QW0F1ZZ4ENlse5i3ac33sF6RkAVvkmP9ZbjEq+N8+2G4v68+Yed7dET + 5Rwo//rU4QJE+To2oqJJpgv1ZsWcdqASUuof3o73gn8fRCZo95ctYvQHURzK7mFT/tTndsl/nlUq + PFeoltj0amd3sI86eHRR99rybruihdt6rETXCbhnh7+nJF6YL9SD73TX5O0+RBzOTRxkYKvdgGyX + pJ83iJtZC5N6SEySjiZlTAaLe4YTuJyaIkYDn/HWKMme7k8A7gK/Ig2iPTRUJiXm31hIn6VLfJXt + XFgoW9Ln0PAZlm4tNbeJraEeArU3YJ3vQ2FzNT7QgU1ruNiHbObOGhs76/a1XOSaoAXBRR8PgUIF + l+GWC77nJVGGeFwdUsDaJf6seEfnEV1Srp2mMFKT3Zo34Z3PMOZrcUxjGVj4tylewlq6zOWLis41 + cKE450S7P/WZZ8I3Ab14uKUYfkwR0yI+DGo/Zp9rHvYcY7eadxMByFc0BjBoWv/kaGjjtnHmLaap + e53Xm1u3oFiecYh/0+74gKZB3XcMWLJNXR/wSQR55NdOXGADoqnWBEGdlC8EHFObbE4K+v9mVmag + F2nmPPk+mbqOl2hRA5ZLutV2iR49bRghbVNPD0Me/lhoiqT9FrSTZWbggLy8ZanTn/CcaWORMJJi + gzDNED9UU/YAsXg4NYE376zKDuZWhUUwAmsjOlMksILIhiSCU0oH59AcXgLjKs9dtpCZP/t/IClr + wKfnm6CgHO2KjnB5kvCsSEmBFiciPkzzLJYGn1PYON/8/9bLXqeu65NjEl/Z+mNRP65565DWVYiN + QFcQbXbYZxDDgmjfYm85w4bOKkesGKkenT/o9jMnUksrbO85MVL1f77riYTvmK6w0mWuwmBxPxaD + nPZc2+D6uB2GHhh4bkYdgtQtaiKOSbdKjGVba1eLLzODYlrxwOZ6I1bJNvP0HoxHkpVoLacF8MgV + CODZzXo/MdVbDDT/5KHgBrXCvRUquhbpNNC7Ut5kvb5O9svvWrr6x0/S0oq8E3JYGD3/tZrErA== + + ADDED client/1.0/pkgd.settings.eagle Index: client/1.0/pkgd.settings.eagle ================================================================== --- client/1.0/pkgd.settings.eagle +++ client/1.0/pkgd.settings.eagle @@ -0,0 +1,17 @@ +############################################################################### +# +# pkgd.settings.eagle -- +# +# Extensible Adaptable Generalized Logic Engine (Eagle) +# Package Downloader Client Settings +# +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. +# +# See the file "license.terms" for information on usage and redistribution of +# this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# RCS: @(#) $Id: $ +# +############################################################################### + +variable quiet false ADDED client/1.0/pkgd.settings.eagle.harpy Index: client/1.0/pkgd.settings.eagle.harpy ================================================================== --- client/1.0/pkgd.settings.eagle.harpy +++ client/1.0/pkgd.settings.eagle.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + 88bd4979-4392-42f3-a0e6-a6d2bb881e55 + SHA512 + Script + 2016-08-19T02:27:08.9736797Z + -1.00:00:00 + 0x2c322765603b5278 + + P36p/qj9tAsoQPeWuc9Pk3g0O51CX9d/AIsxoMlrjtIreeCFn1Ki3x2KA3Jp7CKuShzSd7ZVbtGD + 4X7uNtPyoiRS3NitWI/hyR0wEN+tWqK+ImLatg4ZUjYCgbqJcGgTWsQZuJqj9X6a7nRhMrH4rJAB + uu92jWB/AHIcGTyMY/1Uw1u5/M3K2kePYnKIkZTmuNndrLBJa525osq3OeiowCshJE2ao+Yjx1RW + whTgplY5hBrgZVLnsSF/hXEXFCamsr7ZCHRjj99pX+7ycMUyyIIm+kVWY/OS9VjO+Mhu2Adq+JBu + 266BF7hDvDReEk5musXDPhZJD//ZS8blH+x+YxGHGfjw88ij9ISudRLkuv53UaGyXRLEyWvaCAHq + WSfiuaHgHSGTGPlXYWJBeQJO4r4NMs8kp/+cTL9HLwGN5UQpQ+CabnKnuGALrZdKwdojr/cS/QPK + znw85PdV2v0cGsHcGy/1qPo84pvtCX1D9aq5djmwhh4Wkg0saXAdJyr0XyNH8xniJ97hHO621QfD + BJpEtI5hOn9F74887mWsRb81zDpi66CgkFNQwgLY4tsJMulGc2inNC8Z4ZcPZHWUfSC9M1RfwlvL + 8eZ4HVK+IlGJPZpWS4qbca+ph25Uu3EPnzHLzqHiEHXd/ybWeTvQyorqvBCsjVOQ0HOv7GC11+6+ + RrO92mnX0+u69guvosE/H+G59pXsTPTW8/mGHSvsRV0lihILje3bBUCpWZk8Vaa0HmH9i/hkDicY + bs1LoTyfVRabBUIfLOujSnszlqVfc2EGZXdccVuBolsonVddzr+czcL+1SzSLtE2m7lsTgE/1mUj + oGlFhUekWPtfHw/nHEHQ09XZHPsQ5E6uCw2sI0mYDtTa6sGTk1H3eWVEwOTFheqrAx+kyAAk6Rcp + 18mg6QBaieARwUFXwdA7KT7tq7yDlB9x3yjem3aFLKWAszwqxA9RT+6WrdgcCKucWrE3gu3EZz+M + GzZ9s87hsvah/6lATnPia11vfGJKiEkTS5WI7uTDwAY1Iru9+qyTxFqQLQzhxtz+p186VOvYbotU + lZHXnl+bs1gzaUHnOSrSCiuMURaEjLfKBeySuHdzbBe8g5H6SISCujx2t7D7g/ZyBH0Lcfs1i1Wf + awwzORvocDdoQbKV8rO/U+CPxLkvqyuhmDi/LJi0os3k9iTKYN5M7WS0hKitxKGslRf0eiV6pzFB + yEDRUy5NlKwgz2Yi8dAJS2cr6O54NWCI7adW4L3PRL/La/2lISMS6oQgKe1NR6qmAiAECkqXDlHb + uESRIJ0JRYNHgLwYPqGS0ZWTZ5yQGAitOcO2GXvbL8O3hXpLalLF8ePvGzCZZMuWxspMCk8D2g== + + ADDED client/1.0/pkgr.eagle Index: client/1.0/pkgr.eagle ================================================================== --- client/1.0/pkgr.eagle +++ client/1.0/pkgr.eagle @@ -0,0 +1,1567 @@ +############################################################################### +# +# pkgr.eagle -- +# +# Extensible Adaptable Generalized Logic Engine (Eagle) +# Package Repository Client +# +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. +# +# See the file "license.terms" for information on usage and redistribution of +# this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# RCS: @(#) $Id: $ +# +############################################################################### + +# +# 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 ::PackageRepository { + # + # NOTE: This package absolutely requires the Eagle core script library + # package, even when it is being used by native Tcl. If needed, + # prior to loading this package, the native Tcl auto-path should + # 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 " " $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} + 3 {set codeString break} + 4 {set codeString continue} + default {set codeString [appendArgs unknown( $code )]} + } + + if {[string length $result] > 0} then { + return [appendArgs $codeString ": " [list $result]] + } 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 + + if {[info exists tcl_version] && $tcl_version >= 8.5} then { + return [string is list $value] + } elseif {[catch {llength $value}] == 0} then { + return true + } else { + 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 + } 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 + + if {[info exists env(PKGR_TEMP)]} then { + set directory $env(PKGD_TEMP) + } elseif {[info exists env(TEMP)]} then { + set directory $env(TEMP) + } elseif {[info exists env(TMP)]} then { + set directory $env(TMP) + } else { + error "please set PKGR_TEMP (via environment) to temporary directory" + } + + set counter [expr {[pid] ^ int(rand() * 0xFFFF)}] + + while {1} { + set fileNameOnly [format tcl%04X.tmp $counter] + set fileName [file join $directory $fileNameOnly] + + if {![file exists $fileName]} then { + return $fileName + } + + 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 \"] + + if {[catch { + eval exec -success Success [subst $pgpCommand] + }] == 0} then { + return true + } + } else { + if {[catch { + eval exec [subst $pgpCommand] 2>@1 + }] == 0} then { + return true + } + } + + 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] + } + + global env + set varName [string trim $varName :] + + if {[info exists env($varName)]} then { + return $env($varName) + } + + return [list]; # NOTE: System default, which is "public-only". + } + + proc getLookupBaseUri {} { + set varName [appendArgs [getLookupVarNamePrefix] base_uri] + + if {[info exists $varName]} then { + return [set $varName] + } + + global env + set varName [string trim $varName :] + + if {[info exists env($varName)]} then { + return $env($varName) + } + + 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 "" + } + + # + # NOTE: Build the HTTP request URI using the specified query parameter + # values, escaping them as necessary. Also, include the standard + # query parameters with constant values for this request type. + # + if {[isEagle]} then { + return [appendArgs \ + $baseUri ?raw=1&method=lookup&apiKey= [uri escape uri $apiKey] \ + &package= [uri escape uri $package] &version= [uri escape uri \ + $version]] + } else { + package require http 2.0 + + return [appendArgs \ + $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 packageRequirementToVersion { requirement } { + set result $requirement + + if {[set index [string first - $result]] != -1} then { + incr index -1; set result [string range $result 0 $index] + } + + if {[set index [string first a $result]] != -1 || \ + [set index [string first b $result]] != -1} then { + incr index -1; set result [string range $result 0 $index] + } + + if {$result eq "0"} then { + set result "" + } elseif {[regexp -- {^\d+$} $result]} then { + append result .0 + } + + return $result + } + + # + # 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. All line-endings are normalized to Unix-style; therefore, + # all script signatures must assume this. + # + proc getLookupData { apiKey package version } { + variable verboseUriDownload + + set uri [getLookupUri $apiKey $package $version] + + if {[string length $uri] == 0} then { + return "" + } + + if {$verboseUriDownload} then { + pkgLog [appendArgs \ + "attempting to download URI \"" $uri \"...] + } + + if {[isEagle]} then { + set data [uri download -inline $uri] + } else { + set quiet [expr {!$verboseUriDownload}] + set data [getFileViaHttp $uri 10 stdout $quiet] + } + + if {$verboseUriDownload} then { + pkgLog [appendArgs \ + "raw response data is: " $data] + } + + set data [string map [list <\; < >\; > "\; \" &\; &] $data] + set data [string map [list \r\n \n \r \n] $data] + 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 was stolen from the "common.tcl" script used by the + # package repository server. It has been modified to support both + # native Tcl and Eagle. It should be noted here that TIP #268 syntax + # is not supported by Eagle. For native Tcl, the requirement argument + # must be a package version or requirement conforming to the TIP #268 + # syntax. For Eagle, the requirement argument must be a simple dotted + # package version, with up to four components, without any 'a' or 'b'. + # The emptyOk argument should be non-zero if an empty string should be + # considered to be valid by the caller. The rangeOk argument should + # be non-zero if the version range syntax is allowed; this argument is + # ignored for Eagle because it requires TIP #268 support. + # + proc isValidPackageRequirement { requirement rangeOk {emptyOk false} } { + if {$emptyOk && [string length $requirement] == 0} then { + return true + } + + if {[isEagle]} then { + # + # NOTE: Eagle does not support TIP #268. Use the built-in sub-command + # that checks a version number. + # + return [string is version -strict $requirement] + } else { + # + # HACK: If a version range is not allowed, make sure that the dash + # character is not present. + # + if {!$rangeOk && [string first - $requirement] != -1} then { + return false + } + + # + # HACK: There is no direct way to check if a package requirement + # that uses the TIP #268 syntax is valid; however, we can + # purposely "misuse" the [package present] command for this + # purpose. We know the "Tcl" package is always present; + # therefore, if an error is raised here, then the package + # requirement is probably invalid. Unfortunately, the error + # message text has to be checked as well; otherwise, there + # is no way to verify version numbers that happen to be less + # than the running patch level of Tcl. + # + if {[catch {package present Tcl $requirement} error] == 0} then { + return true + } else { + # + # TODO: Maybe this will require updates in the future? + # + set pattern(1) "expected version number but got *" + set pattern(2) "expected versionMin-versionMax but got *" + + if {![string match $pattern(1) $error] && \ + ![string match $pattern(2) $error]} then { + return true + } else { + return false + } + } + } + } + + # + # 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 returned patch level. It cannot be an empty string + # and it must conform to the TIP #268 requirements for a single + # package version. + # + set patchLevel [getDictionaryValue $result PatchLevel] + + if {[string length $patchLevel] == 0} then { + error "missing patch level" + } + + if {![isValidPackageRequirement $patchLevel false]} then { + error "bad patch level" + } + + # + # NOTE: Grab the language for the package script. It must be an empty + # string, "Tcl", or "Eagle". If it is an empty string, "Eagle" + # will be assumed. + # + set language [getDictionaryValue $result Language] + + if {[lsearch -exact [list "" Tcl Eagle] $language] == -1} then { + error "unsupported language" + } + + # + # NOTE: Grab the package script. If it is an empty string, then the + # package cannot be loaded and there is nothing to do. In that + # case, just raise an error. + # + set script [getDictionaryValue $result Script] + + if {[string length $script] == 0} then { + error "missing script" + } + + # + # NOTE: Grab the package script certificate. If it is an empty string + # then the package script is unsigned, which is not allowed by + # this client. In that case, just raise an error. + # + set certificate [getDictionaryValue $result Certificate] + + if {[string length $certificate] == 0} then { + error "missing script certificate" + } + + # + # NOTE: Are we being called from the [package unknown] handler + # in "strict" mode? + # + if {$strictUnknownLanguage && $caller eq "handler"} then { + # + # NOTE: If so, the package script must be targeted at the this + # language; otherwise, there exists the possibility that + # the package may not be provided to this language. + # + if {[isEagle]} then { + if {$language ne "Eagle"} then { + error "repository package is not for Eagle" + } + } else { + if {$language ne "Tcl"} then { + error "repository package is not for Tcl" + } + } + } + + # + # 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(patchLevel) $patchLevel + set metadata(language) $language + 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. + # + if {![isEagle]} then { + error "already running in Tcl language" + } + + 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. + # + if {[isEagle]} then { + error "already running in Eagle language" + } + + 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. + # + if {[isEagle] && [llength [info commands object]] > 0} then { + if {[catch { + object invoke -flags +NonPublic Interpreter.GetActive HasSecurity + } security] == 0 && $security} then { + return true + } + } + + 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 { + error "bad metadata" + } + + # + # NOTE: This procedure requires that the metadata array variable is + # present in the call frame immediately above this one. + # + upvar 1 $varName metadata + + # + # NOTE: If the entire package metadata array is missing, fail. + # + if {![info exists metadata]} then { + error "missing metadata" + } + + # + # NOTE: If the patch level for the package is mising, fail. + # + if {![info exists metadata(patchLevel)]} then { + error "missing patch level" + } + + # + # NOTE: If the language for the package script is mising, fail. + # + if {![info exists metadata(language)]} then { + error "missing language" + } + + # + # NOTE: If the package script is mising, fail. + # + if {![info exists metadata(script)]} then { + error "missing script" + } + + # + # NOTE: If the package script certificate is mising, fail. + # + if {![info exists metadata(certificate)]} then { + error "missing script certificate" + } + + # + # NOTE: Create common cleanup script block that deletes any temporary + # files created for the script verification process. + # + set script(cleanup) { + if {[string length $fileName(2)] > 0 && \ + [file exists $fileName(2)] && [file isfile $fileName(2)]} then { + if {![info exists ::env(pkgr_keep_files)]} then { + catch {file delete $fileName(2)} + } + unset -nocomplain fileName(2) + } + + if {[string length $fileName(1)] > 0 && \ + [file exists $fileName(1)] && [file isfile $fileName(1)]} then { + if {![info exists ::env(pkgr_keep_files)]} then { + catch {file delete $fileName(1)} + } + unset -nocomplain fileName(1) + } + } + + # + # NOTE: Figure out the "type" of script certificate we are now dealing + # with. + # + if {[isHarpyCertificate $metadata(certificate)]} then { + # + # NOTE: Attempt to create a completely unique array variable name to + # hold the package metadata in this scripting language as well + # as possibly in the other necessary scripting language(s). + # + set newVarName(1) [appendArgs \ + [getLookupVarNamePrefix] metadata_ [getLookupVarNameSuffix]] + + set newVarName(2) [appendArgs \ + [getLookupVarNamePrefix] cleanup_ [getLookupVarNameSuffix]] + + set newProcName(1) [appendArgs \ + [getLookupVarNamePrefix] eagleHasSecurity_ [getLookupVarNameSuffix]] + + set newProcName(2) [appendArgs \ + [getLookupVarNamePrefix] getFileTempName_ [getLookupVarNameSuffix]] + + set newProcName(3) [appendArgs \ + [getLookupVarNamePrefix] tclMustBeReady_ [getLookupVarNameSuffix]] + + # + # NOTE: Create the Eagle script block that will be used to securely + # evaluate a signed package script. This must be evaluated in + # Eagle because it uses several plugins only available there. + # + set script(outer) [string map [list \ + %metadata% $newVarName(1) %cleanup% $newVarName(2) \ + %eagleHasSecurity% $newProcName(1) %getFileTempName% \ + $newProcName(2) %tclMustBeReady% $newProcName(3)] { + try { + # + # NOTE: If there is no package script, there is nothing we + # can do here. + # + if {[string length ${%metadata%(script)}] > 0} then { + # + # NOTE: Save the security state for the interpreter. Then, attempt + # to enable it. This will fail if one of the needed plugins + # cannot be loaded. + # + set savedSecurity [{%eagleHasSecurity%}] + if {!$savedSecurity} then {source enableSecurity} + + try { + # + # NOTE: Figure out temporary file name for the downloaded script + # and its associated script certificate. + # + set fileName(1) [{%getFileTempName%}] + set fileName(2) [appendArgs $fileName(1) .harpy] + + try { + # + # NOTE: Write downloaded script to a temporary file. + # + writeFile $fileName(1) ${%metadata%(script)} + + # + # NOTE: Write downloaded script certificate to a temporary + # file. + # + if {[string length ${%metadata%(certificate)}] > 0} then { + writeFile $fileName(2) ${%metadata%(certificate)} + } + + # + # NOTE: This seems stupid. Why are we reading the downloaded + # script from the temporary file when we already had it + # in memory? The reason is that we need to make sure + # that the Harpy policy engine has a chance to check the + # downloaded script against its associated certificate. + # This will raise a script error if the script signature + # is missing or invalid. + # + set script(inner) [interp readorgetscriptfile -- \ + "" $fileName(1)] + + # + # NOTE: Determine the target language for the package script, + # which may or may not be the language that is currently + # evaluating this script (Eagle). The default language, + # when one was not explicitly specified, is Eagle. In + # the future, this may be changed, e.g. to use the file + # extension of the client script. + # + switch -exact -- ${%metadata%(language)} { + "" - + Eagle { + # + # NOTE: The target language is Eagle, which is evaluating + # this script. No special handling is needed here. + # + return [uplevel #0 $script(inner)] + } + Tcl { + # + # NOTE: The target language is Tcl; therefore, a bit of + # special handling is needed here. + # + {%tclMustBeReady%}; return [tcl eval [tcl master] [list \ + uplevel #0 $script(inner)]] + } + default { + error "unsupported language" + } + } + } finally { + # + # NOTE: Perform any necessary cleanup steps. + # + eval ${%cleanup%} + } + } finally { + # + # NOTE: Restore the saved security state for the interpreter. + # + if {!$savedSecurity} then {source disableSecurity} + unset -nocomplain savedSecurity + } + } + } finally { + rename {%tclMustBeReady%} "" + rename {%getFileTempName%} "" + rename {%eagleHasSecurity%} "" + + unset -nocomplain {%cleanup%} + unset -nocomplain {%metadata%} + } + }] + + # + # NOTE: Copy the package metadata into the fresh array variable, + # if necessary, marshalling it from native Tcl to Eagle. + # + if {[isEagle]} then { + array set $newVarName(1) [array get metadata] + set $newVarName(2) $script(cleanup) + + proc $newProcName(1) {} [info body [appendArgs \ + [namespace current] ::eagleHasSecurity]] + + proc $newProcName(2) {} [info body [appendArgs \ + [namespace current] ::getFileTempName]] + + proc $newProcName(3) {} [info body [appendArgs \ + [namespace current] ::tclMustBeReady]] + + return [eval $script(outer)] + } else { + eagleMustBeReady + + eagle [list array set $newVarName(1) [array get metadata]] + eagle [list set $newVarName(2) $script(cleanup)] + + eagle [list proc $newProcName(1) {} [info body [appendArgs \ + [namespace current] ::eagleHasSecurity]]] + + eagle [list proc $newProcName(2) {} [info body [appendArgs \ + [namespace current] ::getFileTempName]]] + + eagle [list proc $newProcName(3) {} [info body [appendArgs \ + [namespace current] ::tclMustBeReady]]] + + return [eagle $script(outer)] + } + } elseif {[isPgpSignature $metadata(certificate)]} then { + # + # NOTE: If there is no package script, there is nothing we + # can do here. + # + if {[string length $metadata(script)] > 0} then { + # + # NOTE: Figure out temporary file name for the downloaded script + # and its associated PGP signature. + # + set fileName(1) [getFileTempName] + set fileName(2) [appendArgs $fileName(1) .asc] + + # + # NOTE: Write downloaded script to a temporary file. + # + writeFile $fileName(1) $metadata(script) + + # + # NOTE: Write downloaded script PGP signature a temporary file. + # + if {[string length $metadata(certificate)] > 0} then { + writeFile $fileName(2) $metadata(certificate) + } + + # + # NOTE: Attempt to verify the PGP signature for the package script. + # + if {[verifyPgpSignature $fileName(2)]} then { + # + # NOTE: Delete the temporary files that we created for the PGP + # signature verification. + # + eval $script(cleanup) + } else { + # + # NOTE: Delete the temporary files that we created for the PGP + # signature verification. + # + eval $script(cleanup) + + # + # NOTE: PGP signature verification failed. Raise an error and + # do not proceed with evaluating the package script. + # + error "bad PGP signature" + } + + # + # NOTE: The PGP signature was verified; use the downloaded package + # script verbatim. + # + set script(inner) $metadata(script) + + # + # NOTE: Determine the target language for the package script, which + # may or may not be the language that is currently evaluating + # this script (Eagle). The default language, when one was not + # explicitly specified, is Eagle. In the future, this may be + # changed, e.g. to use the file extension of the client script. + # + switch -exact -- $metadata(language) { + "" - + Eagle { + if {[isEagle]} then { + return [uplevel #0 $script(inner)] + } else { + eagleMustBeReady + + return [eagle [list uplevel #0 $script(inner)]] + } + } + Tcl { + if {[isEagle]} then { + tclMustBeReady; return [tcl eval [tcl master] [list \ + uplevel #0 $script(inner)]] + } else { + return [uplevel #0 $script(inner)] + } + } + default { + error "unsupported language" + } + } + } + } 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 + + if {$autoRequireGaruda && ![isEagle]} then { + # + # TODO: Assume this package is trusted? How can we verify it + # at this point? + # + package require Garuda + } + + if {$autoLoadTcl && [isEagle]} then { + # + # NOTE: Load a native Tcl library. It must be signed with a valid + # Authenticode signature. + # + tcl load -findflags +TrustedOnly -loadflags +SetDllDirectory + } + + if {$autoHook && ![isPackageUnknownHandlerHooked]} then { + # + # NOTE: Install our [package unknown] handler and save the original + # one for our use as well. + # + 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" + } + + 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" + } + + 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. + # + set varName [appendArgs [getLookupVarNamePrefix] saved_package_unknown] + set oldHandler [expr {[info exists $varName] ? [set $varName] : ""}] + + if {[string length $oldHandler] > 0} then { + lappend oldHandler $package $version; uplevel #0 $oldHandler + } + } + + # + # 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 + + # + # NOTE: First, run our [package unknown] handler. + # + set code(1) [catch {main $package $version handler} result(1)] + + if {$verboseUnknownResult} then { + pkgLog [appendArgs \ + "repository handler results for package \"" [formatPackageName \ + $package $version] "\" are " [formatResult $code(1) $result(1)]] + } + + # + # NOTE: Next, run the saved [package unknown] handler. + # + set code(2) [catch { + runSavedPackageUnknownHandler $package $version + } result(2)] + + if {$verboseUnknownResult} then { + pkgLog [appendArgs \ + "saved handler results for package \"" [formatPackageName \ + $package $version] "\" are " [formatResult $code(2) $result(2)]] + } + + # + # NOTE: Maybe check for the package and then optionally log results. + # + if {$verboseUnknownResult} then { + set ifNeededVersion [getIfNeededVersion \ + $package [packageRequirementToVersion $version]] + + if {[string length $ifNeededVersion] > 0} then { + set command [list package ifneeded $package $ifNeededVersion] + + if {[catch $command result(3)] == 0 && \ + [string length $result(3)] > 0} then { + pkgLog [appendArgs \ + "package script for \"" [formatPackageName $package \ + $ifNeededVersion] "\" was added: " [list $result(3)]] + } else { + pkgLog [appendArgs \ + "package script for \"" [formatPackageName $package \ + $ifNeededVersion] "\" was not added: " [list $result(3)]] + } + } else { + pkgLog [appendArgs \ + "package script for \"" [formatPackageName $package \ + $ifNeededVersion] "\" was not added"] + } + + set command [list package present $package] + if {[string length $version] > 0} then {lappend command $version} + + if {[catch $command] == 0} then { + pkgLog [appendArgs \ + "package \"" [formatPackageName $package $version] \ + "\" was loaded"] + } else { + pkgLog [appendArgs \ + "package \"" [formatPackageName $package $version] \ + "\" 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 + } + + set fileName [appendArgs \ + [file rootname $script] .settings [file extension $script]] + + 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: Automatically install our [package unknown] handler when this + # package is loaded? + # + variable autoHook; # DEFAULT: true + + if {![info exists autoHook]} then { + set autoHook true + } + + # + # NOTE: Automatically [tcl load] when this package is loaded from the + # Eagle language? + # + variable autoLoadTcl; # DEFAULT: true + + if {![info exists autoLoadTcl]} then { + set autoLoadTcl true + } + + # + # NOTE: Automatically [package require Garuda] when this package is + # loaded from the Tcl language? + # + variable autoRequireGaruda; # DEFAULT: true + + if {![info exists autoRequireGaruda]} then { + set autoRequireGaruda true + } + + # + # NOTE: The command to use when verifying OpenPGP signatures for the + # downloaded package scripts. + # + variable pgpCommand; # DEFAULT: gpg2 --verify {${fileName}} + + if {![info exists pgpCommand]} then { + set pgpCommand {gpg2 --verify {${fileName}}} + } + + # + # NOTE: Verify that the package script matches the current language + # when called from the [package unknown] handler? + # + variable strictUnknownLanguage; # DEFAULT: true + + if {![info exists strictUnknownLanguage]} then { + set strictUnknownLanguage true + } + + # + # NOTE: Emit diagnostic messages when a [package unknown] handler + # is called? + # + variable verboseUnknownResult; # DEFAULT: false + + if {![info exists verboseUnknownResult]} then { + set verboseUnknownResult false + } + + # + # NOTE: Emit diagnostic messages when a URI is fetched? + # + variable verboseUriDownload; # DEFAULT: false + + 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. + # + set apiKeys [getLookupApiKeys]; lappend apiKeys "" + + foreach apiKey $apiKeys { + # + # NOTE: Issue the lookup request to the remote package repository. + # + set data [getLookupData $apiKey $package $version] + + # + # NOTE: Attempt to grab the lookup code from the response data. + # + set code [getLookupCodeFromData $data] + + # + # NOTE: Did the lookup operation succeed? If so, stop trying + # other API keys. + # + if {[isLookupCodeOk $code]} then { + break + } + } + + # + # NOTE: Attempt to grab the lookup data from the response data. + # Upon failure, this should contain the error message. + # + set result [getLookupResultFromData $data] + + # + # NOTE: Did the lookup operation fail? + # + if {![isLookupCodeOk $code]} then { + # + # NOTE: Is there an error message? + # + if {[string length $result] > 0} then { + # + # NOTE: Yes. Use the returned error message verbatim. + # + error $result + } else { + # + # NOTE: No. Use the whole response data string as the error + # message. + # + error $data + } + } + + # + # NOTE: Process the lookup data into the pieces of metadata that we + # need to load the requested package. + # + extractAndVerifyLookupMetadata $result metadata $caller + + # + # NOTE: Attempt to load the requested package using the metadata + # extracted in the previous step. + # + processLookupMetadata metadata + } + + if {![isEagle]} then { + ########################################################################### + ############################# BEGIN Tcl ONLY ############################## + ########################################################################### + + # + # 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, if requested. + # + if {$milliseconds > 0} then { + set afterForPageProgress [after $milliseconds \ + [namespace code [list pageProgress $channel $type \ + $milliseconds]]] + } else { + unset -nocomplain afterForPageProgress + } + } + + # + # 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 + + # + # NOTE: If the 'tls' package is available, always attempt to use HTTPS. + # + if {[catch {package require tls}] == 0} then { + ::http::register https 443 ::tls::socket + + if {[string range $uri 0 6] eq "http://"} then { + set uri [appendArgs https:// [string range $uri 7 end]] + } + } + + # + # NOTE: Unless the caller forbids it, display progress messages during + # the download. + # + if {!$quiet} then { + pageProgress $channel . 250 + } + + # + # NOTE: All downloads are handled synchronously, which is not ideal; + # however, it is simple. Keep going as long as there are less + # than X redirects. + # + set redirectCount 0 + + while {1} { + # + # NOTE: Issue the HTTP request now, grabbing the resulting token. + # + set token [eval [list ::http::geturl $uri] $args] + + # + # NOTE: Check the HTTP response code, in order to follow any HTTP + # redirect responses. + # + switch -exact -- [http::ncode $token] { + 301 - + 302 - + 303 - + 307 { + # + # NOTE: Unless the caller forbids it, display progress messages + # when an HTTP redirect is returned. + # + if {!$quiet} then { + pageProgress $channel > 0 + } + + # + # NOTE: We hit another HTTP redirect. Stop if there are more + # than X. + # + incr redirectCount + + # + # TODO: Maybe make this limit configurable? + # + if {$redirectCount > $redirectLimit} then { + # + # NOTE: Just "give up" and return whatever data that we have + # now. + # + set data [::http::data $token] + ::http::cleanup $token; break + } + + # + # NOTE: Grab the metadata associated with this HTTP response. + # + array set meta [::http::meta $token] + + # + # NOTE: Is there actually a new URI (location) to use? + # + if {[info exist meta(Location)]} then { + # + # NOTE: Ok, grab it now. Later, at the top of the loop, + # it will be used in the subsequent HTTP request. + # + set location $meta(Location); unset meta + + # + # NOTE: For security, do NOT follow an HTTP redirect if + # it attempts to redirect from HTTPS to HTTP. + # + if {[string range $uri 0 7] eq "https://" && \ + [string range $location 0 7] ne "https://"} then { + # + # NOTE: Just "give up" and return whatever data that + # we have now. + # + set data [::http::data $token] + ::http::cleanup $token; break + } + + # + # NOTE: Replace the original URI with the new one, for + # use in the next HTTP request. + # + set uri $location + + # + # NOTE: Cleanup the current HTTP token now beause a new + # one will be created for the next request. + # + ::http::cleanup $token + } else { + # + # NOTE: Just "give up" and return whatever data that we + # have now. + # + set data [::http::data $token] + ::http::cleanup $token; break + } + } + default { + # + # NOTE: Ok, the HTTP response is actual data of some kind + # (which may be an error); however, it is not any + # kind of supported HTTP redirect. + # + 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} + unset -nocomplain afterForPageProgress + } + + # + # NOTE: If progress messages were emitted, start a fresh line. + # + if {!$quiet} then { + catch {puts $channel [appendArgs " " $uri]; flush $channel} + } + + return $data + } + + ########################################################################### + ############################## END Tcl ONLY ############################### + ########################################################################### + } + + # + # NOTE: Attempt to read optional settings file now. This may override + # one or more of the variable setup in the next step. + # + maybeReadSettingsFile [info script] + + # + # NOTE: Setup the variables, within this namespace, used by this script. + # + setupPackageUnknownVars + + # + # NOTE: Setup for our [package unknown] handler, which may involve a few + # different operations. + # + setupPackageUnknownHandler + + # + # NOTE: Provide the package to the interpreter. + # + package provide Eagle.Package.Repository \ + [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] +} + ADDED client/1.0/pkgr.eagle.harpy Index: client/1.0/pkgr.eagle.harpy ================================================================== --- client/1.0/pkgr.eagle.harpy +++ client/1.0/pkgr.eagle.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + 88d42a28-1e95-4dd7-aaf9-11bb262f10d0 + SHA512 + Script + 2016-08-19T02:47:51.4043437Z + -1.00:00:00 + 0x2c322765603b5278 + + Mn+rsBh675oM30+X6J/Myzrc0MmxmLCjpzV4bDcl8nZcbdSXszHTHE9ma5tAXopb05bMomy5lHal + CjEGgYubJtQFcQzuKlxp0UMVgMpK28uTS/ik9RSKXwgq83N1pwvM7cmF2RzxF/fmD/0dtb0Ulc+h + Ior9NeJcpD6lBAE3XEB288f+79mA3U2X1io4qLYvFzktpKyjen8pC8J46078b3HXSoYGUHehmZo+ + EJhVhD0Lfb9XtGh4V9hgmL9aMWJdv/jGmq+tKOJxxpU70avW4aaUzDKZE/zgR674/o2jhTw8LC+P + 7Ed5UhgnXXr6Ko0HlIZqWwwblP+/WJ91Rf3DBzlJDG1Wjwku2xAQN2JcLipbn0YGG3jr4qx9yrnw + /K1HT0CEWW/41F/LeZAZ36Kao76kGcl4OcamgAW4fPp2c85wRyIh3i6f4t1RxgixgVUuMWhbVVu5 + Fb/opbLwHBLIGQpmYqmZhz6A97CSr5eyj1CpKEAz/v76ma3qgravdVZ59C5NdhPXHQGS5MpgsWUC + tHc5aXK9npgN3femt1czY8J+dLMFP0N4ENlqJNRP14zFOd0a2vNnc6KB8OE4GAdL0V1KaAK2WIOQ + h5cPFMKSphWT8cst4/nLbOhs9G8JlXD1PsIKxgGW5YSYutkZQJPUcDMFmSEdaQ6CCc1K+o6SEXvS + RcdPzCEDwJmgYUF77ILI0whNBNFSVD+UPcoD1j6KUmJKHhOt63EYVmRUFlYfw1afVeCjrgm2q5Tp + aSVYPaoqUlBuZ0lomqUD03/XsqdwVdiZXEuoObr4INoMeZnHyQf5wpLl1ZGBvGdc7ujOkU8y/sVX + Y9ATG3czSvQlNz/06J/ghVEK1t7ZNyEe0thwj6AHM52D/GuFTmfnFaBS5HQawOE9FkYppA7x+65Z + rZjUaIBEaybEYPok29IKqw+aqA2s21gJ9c70d/M7UlpwGbT9CQqV+o6/2frQF6vSPUhrFsZPCZZs + hE7hn/jluR+tT0g3awKqWayNfG5/ZfJruKwmXcipeacr42Affi0zNxuxsMglndGGEKFtsrGySjcF + 2NCquShXYNz4i/7jh0IO4Udb5t/PP1Brpp26t35/Oug/2i2eTO5gq4MvsffXvjeEPYWPjUBug0y1 + HNAmHDDUqLoD3nK3AK+em1ukGdjMEsvlz+L9+IjOJ/po5ypkgNIsNqQITBY0S4ofp1XO2o9IHPGN + G0qKBB7G2PcGe7hh9FOAyPL81OYpxYc7Pe80zxqu+KZP6OPalBssNqIIHqj2p03cukS5X8U0QPO+ + 2f7Iv9SxJ0t9pcKyZX2iHx5H9+u0TpsghdQPiu9u63GUnIkMJfEWUoBxfJNfWuIzuMoe3rugJQ== + + ADDED client/1.0/pkgr.settings.eagle Index: client/1.0/pkgr.settings.eagle ================================================================== --- client/1.0/pkgr.settings.eagle +++ client/1.0/pkgr.settings.eagle @@ -0,0 +1,22 @@ +############################################################################### +# +# pkgr.settings.eagle -- +# +# Extensible Adaptable Generalized Logic Engine (Eagle) +# Package Repository Client Settings +# +# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. +# +# See the file "license.terms" for information on usage and redistribution of +# this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# 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 ADDED client/1.0/pkgr.settings.eagle.harpy Index: client/1.0/pkgr.settings.eagle.harpy ================================================================== --- client/1.0/pkgr.settings.eagle.harpy +++ client/1.0/pkgr.settings.eagle.harpy @@ -0,0 +1,50 @@ + + + + None + Mistachkin Systems + b44b4ef2-76ad-4786-b4b7-e1d604e15e8b + SHA512 + Script + 2016-08-17T22:22:56.6608906Z + -1.00:00:00 + 0x2c322765603b5278 + + efFtWFrnFkJeMBafjl1MxA1lygnogjoQHoS8qP4ptK+GuB7mvyJbSgtjYyUVJ0Nj9W88qJ0eGtQ3 + 5yd2fZrTX7nXmEkXaRFcSBiB/S6dtUrc6GGDO1PlOB7w3JGfSkh/4MgfRTrZyN+sRN6ODWPjFU4s + m1+HXSXynBUKyPaJ8qNd/0foXvNC/rGbHyZ+Z4FkbAfYZZSTiIz57kv9ZvlmxjSoZNffYpOGD0kQ + mFXkvDkoM4JxE3b19M/VFcKJ6NIn7o6Vqc3Xt40+9W5OTKjLVfpeV8gK01rU1d9+KuBnC3hQWyqZ + V0fF2mkyGnxO0LAZpOiQ82b8Myld/vl79iGcMLb9F5+SdnYeqBrT8VFRaiZz12MS+8BP+3JItBqn + WNFjKVWFXG5SI6aTaqbTDtiKRADqoJyb6HN5imfZll2tcK2ruICy4zYo79mDAolBEnwp4TnNcwKA + h5oQTnc44dQht8xeZdT5Ah2SNRBx0GA3nHmJO9AP68JMYbPWLABQQ6hZf2b6u6zB3ZzU8RN/+NIa + 3Y5jfKVD/f2BCJumU1jWh2R4785SNXfFT5Id5U0II9I9WhTdiylFXP3hiN/dNW0GAWq9v6TLG+pi + vfJrr/26RQfEwIiNa8keQkfJ/HgTmille05ct7DycbHu9Edq9NxWgfTtWDsAnlERTG+WhRc5hZyc + VegPWnHpoXeUHgEe5d4bkmmRfePnH+eWeibRveMErlv16jeTv4H28ftp3Vq8n0n+hkVHpYOHMV6i + J3/RhJ4G1Se/adEPdVUQRfYrP5HwI/szeR5DQoZ+zAsB34cfwWn0hsj21FeqitSE/rQ0j8pcD3yy + OIH07uf/sOqQ8hSEFnxVPTvQYp9H7QTBxFAxaMbpIUTpB6DyroPWzi4uSa+JXWSkf5sQOo2SLCoX + t3w7tpZ6o2FZNd3O519FiX3+cM78vyfIMkbT4ZFEHpFkccuG6g9nVgUDCTgMwNldSKQAC7JtwjOZ + tSQ9YjRTgZSGcJ+TjLnXJfWo8u0SM+J3K9o+zRcN+6zDZsCAo6inrxgICEFN+lTabJxyWas6MYeX + hkq7CnGT9nsDtynLniiF4FUsKTVlgHfR1ZVPwWGb0Ow/L4PdPMTlzPVttsE4bcrii6xnf85O9ijU + zfgBhDtRsst9TLXb514J8t4zT54AQPLav2DjXjDjChbm+/JF8ywAfq/v8/A0B3PIeDPgmRTouH48 + 0MFanzDYI3r75OC0ZMuS8EoseqkcC2C3IvJY9ZtsgnuIPbpxzsdooCCaHXUWL2mHZ+QOk8Q+jn7J + 1V0BeUNi0hU9SZeAai1co8ehsCw3vUkOPUoYginn2ry5ALf/jEBHW+1CX3+jlT/snSZba0GPDg== + + DELETED client/pkgIndex.eagle Index: client/pkgIndex.eagle ================================================================== --- client/pkgIndex.eagle +++ client/pkgIndex.eagle @@ -1,24 +0,0 @@ -############################################################################### -# -# pkgIndex.eagle -- -# -# Extensible Adaptable Generalized Logic Engine (Eagle) -# Package Index File -# -# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. -# -# See the file "license.terms" for information on usage and redistribution of -# this file, and for a DISCLAIMER OF ALL WARRANTIES. -# -# RCS: @(#) $Id: $ -# -############################################################################### - -if {![package vsatisfies [package provide Tcl] 8.4]} {return} -if {![package vsatisfies [package provide Eagle] 1.0]} {return} - -package ifneeded Eagle.Package.Repository 1.0 \ - [list source [file join $dir pkgr.eagle]] - -package ifneeded Eagle.Package.Downloader 1.0 \ - [list source [file join $dir pkgd.eagle]] DELETED client/pkgIndex.eagle.harpy Index: client/pkgIndex.eagle.harpy ================================================================== --- client/pkgIndex.eagle.harpy +++ client/pkgIndex.eagle.harpy @@ -1,50 +0,0 @@ - - - - None - Mistachkin Systems - b0129e44-3b2c-42b0-a71f-1c07aa16afd2 - SHA512 - Script - 2016-08-16T02:51:50.7605000Z - -1.00:00:00 - 0x2c322765603b5278 - - nF615q+lJascFI+duD9pIrNyquMbrsLaX1Za3HFLitpOmFxsTcj+cKQ6d5y48SHsN6OlU4DMbyJX - egCciqoZqk8SEnkD6WgFjD+oKWQRsaRMnbmI3MmZOP/z1jSff6FeLwQQkDlkV9hhJRTo/rBBS6Vu - hfTmX7eVy4Uy+ifV8/i9T+/9dfS8DTifSWq1+6YSJuhETNDCmjnBxT+SrHeOdSZE0n65SPlb6Fde - yzVVPp/DfsA3cP3q9pMHDENyueXKAz0yXjunKJAXiGM1NNDLKkJ9YKpIxb4yV/uGMduK9KIy9hkQ - PlQQpn0Eh+fWst+DZlTl1PTL6sc9WJf9GSoAnNERIYvwA6z8eqRgRfXRhAsTEa+Om76Nt5so2Prz - jtUP39UldpoXhq87H2I4NkJfngcx0x3Jybv25nwMygQv0paXWAlaEFN49atMLXYO6F85ghi8dmps - zCY6O06FjnyI43RkBUYIvuDyU1q9HjVZbKiHr0YoFVhBCZDZp/lcxucIYG2sXpX5YKDT09WPMiUz - 1r/TO286Y2kJyzmgJMMjRmqTmOID6nTRSjvPSQCyFikJ8tCIWA78IiI2+/539J99VYfNmJoPRq3j - sBJ0IERsQCrp7baZkPiF/vzGEIScXtqAsxOKbep4oLEMt8mBmLOONkAwfo+mjjvtUy5s2ZYdnqQJ - 8EUbAKF7pRcgu7+w8LVMnGJr+dh5iJG0U8J6P2yrSeu+PRKr+aiX41/w1vJLRXZEljdqQ7PoIdPt - vsgFY+HoWs0pydJDcReMQDQtNczgn+88gv0IH5YL+7bp91yMqABR4RL7wekVv9IHSt2Da3l884Zn - WWb7Xso51BdH79a4zlSwHbbetu297ScvsmiNzCnyNYVa22Dx6UrVTl5OMao8219HJkjYlsXSkmQq - IZQwQXcutbp+alQ3v/uZMytujpL4+2wldzAFCuKWEzTOoBcroxnWfrFY80XyLmGODWU0j1Np+iJC - TAnErZYhU74BXFFHuRTLjjWhOqADSdCHk1K1zgs27k1uL6maIYU0wyYc4r/KuI+zKQ13JPYcauND - L1fTB6DTropeAZIZxlJAhZGadgs3UX8yx7JHIHgJQz4LjxCcRpxwvbz+o2PVcHoav9YJOLOzhfHK - 2+/XSxTalaF+XetBz2fDBYosj/JY61zy3YJPnaq/pc5r7TCDwF/hHPrAUZh84aMjsDNcOkU6DJSU - 0ykiZeFDm/A7XxVWANeBbfgci1UgK5VCdJr4A8kFDzwBWj+u1Q3bxmYxTJurpXBC/LRNh5s1/30B - KrG8eRNO4JlCGJbCqrgdVVF8gGa/MvuVKvsC2BWLtNsmVf2BUt3wkQEXxLtQdjv3yDMzxtBtTQ== - - DELETED client/pkgIndex.tcl Index: client/pkgIndex.tcl ================================================================== --- client/pkgIndex.tcl +++ client/pkgIndex.tcl @@ -1,24 +0,0 @@ -############################################################################### -# -# pkgIndex.tcl -- -# -# Extensible Adaptable Generalized Logic Engine (Eagle) -# Package Index File -# -# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. -# -# See the file "license.terms" for information on usage and redistribution of -# this file, and for a DISCLAIMER OF ALL WARRANTIES. -# -# RCS: @(#) $Id: $ -# -############################################################################### - -if {![package vsatisfies [package provide Tcl] 8.4]} {return} -if {[string length [package provide Eagle]] > 0} then {return} - -package ifneeded Eagle.Package.Repository 1.0 \ - [list source [file join $dir pkgr.eagle]] - -package ifneeded Eagle.Package.Downloader 1.0 \ - [list source [file join $dir pkgd.eagle]] DELETED client/pkgIndex.tcl.harpy Index: client/pkgIndex.tcl.harpy ================================================================== --- client/pkgIndex.tcl.harpy +++ client/pkgIndex.tcl.harpy @@ -1,50 +0,0 @@ - - - - None - Mistachkin Systems - 69ca970a-0c3d-447a-91cf-44fed99731ee - SHA512 - Script - 2016-08-16T02:54:51.9323750Z - -1.00:00:00 - 0x2c322765603b5278 - - HjolMDm2KhyxiFYnQ7OlS81iIPJnyriCCNo4ATggqUhZP1ag2J7e7j4EHbit8RSgAYuPUnT3SFQH - yOxLq410LfAGS0p+jTfua7JwfeXIBOGf5zbOADM79TS9iTI0qSyqHBrvSmHBhsyoPxrGs07wcrpe - hKYneaf+j9rjJpVL0bUasIEsBtoVhXUXEubuLuiLclcCT5w/VoHHa5IP6ogNdch2zPYv1wT0Vmo4 - RO3wHzvcMwCmyj0cV9z6QxoSVgmlHDRnODXRJP4kY+nzWsEkGlNy+Gy21qjL8h8fk+CWWU/AnqhC - M4VR4fJJYlfKBXFipq6SKCWe3dQV3skkE/ETVqPoTNW3I1/UcrH7CGZiWgCPoPGKFT1uVZt0aw5y - 9J9B1nuzNTxrPlyr3IM1UY91MBxyVO4sPUhvBYkhv36/CuWj7Nixlh9Mu7MyTGquj99l7K5/gThH - VkThdOn/Vn3+7X+8OfB6aDJmQotZzu1bqvywCqrtu/DbwE0QNRI/4VbVunZilAE0ULwwAEsCVqOQ - itILH2X2XVvNLFfRjK9BCVovyrzzmAj7bzj+furph1YY4XZmEDn/Sk+xbnHQencqAFjracL4kpOs - gZ7FaGgXcAuk8LmCfVD0HWGLiVVGMVMON35lu6FUfjiFkEZTuE104/ijZUce2KxWYQfbSwrv/Ywu - hUPSDRXnmaZwwYNSYVkR6zGinmWibVRWLWP6hLy0rf2xxU5bbRDhLddHoOVLdIV0s6SRMS87z1y4 - I7mNnql6OVVl55O/Ro/GQgIkp2+gaMkef9/FDGS6jMufZA6EuCEJD0yraARofd100tjnln3EVa/r - tn0ggpaqEXyXlqv9plG9PONcjPbBp+/zPnfihOuzUEsxgi+mkoxmSQh9UWQ9ELdgrL0m9zoVr5IV - jdyHqWkAIoVM4ILN7ORsRTIKGqxqHxSiier0ySvgjaPFxd7M1uzaAuvFeCjMx74XCmsDFGEHbI49 - jc++8u+rA+ZvCVL6nhv64QgP3ce4PYznUDhSDHkLGstaw5JOibPTgiUGvyPTFVJE1cwSmYUe6KuQ - 2oeD1DKFs4/U0n902tq2aly5H7plz4NLuV5kUy1EWCANQFi2A1LX0xMoztHdSagmdmvykIronkbP - kAawsH99A9ukY1Er09bw6WDMUX6aACZQw++/e0eQs/0hkWX7o+rFFQMgEbRz5g5q7+pXVkDd1T9x - WTHAMsXWaaDeB0hcA53HkB92APDnrUi04+YOu5F1Eh0+lIi7GA+Y5fFcItVaXzuog9bUIzUAvuPh - FJGnCHXQ+gKP2/Y6c2Kd/3+O8vHPUNwbQfH4vONk4j4WANYU7oqYczSkxejI7/FJ0qGO2pueoA== - - DELETED client/pkgd.eagle Index: client/pkgd.eagle ================================================================== --- client/pkgd.eagle +++ client/pkgd.eagle @@ -1,291 +0,0 @@ -############################################################################### -# -# pkgd.eagle -- -# -# Extensible Adaptable Generalized Logic Engine (Eagle) -# Package Downloader Client -# -# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. -# -# See the file "license.terms" for information on usage and redistribution of -# this file, and for a DISCLAIMER OF ALL WARRANTIES. -# -# RCS: @(#) $Id: $ -# -############################################################################### - -# -# 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. - # - variable quiet; # DEFAULT: true - - if {![info exists quiet]} then { - set quiet true - } - - # - # NOTE: The base URI for the package distribution web site. - # - variable baseUri; # DEFAULT: https://urn.to/r/pkgd - - if {![info exists baseUri]} then { - set baseUri https://urn.to/r/pkgd - } - - # - # NOTE: The URI where a single package file may be found. This file will - # belong to a specific version of one package. - # - variable downloadUri; # DEFAULT: ${baseUri}?...&filename=${fileName} - - if {![info exists downloadUri]} then { - set downloadUri {${baseUri}?download&ci=trunk&filename=${fileName}} - } - - # - # NOTE: The root directory where any persistent packages will be saved. - # - variable persistentDirectory; # DEFAULT: [getPersistentRootDirectory] - - 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]] pkgd] - } - - # - # 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. - # - if {[string length $language] == 0 || $language eq "eagle"} then { - if {[isEagle]} then { - if {![info exists ::auto_path] || \ - [lsearch -exact $::auto_path $directory] == -1} then { - lappend ::auto_path $directory - } - } else { - ::PackageRepository::eagleMustBeReady - - eagle [string map [list %directory% $directory] { - if {![info exists ::auto_path] || \ - [lsearch -exact $::auto_path {%directory%}] == -1} then { - lappend ::auto_path {%directory%} - } - }] - } - } elseif {$language eq "tcl"} then { - if {[isEagle]} then { - tcl eval [tcl master] [string map [list %directory% $directory] { - if {![info exists ::auto_path] || \ - [lsearch -exact $::auto_path {%directory%}] == -1} then { - lappend ::auto_path {%directory%} - } - }] - } else { - if {![info exists ::auto_path] || \ - [lsearch -exact $::auto_path $directory] == -1} then { - lappend ::auto_path $directory - } - } - } 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. 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 } { - 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 $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] - - foreach fileName $fileNames { - if {[string length $fileName] == 0 || \ - [file pathtype $fileName] ne "relative"} then { - error [appendArgs \ - "bad file name \"" $fileName "\", not relative"] - } - - set directoryParts [file split [file dirname $fileName]] - - 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 downloadFileName [file normalize [file join \ - $downloadDirectory [file tail $fileName]]] - - if {!$persistent} then { - catch {file delete $downloadFileName} - } - - 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 - } - - set downloadDirectories [lsort -unique $downloadDirectories] - - if {$useAutoPath} then { - foreach downloadDirectory $downloadDirectories { - addToAutoPath $language $downloadDirectory - } - } - - return $downloadDirectories - } - - # - # NOTE: This package requires the package repository client package. - # - package require Eagle.Package.Repository - - # - # NOTE: Attempt to read optional settings file now. This may override - # one or more of the variable setup in the next step. - # - ::PackageRepository::maybeReadSettingsFile [info script] - - # - # NOTE: Setup the variables, within this namespace, used by this script. - # - setupDownloadVars - - # - # NOTE: Provide the package to the interpreter. - # - package provide Eagle.Package.Downloader \ - [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] -} DELETED client/pkgd.eagle.harpy Index: client/pkgd.eagle.harpy ================================================================== --- client/pkgd.eagle.harpy +++ client/pkgd.eagle.harpy @@ -1,50 +0,0 @@ - - - - None - Mistachkin Systems - 53a7ee35-d269-407b-9849-062af5a64876 - SHA512 - Script - 2016-08-19T02:47:10.2744609Z - -1.00:00:00 - 0x2c322765603b5278 - - Yp/5NPzblsNKAItBjTMKYni6R3tyMPN2ZEGrwDcSZNi4/Y1QlXWD9BF/MdF0Glc93u7UpJw3Itwt - dYziRGX2yMsaddIx7gUawg5L7vc1eCz0BTohnSctzR1wU8My0BV5wCqDvAgJICtHWJM53H6XTLW3 - CgtsZjXN0GRbDVEos3D77uK1BmPdSWLyi2L0SMA/QiGtlVY1lnP5/X6hpvBfbEX0YmDK45mEO1VR - lh2Y+aN+Jf05jiKjJwdOR4ZK1wDuW313FByM9FgDlRDpCXlnZfAeSnTPmCIafwOILlK3EZdHDJCy - 0gvFySMslL0m177+Sfpr7QW0F1ZZ4ENlse5i3ac33sF6RkAVvkmP9ZbjEq+N8+2G4v68+Yed7dET - 5Rwo//rU4QJE+To2oqJJpgv1ZsWcdqASUuof3o73gn8fRCZo95ctYvQHURzK7mFT/tTndsl/nlUq - PFeoltj0amd3sI86eHRR99rybruihdt6rETXCbhnh7+nJF6YL9SD73TX5O0+RBzOTRxkYKvdgGyX - pJ83iJtZC5N6SEySjiZlTAaLe4YTuJyaIkYDn/HWKMme7k8A7gK/Ig2iPTRUJiXm31hIn6VLfJXt - XFgoW9Ln0PAZlm4tNbeJraEeArU3YJ3vQ2FzNT7QgU1ruNiHbObOGhs76/a1XOSaoAXBRR8PgUIF - l+GWC77nJVGGeFwdUsDaJf6seEfnEV1Srp2mMFKT3Zo34Z3PMOZrcUxjGVj4tylewlq6zOWLis41 - cKE450S7P/WZZ8I3Ab14uKUYfkwR0yI+DGo/Zp9rHvYcY7eadxMByFc0BjBoWv/kaGjjtnHmLaap - e53Xm1u3oFiecYh/0+74gKZB3XcMWLJNXR/wSQR55NdOXGADoqnWBEGdlC8EHFObbE4K+v9mVmag - F2nmPPk+mbqOl2hRA5ZLutV2iR49bRghbVNPD0Me/lhoiqT9FrSTZWbggLy8ZanTn/CcaWORMJJi - gzDNED9UU/YAsXg4NYE376zKDuZWhUUwAmsjOlMksILIhiSCU0oH59AcXgLjKs9dtpCZP/t/IClr - wKfnm6CgHO2KjnB5kvCsSEmBFiciPkzzLJYGn1PYON/8/9bLXqeu65NjEl/Z+mNRP65565DWVYiN - QFcQbXbYZxDDgmjfYm85w4bOKkesGKkenT/o9jMnUksrbO85MVL1f77riYTvmK6w0mWuwmBxPxaD - nPZc2+D6uB2GHhh4bkYdgtQtaiKOSbdKjGVba1eLLzODYlrxwOZ6I1bJNvP0HoxHkpVoLacF8MgV - CODZzXo/MdVbDDT/5KHgBrXCvRUquhbpNNC7Ut5kvb5O9svvWrr6x0/S0oq8E3JYGD3/tZrErA== - - DELETED client/pkgd.settings.eagle Index: client/pkgd.settings.eagle ================================================================== --- client/pkgd.settings.eagle +++ client/pkgd.settings.eagle @@ -1,17 +0,0 @@ -############################################################################### -# -# pkgd.settings.eagle -- -# -# Extensible Adaptable Generalized Logic Engine (Eagle) -# Package Downloader Client Settings -# -# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. -# -# See the file "license.terms" for information on usage and redistribution of -# this file, and for a DISCLAIMER OF ALL WARRANTIES. -# -# RCS: @(#) $Id: $ -# -############################################################################### - -variable quiet false DELETED client/pkgd.settings.eagle.harpy Index: client/pkgd.settings.eagle.harpy ================================================================== --- client/pkgd.settings.eagle.harpy +++ client/pkgd.settings.eagle.harpy @@ -1,50 +0,0 @@ - - - - None - Mistachkin Systems - 88bd4979-4392-42f3-a0e6-a6d2bb881e55 - SHA512 - Script - 2016-08-19T02:27:08.9736797Z - -1.00:00:00 - 0x2c322765603b5278 - - P36p/qj9tAsoQPeWuc9Pk3g0O51CX9d/AIsxoMlrjtIreeCFn1Ki3x2KA3Jp7CKuShzSd7ZVbtGD - 4X7uNtPyoiRS3NitWI/hyR0wEN+tWqK+ImLatg4ZUjYCgbqJcGgTWsQZuJqj9X6a7nRhMrH4rJAB - uu92jWB/AHIcGTyMY/1Uw1u5/M3K2kePYnKIkZTmuNndrLBJa525osq3OeiowCshJE2ao+Yjx1RW - whTgplY5hBrgZVLnsSF/hXEXFCamsr7ZCHRjj99pX+7ycMUyyIIm+kVWY/OS9VjO+Mhu2Adq+JBu - 266BF7hDvDReEk5musXDPhZJD//ZS8blH+x+YxGHGfjw88ij9ISudRLkuv53UaGyXRLEyWvaCAHq - WSfiuaHgHSGTGPlXYWJBeQJO4r4NMs8kp/+cTL9HLwGN5UQpQ+CabnKnuGALrZdKwdojr/cS/QPK - znw85PdV2v0cGsHcGy/1qPo84pvtCX1D9aq5djmwhh4Wkg0saXAdJyr0XyNH8xniJ97hHO621QfD - BJpEtI5hOn9F74887mWsRb81zDpi66CgkFNQwgLY4tsJMulGc2inNC8Z4ZcPZHWUfSC9M1RfwlvL - 8eZ4HVK+IlGJPZpWS4qbca+ph25Uu3EPnzHLzqHiEHXd/ybWeTvQyorqvBCsjVOQ0HOv7GC11+6+ - RrO92mnX0+u69guvosE/H+G59pXsTPTW8/mGHSvsRV0lihILje3bBUCpWZk8Vaa0HmH9i/hkDicY - bs1LoTyfVRabBUIfLOujSnszlqVfc2EGZXdccVuBolsonVddzr+czcL+1SzSLtE2m7lsTgE/1mUj - oGlFhUekWPtfHw/nHEHQ09XZHPsQ5E6uCw2sI0mYDtTa6sGTk1H3eWVEwOTFheqrAx+kyAAk6Rcp - 18mg6QBaieARwUFXwdA7KT7tq7yDlB9x3yjem3aFLKWAszwqxA9RT+6WrdgcCKucWrE3gu3EZz+M - GzZ9s87hsvah/6lATnPia11vfGJKiEkTS5WI7uTDwAY1Iru9+qyTxFqQLQzhxtz+p186VOvYbotU - lZHXnl+bs1gzaUHnOSrSCiuMURaEjLfKBeySuHdzbBe8g5H6SISCujx2t7D7g/ZyBH0Lcfs1i1Wf - awwzORvocDdoQbKV8rO/U+CPxLkvqyuhmDi/LJi0os3k9iTKYN5M7WS0hKitxKGslRf0eiV6pzFB - yEDRUy5NlKwgz2Yi8dAJS2cr6O54NWCI7adW4L3PRL/La/2lISMS6oQgKe1NR6qmAiAECkqXDlHb - uESRIJ0JRYNHgLwYPqGS0ZWTZ5yQGAitOcO2GXvbL8O3hXpLalLF8ePvGzCZZMuWxspMCk8D2g== - - DELETED client/pkgr.eagle Index: client/pkgr.eagle ================================================================== --- client/pkgr.eagle +++ client/pkgr.eagle @@ -1,1567 +0,0 @@ -############################################################################### -# -# pkgr.eagle -- -# -# Extensible Adaptable Generalized Logic Engine (Eagle) -# Package Repository Client -# -# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. -# -# See the file "license.terms" for information on usage and redistribution of -# this file, and for a DISCLAIMER OF ALL WARRANTIES. -# -# RCS: @(#) $Id: $ -# -############################################################################### - -# -# 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 ::PackageRepository { - # - # NOTE: This package absolutely requires the Eagle core script library - # package, even when it is being used by native Tcl. If needed, - # prior to loading this package, the native Tcl auto-path should - # 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 " " $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} - 3 {set codeString break} - 4 {set codeString continue} - default {set codeString [appendArgs unknown( $code )]} - } - - if {[string length $result] > 0} then { - return [appendArgs $codeString ": " [list $result]] - } 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 - - if {[info exists tcl_version] && $tcl_version >= 8.5} then { - return [string is list $value] - } elseif {[catch {llength $value}] == 0} then { - return true - } else { - 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 - } 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 - - if {[info exists env(PKGR_TEMP)]} then { - set directory $env(PKGD_TEMP) - } elseif {[info exists env(TEMP)]} then { - set directory $env(TEMP) - } elseif {[info exists env(TMP)]} then { - set directory $env(TMP) - } else { - error "please set PKGR_TEMP (via environment) to temporary directory" - } - - set counter [expr {[pid] ^ int(rand() * 0xFFFF)}] - - while {1} { - set fileNameOnly [format tcl%04X.tmp $counter] - set fileName [file join $directory $fileNameOnly] - - if {![file exists $fileName]} then { - return $fileName - } - - 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 \"] - - if {[catch { - eval exec -success Success [subst $pgpCommand] - }] == 0} then { - return true - } - } else { - if {[catch { - eval exec [subst $pgpCommand] 2>@1 - }] == 0} then { - return true - } - } - - 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] - } - - global env - set varName [string trim $varName :] - - if {[info exists env($varName)]} then { - return $env($varName) - } - - return [list]; # NOTE: System default, which is "public-only". - } - - proc getLookupBaseUri {} { - set varName [appendArgs [getLookupVarNamePrefix] base_uri] - - if {[info exists $varName]} then { - return [set $varName] - } - - global env - set varName [string trim $varName :] - - if {[info exists env($varName)]} then { - return $env($varName) - } - - 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 "" - } - - # - # NOTE: Build the HTTP request URI using the specified query parameter - # values, escaping them as necessary. Also, include the standard - # query parameters with constant values for this request type. - # - if {[isEagle]} then { - return [appendArgs \ - $baseUri ?raw=1&method=lookup&apiKey= [uri escape uri $apiKey] \ - &package= [uri escape uri $package] &version= [uri escape uri \ - $version]] - } else { - package require http 2.0 - - return [appendArgs \ - $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 packageRequirementToVersion { requirement } { - set result $requirement - - if {[set index [string first - $result]] != -1} then { - incr index -1; set result [string range $result 0 $index] - } - - if {[set index [string first a $result]] != -1 || \ - [set index [string first b $result]] != -1} then { - incr index -1; set result [string range $result 0 $index] - } - - if {$result eq "0"} then { - set result "" - } elseif {[regexp -- {^\d+$} $result]} then { - append result .0 - } - - return $result - } - - # - # 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. All line-endings are normalized to Unix-style; therefore, - # all script signatures must assume this. - # - proc getLookupData { apiKey package version } { - variable verboseUriDownload - - set uri [getLookupUri $apiKey $package $version] - - if {[string length $uri] == 0} then { - return "" - } - - if {$verboseUriDownload} then { - pkgLog [appendArgs \ - "attempting to download URI \"" $uri \"...] - } - - if {[isEagle]} then { - set data [uri download -inline $uri] - } else { - set quiet [expr {!$verboseUriDownload}] - set data [getFileViaHttp $uri 10 stdout $quiet] - } - - if {$verboseUriDownload} then { - pkgLog [appendArgs \ - "raw response data is: " $data] - } - - set data [string map [list <\; < >\; > "\; \" &\; &] $data] - set data [string map [list \r\n \n \r \n] $data] - 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 was stolen from the "common.tcl" script used by the - # package repository server. It has been modified to support both - # native Tcl and Eagle. It should be noted here that TIP #268 syntax - # is not supported by Eagle. For native Tcl, the requirement argument - # must be a package version or requirement conforming to the TIP #268 - # syntax. For Eagle, the requirement argument must be a simple dotted - # package version, with up to four components, without any 'a' or 'b'. - # The emptyOk argument should be non-zero if an empty string should be - # considered to be valid by the caller. The rangeOk argument should - # be non-zero if the version range syntax is allowed; this argument is - # ignored for Eagle because it requires TIP #268 support. - # - proc isValidPackageRequirement { requirement rangeOk {emptyOk false} } { - if {$emptyOk && [string length $requirement] == 0} then { - return true - } - - if {[isEagle]} then { - # - # NOTE: Eagle does not support TIP #268. Use the built-in sub-command - # that checks a version number. - # - return [string is version -strict $requirement] - } else { - # - # HACK: If a version range is not allowed, make sure that the dash - # character is not present. - # - if {!$rangeOk && [string first - $requirement] != -1} then { - return false - } - - # - # HACK: There is no direct way to check if a package requirement - # that uses the TIP #268 syntax is valid; however, we can - # purposely "misuse" the [package present] command for this - # purpose. We know the "Tcl" package is always present; - # therefore, if an error is raised here, then the package - # requirement is probably invalid. Unfortunately, the error - # message text has to be checked as well; otherwise, there - # is no way to verify version numbers that happen to be less - # than the running patch level of Tcl. - # - if {[catch {package present Tcl $requirement} error] == 0} then { - return true - } else { - # - # TODO: Maybe this will require updates in the future? - # - set pattern(1) "expected version number but got *" - set pattern(2) "expected versionMin-versionMax but got *" - - if {![string match $pattern(1) $error] && \ - ![string match $pattern(2) $error]} then { - return true - } else { - return false - } - } - } - } - - # - # 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 returned patch level. It cannot be an empty string - # and it must conform to the TIP #268 requirements for a single - # package version. - # - set patchLevel [getDictionaryValue $result PatchLevel] - - if {[string length $patchLevel] == 0} then { - error "missing patch level" - } - - if {![isValidPackageRequirement $patchLevel false]} then { - error "bad patch level" - } - - # - # NOTE: Grab the language for the package script. It must be an empty - # string, "Tcl", or "Eagle". If it is an empty string, "Eagle" - # will be assumed. - # - set language [getDictionaryValue $result Language] - - if {[lsearch -exact [list "" Tcl Eagle] $language] == -1} then { - error "unsupported language" - } - - # - # NOTE: Grab the package script. If it is an empty string, then the - # package cannot be loaded and there is nothing to do. In that - # case, just raise an error. - # - set script [getDictionaryValue $result Script] - - if {[string length $script] == 0} then { - error "missing script" - } - - # - # NOTE: Grab the package script certificate. If it is an empty string - # then the package script is unsigned, which is not allowed by - # this client. In that case, just raise an error. - # - set certificate [getDictionaryValue $result Certificate] - - if {[string length $certificate] == 0} then { - error "missing script certificate" - } - - # - # NOTE: Are we being called from the [package unknown] handler - # in "strict" mode? - # - if {$strictUnknownLanguage && $caller eq "handler"} then { - # - # NOTE: If so, the package script must be targeted at the this - # language; otherwise, there exists the possibility that - # the package may not be provided to this language. - # - if {[isEagle]} then { - if {$language ne "Eagle"} then { - error "repository package is not for Eagle" - } - } else { - if {$language ne "Tcl"} then { - error "repository package is not for Tcl" - } - } - } - - # - # 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(patchLevel) $patchLevel - set metadata(language) $language - 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. - # - if {![isEagle]} then { - error "already running in Tcl language" - } - - 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. - # - if {[isEagle]} then { - error "already running in Eagle language" - } - - 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. - # - if {[isEagle] && [llength [info commands object]] > 0} then { - if {[catch { - object invoke -flags +NonPublic Interpreter.GetActive HasSecurity - } security] == 0 && $security} then { - return true - } - } - - 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 { - error "bad metadata" - } - - # - # NOTE: This procedure requires that the metadata array variable is - # present in the call frame immediately above this one. - # - upvar 1 $varName metadata - - # - # NOTE: If the entire package metadata array is missing, fail. - # - if {![info exists metadata]} then { - error "missing metadata" - } - - # - # NOTE: If the patch level for the package is mising, fail. - # - if {![info exists metadata(patchLevel)]} then { - error "missing patch level" - } - - # - # NOTE: If the language for the package script is mising, fail. - # - if {![info exists metadata(language)]} then { - error "missing language" - } - - # - # NOTE: If the package script is mising, fail. - # - if {![info exists metadata(script)]} then { - error "missing script" - } - - # - # NOTE: If the package script certificate is mising, fail. - # - if {![info exists metadata(certificate)]} then { - error "missing script certificate" - } - - # - # NOTE: Create common cleanup script block that deletes any temporary - # files created for the script verification process. - # - set script(cleanup) { - if {[string length $fileName(2)] > 0 && \ - [file exists $fileName(2)] && [file isfile $fileName(2)]} then { - if {![info exists ::env(pkgr_keep_files)]} then { - catch {file delete $fileName(2)} - } - unset -nocomplain fileName(2) - } - - if {[string length $fileName(1)] > 0 && \ - [file exists $fileName(1)] && [file isfile $fileName(1)]} then { - if {![info exists ::env(pkgr_keep_files)]} then { - catch {file delete $fileName(1)} - } - unset -nocomplain fileName(1) - } - } - - # - # NOTE: Figure out the "type" of script certificate we are now dealing - # with. - # - if {[isHarpyCertificate $metadata(certificate)]} then { - # - # NOTE: Attempt to create a completely unique array variable name to - # hold the package metadata in this scripting language as well - # as possibly in the other necessary scripting language(s). - # - set newVarName(1) [appendArgs \ - [getLookupVarNamePrefix] metadata_ [getLookupVarNameSuffix]] - - set newVarName(2) [appendArgs \ - [getLookupVarNamePrefix] cleanup_ [getLookupVarNameSuffix]] - - set newProcName(1) [appendArgs \ - [getLookupVarNamePrefix] eagleHasSecurity_ [getLookupVarNameSuffix]] - - set newProcName(2) [appendArgs \ - [getLookupVarNamePrefix] getFileTempName_ [getLookupVarNameSuffix]] - - set newProcName(3) [appendArgs \ - [getLookupVarNamePrefix] tclMustBeReady_ [getLookupVarNameSuffix]] - - # - # NOTE: Create the Eagle script block that will be used to securely - # evaluate a signed package script. This must be evaluated in - # Eagle because it uses several plugins only available there. - # - set script(outer) [string map [list \ - %metadata% $newVarName(1) %cleanup% $newVarName(2) \ - %eagleHasSecurity% $newProcName(1) %getFileTempName% \ - $newProcName(2) %tclMustBeReady% $newProcName(3)] { - try { - # - # NOTE: If there is no package script, there is nothing we - # can do here. - # - if {[string length ${%metadata%(script)}] > 0} then { - # - # NOTE: Save the security state for the interpreter. Then, attempt - # to enable it. This will fail if one of the needed plugins - # cannot be loaded. - # - set savedSecurity [{%eagleHasSecurity%}] - if {!$savedSecurity} then {source enableSecurity} - - try { - # - # NOTE: Figure out temporary file name for the downloaded script - # and its associated script certificate. - # - set fileName(1) [{%getFileTempName%}] - set fileName(2) [appendArgs $fileName(1) .harpy] - - try { - # - # NOTE: Write downloaded script to a temporary file. - # - writeFile $fileName(1) ${%metadata%(script)} - - # - # NOTE: Write downloaded script certificate to a temporary - # file. - # - if {[string length ${%metadata%(certificate)}] > 0} then { - writeFile $fileName(2) ${%metadata%(certificate)} - } - - # - # NOTE: This seems stupid. Why are we reading the downloaded - # script from the temporary file when we already had it - # in memory? The reason is that we need to make sure - # that the Harpy policy engine has a chance to check the - # downloaded script against its associated certificate. - # This will raise a script error if the script signature - # is missing or invalid. - # - set script(inner) [interp readorgetscriptfile -- \ - "" $fileName(1)] - - # - # NOTE: Determine the target language for the package script, - # which may or may not be the language that is currently - # evaluating this script (Eagle). The default language, - # when one was not explicitly specified, is Eagle. In - # the future, this may be changed, e.g. to use the file - # extension of the client script. - # - switch -exact -- ${%metadata%(language)} { - "" - - Eagle { - # - # NOTE: The target language is Eagle, which is evaluating - # this script. No special handling is needed here. - # - return [uplevel #0 $script(inner)] - } - Tcl { - # - # NOTE: The target language is Tcl; therefore, a bit of - # special handling is needed here. - # - {%tclMustBeReady%}; return [tcl eval [tcl master] [list \ - uplevel #0 $script(inner)]] - } - default { - error "unsupported language" - } - } - } finally { - # - # NOTE: Perform any necessary cleanup steps. - # - eval ${%cleanup%} - } - } finally { - # - # NOTE: Restore the saved security state for the interpreter. - # - if {!$savedSecurity} then {source disableSecurity} - unset -nocomplain savedSecurity - } - } - } finally { - rename {%tclMustBeReady%} "" - rename {%getFileTempName%} "" - rename {%eagleHasSecurity%} "" - - unset -nocomplain {%cleanup%} - unset -nocomplain {%metadata%} - } - }] - - # - # NOTE: Copy the package metadata into the fresh array variable, - # if necessary, marshalling it from native Tcl to Eagle. - # - if {[isEagle]} then { - array set $newVarName(1) [array get metadata] - set $newVarName(2) $script(cleanup) - - proc $newProcName(1) {} [info body [appendArgs \ - [namespace current] ::eagleHasSecurity]] - - proc $newProcName(2) {} [info body [appendArgs \ - [namespace current] ::getFileTempName]] - - proc $newProcName(3) {} [info body [appendArgs \ - [namespace current] ::tclMustBeReady]] - - return [eval $script(outer)] - } else { - eagleMustBeReady - - eagle [list array set $newVarName(1) [array get metadata]] - eagle [list set $newVarName(2) $script(cleanup)] - - eagle [list proc $newProcName(1) {} [info body [appendArgs \ - [namespace current] ::eagleHasSecurity]]] - - eagle [list proc $newProcName(2) {} [info body [appendArgs \ - [namespace current] ::getFileTempName]]] - - eagle [list proc $newProcName(3) {} [info body [appendArgs \ - [namespace current] ::tclMustBeReady]]] - - return [eagle $script(outer)] - } - } elseif {[isPgpSignature $metadata(certificate)]} then { - # - # NOTE: If there is no package script, there is nothing we - # can do here. - # - if {[string length $metadata(script)] > 0} then { - # - # NOTE: Figure out temporary file name for the downloaded script - # and its associated PGP signature. - # - set fileName(1) [getFileTempName] - set fileName(2) [appendArgs $fileName(1) .asc] - - # - # NOTE: Write downloaded script to a temporary file. - # - writeFile $fileName(1) $metadata(script) - - # - # NOTE: Write downloaded script PGP signature a temporary file. - # - if {[string length $metadata(certificate)] > 0} then { - writeFile $fileName(2) $metadata(certificate) - } - - # - # NOTE: Attempt to verify the PGP signature for the package script. - # - if {[verifyPgpSignature $fileName(2)]} then { - # - # NOTE: Delete the temporary files that we created for the PGP - # signature verification. - # - eval $script(cleanup) - } else { - # - # NOTE: Delete the temporary files that we created for the PGP - # signature verification. - # - eval $script(cleanup) - - # - # NOTE: PGP signature verification failed. Raise an error and - # do not proceed with evaluating the package script. - # - error "bad PGP signature" - } - - # - # NOTE: The PGP signature was verified; use the downloaded package - # script verbatim. - # - set script(inner) $metadata(script) - - # - # NOTE: Determine the target language for the package script, which - # may or may not be the language that is currently evaluating - # this script (Eagle). The default language, when one was not - # explicitly specified, is Eagle. In the future, this may be - # changed, e.g. to use the file extension of the client script. - # - switch -exact -- $metadata(language) { - "" - - Eagle { - if {[isEagle]} then { - return [uplevel #0 $script(inner)] - } else { - eagleMustBeReady - - return [eagle [list uplevel #0 $script(inner)]] - } - } - Tcl { - if {[isEagle]} then { - tclMustBeReady; return [tcl eval [tcl master] [list \ - uplevel #0 $script(inner)]] - } else { - return [uplevel #0 $script(inner)] - } - } - default { - error "unsupported language" - } - } - } - } 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 - - if {$autoRequireGaruda && ![isEagle]} then { - # - # TODO: Assume this package is trusted? How can we verify it - # at this point? - # - package require Garuda - } - - if {$autoLoadTcl && [isEagle]} then { - # - # NOTE: Load a native Tcl library. It must be signed with a valid - # Authenticode signature. - # - tcl load -findflags +TrustedOnly -loadflags +SetDllDirectory - } - - if {$autoHook && ![isPackageUnknownHandlerHooked]} then { - # - # NOTE: Install our [package unknown] handler and save the original - # one for our use as well. - # - 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" - } - - 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" - } - - 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. - # - set varName [appendArgs [getLookupVarNamePrefix] saved_package_unknown] - set oldHandler [expr {[info exists $varName] ? [set $varName] : ""}] - - if {[string length $oldHandler] > 0} then { - lappend oldHandler $package $version; uplevel #0 $oldHandler - } - } - - # - # 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 - - # - # NOTE: First, run our [package unknown] handler. - # - set code(1) [catch {main $package $version handler} result(1)] - - if {$verboseUnknownResult} then { - pkgLog [appendArgs \ - "repository handler results for package \"" [formatPackageName \ - $package $version] "\" are " [formatResult $code(1) $result(1)]] - } - - # - # NOTE: Next, run the saved [package unknown] handler. - # - set code(2) [catch { - runSavedPackageUnknownHandler $package $version - } result(2)] - - if {$verboseUnknownResult} then { - pkgLog [appendArgs \ - "saved handler results for package \"" [formatPackageName \ - $package $version] "\" are " [formatResult $code(2) $result(2)]] - } - - # - # NOTE: Maybe check for the package and then optionally log results. - # - if {$verboseUnknownResult} then { - set ifNeededVersion [getIfNeededVersion \ - $package [packageRequirementToVersion $version]] - - if {[string length $ifNeededVersion] > 0} then { - set command [list package ifneeded $package $ifNeededVersion] - - if {[catch $command result(3)] == 0 && \ - [string length $result(3)] > 0} then { - pkgLog [appendArgs \ - "package script for \"" [formatPackageName $package \ - $ifNeededVersion] "\" was added: " [list $result(3)]] - } else { - pkgLog [appendArgs \ - "package script for \"" [formatPackageName $package \ - $ifNeededVersion] "\" was not added: " [list $result(3)]] - } - } else { - pkgLog [appendArgs \ - "package script for \"" [formatPackageName $package \ - $ifNeededVersion] "\" was not added"] - } - - set command [list package present $package] - if {[string length $version] > 0} then {lappend command $version} - - if {[catch $command] == 0} then { - pkgLog [appendArgs \ - "package \"" [formatPackageName $package $version] \ - "\" was loaded"] - } else { - pkgLog [appendArgs \ - "package \"" [formatPackageName $package $version] \ - "\" 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 - } - - set fileName [appendArgs \ - [file rootname $script] .settings [file extension $script]] - - 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: Automatically install our [package unknown] handler when this - # package is loaded? - # - variable autoHook; # DEFAULT: true - - if {![info exists autoHook]} then { - set autoHook true - } - - # - # NOTE: Automatically [tcl load] when this package is loaded from the - # Eagle language? - # - variable autoLoadTcl; # DEFAULT: true - - if {![info exists autoLoadTcl]} then { - set autoLoadTcl true - } - - # - # NOTE: Automatically [package require Garuda] when this package is - # loaded from the Tcl language? - # - variable autoRequireGaruda; # DEFAULT: true - - if {![info exists autoRequireGaruda]} then { - set autoRequireGaruda true - } - - # - # NOTE: The command to use when verifying OpenPGP signatures for the - # downloaded package scripts. - # - variable pgpCommand; # DEFAULT: gpg2 --verify {${fileName}} - - if {![info exists pgpCommand]} then { - set pgpCommand {gpg2 --verify {${fileName}}} - } - - # - # NOTE: Verify that the package script matches the current language - # when called from the [package unknown] handler? - # - variable strictUnknownLanguage; # DEFAULT: true - - if {![info exists strictUnknownLanguage]} then { - set strictUnknownLanguage true - } - - # - # NOTE: Emit diagnostic messages when a [package unknown] handler - # is called? - # - variable verboseUnknownResult; # DEFAULT: false - - if {![info exists verboseUnknownResult]} then { - set verboseUnknownResult false - } - - # - # NOTE: Emit diagnostic messages when a URI is fetched? - # - variable verboseUriDownload; # DEFAULT: false - - 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. - # - set apiKeys [getLookupApiKeys]; lappend apiKeys "" - - foreach apiKey $apiKeys { - # - # NOTE: Issue the lookup request to the remote package repository. - # - set data [getLookupData $apiKey $package $version] - - # - # NOTE: Attempt to grab the lookup code from the response data. - # - set code [getLookupCodeFromData $data] - - # - # NOTE: Did the lookup operation succeed? If so, stop trying - # other API keys. - # - if {[isLookupCodeOk $code]} then { - break - } - } - - # - # NOTE: Attempt to grab the lookup data from the response data. - # Upon failure, this should contain the error message. - # - set result [getLookupResultFromData $data] - - # - # NOTE: Did the lookup operation fail? - # - if {![isLookupCodeOk $code]} then { - # - # NOTE: Is there an error message? - # - if {[string length $result] > 0} then { - # - # NOTE: Yes. Use the returned error message verbatim. - # - error $result - } else { - # - # NOTE: No. Use the whole response data string as the error - # message. - # - error $data - } - } - - # - # NOTE: Process the lookup data into the pieces of metadata that we - # need to load the requested package. - # - extractAndVerifyLookupMetadata $result metadata $caller - - # - # NOTE: Attempt to load the requested package using the metadata - # extracted in the previous step. - # - processLookupMetadata metadata - } - - if {![isEagle]} then { - ########################################################################### - ############################# BEGIN Tcl ONLY ############################## - ########################################################################### - - # - # 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, if requested. - # - if {$milliseconds > 0} then { - set afterForPageProgress [after $milliseconds \ - [namespace code [list pageProgress $channel $type \ - $milliseconds]]] - } else { - unset -nocomplain afterForPageProgress - } - } - - # - # 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 - - # - # NOTE: If the 'tls' package is available, always attempt to use HTTPS. - # - if {[catch {package require tls}] == 0} then { - ::http::register https 443 ::tls::socket - - if {[string range $uri 0 6] eq "http://"} then { - set uri [appendArgs https:// [string range $uri 7 end]] - } - } - - # - # NOTE: Unless the caller forbids it, display progress messages during - # the download. - # - if {!$quiet} then { - pageProgress $channel . 250 - } - - # - # NOTE: All downloads are handled synchronously, which is not ideal; - # however, it is simple. Keep going as long as there are less - # than X redirects. - # - set redirectCount 0 - - while {1} { - # - # NOTE: Issue the HTTP request now, grabbing the resulting token. - # - set token [eval [list ::http::geturl $uri] $args] - - # - # NOTE: Check the HTTP response code, in order to follow any HTTP - # redirect responses. - # - switch -exact -- [http::ncode $token] { - 301 - - 302 - - 303 - - 307 { - # - # NOTE: Unless the caller forbids it, display progress messages - # when an HTTP redirect is returned. - # - if {!$quiet} then { - pageProgress $channel > 0 - } - - # - # NOTE: We hit another HTTP redirect. Stop if there are more - # than X. - # - incr redirectCount - - # - # TODO: Maybe make this limit configurable? - # - if {$redirectCount > $redirectLimit} then { - # - # NOTE: Just "give up" and return whatever data that we have - # now. - # - set data [::http::data $token] - ::http::cleanup $token; break - } - - # - # NOTE: Grab the metadata associated with this HTTP response. - # - array set meta [::http::meta $token] - - # - # NOTE: Is there actually a new URI (location) to use? - # - if {[info exist meta(Location)]} then { - # - # NOTE: Ok, grab it now. Later, at the top of the loop, - # it will be used in the subsequent HTTP request. - # - set location $meta(Location); unset meta - - # - # NOTE: For security, do NOT follow an HTTP redirect if - # it attempts to redirect from HTTPS to HTTP. - # - if {[string range $uri 0 7] eq "https://" && \ - [string range $location 0 7] ne "https://"} then { - # - # NOTE: Just "give up" and return whatever data that - # we have now. - # - set data [::http::data $token] - ::http::cleanup $token; break - } - - # - # NOTE: Replace the original URI with the new one, for - # use in the next HTTP request. - # - set uri $location - - # - # NOTE: Cleanup the current HTTP token now beause a new - # one will be created for the next request. - # - ::http::cleanup $token - } else { - # - # NOTE: Just "give up" and return whatever data that we - # have now. - # - set data [::http::data $token] - ::http::cleanup $token; break - } - } - default { - # - # NOTE: Ok, the HTTP response is actual data of some kind - # (which may be an error); however, it is not any - # kind of supported HTTP redirect. - # - 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} - unset -nocomplain afterForPageProgress - } - - # - # NOTE: If progress messages were emitted, start a fresh line. - # - if {!$quiet} then { - catch {puts $channel [appendArgs " " $uri]; flush $channel} - } - - return $data - } - - ########################################################################### - ############################## END Tcl ONLY ############################### - ########################################################################### - } - - # - # NOTE: Attempt to read optional settings file now. This may override - # one or more of the variable setup in the next step. - # - maybeReadSettingsFile [info script] - - # - # NOTE: Setup the variables, within this namespace, used by this script. - # - setupPackageUnknownVars - - # - # NOTE: Setup for our [package unknown] handler, which may involve a few - # different operations. - # - setupPackageUnknownHandler - - # - # NOTE: Provide the package to the interpreter. - # - package provide Eagle.Package.Repository \ - [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}] -} - DELETED client/pkgr.eagle.harpy Index: client/pkgr.eagle.harpy ================================================================== --- client/pkgr.eagle.harpy +++ client/pkgr.eagle.harpy @@ -1,50 +0,0 @@ - - - - None - Mistachkin Systems - 88d42a28-1e95-4dd7-aaf9-11bb262f10d0 - SHA512 - Script - 2016-08-19T02:47:51.4043437Z - -1.00:00:00 - 0x2c322765603b5278 - - Mn+rsBh675oM30+X6J/Myzrc0MmxmLCjpzV4bDcl8nZcbdSXszHTHE9ma5tAXopb05bMomy5lHal - CjEGgYubJtQFcQzuKlxp0UMVgMpK28uTS/ik9RSKXwgq83N1pwvM7cmF2RzxF/fmD/0dtb0Ulc+h - Ior9NeJcpD6lBAE3XEB288f+79mA3U2X1io4qLYvFzktpKyjen8pC8J46078b3HXSoYGUHehmZo+ - EJhVhD0Lfb9XtGh4V9hgmL9aMWJdv/jGmq+tKOJxxpU70avW4aaUzDKZE/zgR674/o2jhTw8LC+P - 7Ed5UhgnXXr6Ko0HlIZqWwwblP+/WJ91Rf3DBzlJDG1Wjwku2xAQN2JcLipbn0YGG3jr4qx9yrnw - /K1HT0CEWW/41F/LeZAZ36Kao76kGcl4OcamgAW4fPp2c85wRyIh3i6f4t1RxgixgVUuMWhbVVu5 - Fb/opbLwHBLIGQpmYqmZhz6A97CSr5eyj1CpKEAz/v76ma3qgravdVZ59C5NdhPXHQGS5MpgsWUC - tHc5aXK9npgN3femt1czY8J+dLMFP0N4ENlqJNRP14zFOd0a2vNnc6KB8OE4GAdL0V1KaAK2WIOQ - h5cPFMKSphWT8cst4/nLbOhs9G8JlXD1PsIKxgGW5YSYutkZQJPUcDMFmSEdaQ6CCc1K+o6SEXvS - RcdPzCEDwJmgYUF77ILI0whNBNFSVD+UPcoD1j6KUmJKHhOt63EYVmRUFlYfw1afVeCjrgm2q5Tp - aSVYPaoqUlBuZ0lomqUD03/XsqdwVdiZXEuoObr4INoMeZnHyQf5wpLl1ZGBvGdc7ujOkU8y/sVX - Y9ATG3czSvQlNz/06J/ghVEK1t7ZNyEe0thwj6AHM52D/GuFTmfnFaBS5HQawOE9FkYppA7x+65Z - rZjUaIBEaybEYPok29IKqw+aqA2s21gJ9c70d/M7UlpwGbT9CQqV+o6/2frQF6vSPUhrFsZPCZZs - hE7hn/jluR+tT0g3awKqWayNfG5/ZfJruKwmXcipeacr42Affi0zNxuxsMglndGGEKFtsrGySjcF - 2NCquShXYNz4i/7jh0IO4Udb5t/PP1Brpp26t35/Oug/2i2eTO5gq4MvsffXvjeEPYWPjUBug0y1 - HNAmHDDUqLoD3nK3AK+em1ukGdjMEsvlz+L9+IjOJ/po5ypkgNIsNqQITBY0S4ofp1XO2o9IHPGN - G0qKBB7G2PcGe7hh9FOAyPL81OYpxYc7Pe80zxqu+KZP6OPalBssNqIIHqj2p03cukS5X8U0QPO+ - 2f7Iv9SxJ0t9pcKyZX2iHx5H9+u0TpsghdQPiu9u63GUnIkMJfEWUoBxfJNfWuIzuMoe3rugJQ== - - DELETED client/pkgr.settings.eagle Index: client/pkgr.settings.eagle ================================================================== --- client/pkgr.settings.eagle +++ client/pkgr.settings.eagle @@ -1,22 +0,0 @@ -############################################################################### -# -# pkgr.settings.eagle -- -# -# Extensible Adaptable Generalized Logic Engine (Eagle) -# Package Repository Client Settings -# -# Copyright (c) 2007-2012 by Joe Mistachkin. All rights reserved. -# -# See the file "license.terms" for information on usage and redistribution of -# this file, and for a DISCLAIMER OF ALL WARRANTIES. -# -# 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 DELETED client/pkgr.settings.eagle.harpy Index: client/pkgr.settings.eagle.harpy ================================================================== --- client/pkgr.settings.eagle.harpy +++ client/pkgr.settings.eagle.harpy @@ -1,50 +0,0 @@ - - - - None - Mistachkin Systems - b44b4ef2-76ad-4786-b4b7-e1d604e15e8b - SHA512 - Script - 2016-08-17T22:22:56.6608906Z - -1.00:00:00 - 0x2c322765603b5278 - - efFtWFrnFkJeMBafjl1MxA1lygnogjoQHoS8qP4ptK+GuB7mvyJbSgtjYyUVJ0Nj9W88qJ0eGtQ3 - 5yd2fZrTX7nXmEkXaRFcSBiB/S6dtUrc6GGDO1PlOB7w3JGfSkh/4MgfRTrZyN+sRN6ODWPjFU4s - m1+HXSXynBUKyPaJ8qNd/0foXvNC/rGbHyZ+Z4FkbAfYZZSTiIz57kv9ZvlmxjSoZNffYpOGD0kQ - mFXkvDkoM4JxE3b19M/VFcKJ6NIn7o6Vqc3Xt40+9W5OTKjLVfpeV8gK01rU1d9+KuBnC3hQWyqZ - V0fF2mkyGnxO0LAZpOiQ82b8Myld/vl79iGcMLb9F5+SdnYeqBrT8VFRaiZz12MS+8BP+3JItBqn - WNFjKVWFXG5SI6aTaqbTDtiKRADqoJyb6HN5imfZll2tcK2ruICy4zYo79mDAolBEnwp4TnNcwKA - h5oQTnc44dQht8xeZdT5Ah2SNRBx0GA3nHmJO9AP68JMYbPWLABQQ6hZf2b6u6zB3ZzU8RN/+NIa - 3Y5jfKVD/f2BCJumU1jWh2R4785SNXfFT5Id5U0II9I9WhTdiylFXP3hiN/dNW0GAWq9v6TLG+pi - vfJrr/26RQfEwIiNa8keQkfJ/HgTmille05ct7DycbHu9Edq9NxWgfTtWDsAnlERTG+WhRc5hZyc - VegPWnHpoXeUHgEe5d4bkmmRfePnH+eWeibRveMErlv16jeTv4H28ftp3Vq8n0n+hkVHpYOHMV6i - J3/RhJ4G1Se/adEPdVUQRfYrP5HwI/szeR5DQoZ+zAsB34cfwWn0hsj21FeqitSE/rQ0j8pcD3yy - OIH07uf/sOqQ8hSEFnxVPTvQYp9H7QTBxFAxaMbpIUTpB6DyroPWzi4uSa+JXWSkf5sQOo2SLCoX - t3w7tpZ6o2FZNd3O519FiX3+cM78vyfIMkbT4ZFEHpFkccuG6g9nVgUDCTgMwNldSKQAC7JtwjOZ - tSQ9YjRTgZSGcJ+TjLnXJfWo8u0SM+J3K9o+zRcN+6zDZsCAo6inrxgICEFN+lTabJxyWas6MYeX - hkq7CnGT9nsDtynLniiF4FUsKTVlgHfR1ZVPwWGb0Ow/L4PdPMTlzPVttsE4bcrii6xnf85O9ijU - zfgBhDtRsst9TLXb514J8t4zT54AQPLav2DjXjDjChbm+/JF8ywAfq/v8/A0B3PIeDPgmRTouH48 - 0MFanzDYI3r75OC0ZMuS8EoseqkcC2C3IvJY9ZtsgnuIPbpxzsdooCCaHXUWL2mHZ+QOk8Q+jn7J - 1V0BeUNi0hU9SZeAai1co8ehsCw3vUkOPUoYginn2ry5ALf/jEBHW+1CX3+jlT/snSZba0GPDg== - -