Hex Artifact Content

Artifact 15db9f608a6ceabfee2bdd672adb10de43dff131:


0000: 23 21 20 2f 75 73 72 2f 62 69 6e 2f 65 6e 76 20  #! /usr/bin/env 
0010: 74 63 6c 73 68 0a 0a 70 61 63 6b 61 67 65 20 72  tclsh..package r
0020: 65 71 75 69 72 65 20 68 74 74 70 20 32 2e 37 0a  equire http 2.7.
0030: 70 61 63 6b 61 67 65 20 72 65 71 75 69 72 65 20  package require 
0040: 73 71 6c 69 74 65 33 0a 70 61 63 6b 61 67 65 20  sqlite3.package 
0050: 72 65 71 75 69 72 65 20 73 68 61 31 0a 70 61 63  require sha1.pac
0060: 6b 61 67 65 20 72 65 71 75 69 72 65 20 61 70 70  kage require app
0070: 66 73 64 0a 70 61 63 6b 61 67 65 20 72 65 71 75  fsd.package requ
0080: 69 72 65 20 70 6c 61 74 66 6f 72 6d 0a 70 61 63  ire platform.pac
0090: 6b 61 67 65 20 72 65 71 75 69 72 65 20 70 6b 69  kage require pki
00a0: 0a 0a 23 20 46 75 6e 63 74 69 6f 6e 73 20 73 70  ..# Functions sp
00b0: 65 63 69 66 69 63 61 6c 6c 79 20 6d 65 61 6e 74  ecifically meant
00c0: 20 66 6f 72 20 75 73 65 72 73 20 74 6f 20 72 65   for users to re
00d0: 70 6c 61 63 65 20 61 73 20 61 20 70 61 72 74 20  place as a part 
00e0: 6f 66 20 63 6f 6e 66 69 67 75 72 61 74 69 6f 6e  of configuration
00f0: 0a 6e 61 6d 65 73 70 61 63 65 20 65 76 61 6c 20  .namespace eval 
0100: 3a 3a 61 70 70 66 73 3a 3a 75 73 65 72 20 7b 0a  ::appfs::user {.
0110: 09 23 20 55 73 65 72 2d 72 65 70 6c 61 63 61 62  .# User-replacab
0120: 6c 65 20 66 75 6e 63 74 69 6f 6e 20 74 6f 20 63  le function to c
0130: 6f 6e 76 65 72 74 20 61 20 68 6f 73 74 6e 61 6d  onvert a hostnam
0140: 65 2f 68 61 73 68 2f 6d 65 74 68 6f 64 20 74 6f  e/hash/method to
0150: 20 61 6e 20 55 52 4c 0a 09 70 72 6f 63 20 63 6f   an URL..proc co
0160: 6e 73 74 72 75 63 74 5f 75 72 6c 20 7b 68 6f 73  nstruct_url {hos
0170: 74 6e 61 6d 65 20 68 61 73 68 20 6d 65 74 68 6f  tname hash metho
0180: 64 7d 20 7b 0a 09 09 72 65 74 75 72 6e 20 22 68  d} {...return "h
0190: 74 74 70 3a 2f 2f 24 68 6f 73 74 6e 61 6d 65 2f  ttp://$hostname/
01a0: 61 70 70 66 73 2f 24 6d 65 74 68 6f 64 2f 24 68  appfs/$method/$h
01b0: 61 73 68 22 0a 09 7d 0a 0a 09 23 20 55 73 65 72  ash"..}...# User
01c0: 2d 72 65 70 6c 61 63 65 61 62 6c 65 20 66 75 6e  -replaceable fun
01d0: 63 74 69 6f 6e 20 67 65 74 20 74 68 65 20 68 6f  ction get the ho
01e0: 6d 65 20 64 69 72 65 63 74 6f 72 79 20 6f 66 20  me directory of 
01f0: 74 68 65 20 63 75 72 72 65 6e 74 20 75 73 65 72  the current user
0200: 0a 09 70 72 6f 63 20 67 65 74 5f 68 6f 6d 65 64  ..proc get_homed
0210: 69 72 20 7b 7d 20 7b 0a 09 09 72 65 74 75 72 6e  ir {} {...return
0220: 20 5b 3a 3a 61 70 70 66 73 64 3a 3a 67 65 74 5f   [::appfsd::get_
0230: 68 6f 6d 65 64 69 72 5d 0a 09 7d 0a 7d 0a 0a 6e  homedir]..}.}..n
0240: 61 6d 65 73 70 61 63 65 20 65 76 61 6c 20 3a 3a  amespace eval ::
0250: 61 70 70 66 73 20 7b 0a 09 76 61 72 69 61 62 6c  appfs {..variabl
0260: 65 20 63 61 63 68 65 64 69 72 20 22 2f 74 6d 70  e cachedir "/tmp
0270: 2f 61 70 70 66 73 2d 63 61 63 68 65 22 0a 09 76  /appfs-cache"..v
0280: 61 72 69 61 62 6c 65 20 74 74 6c 20 33 36 30 30  ariable ttl 3600
0290: 0a 09 76 61 72 69 61 62 6c 65 20 6e 74 74 6c 20  ..variable nttl 
02a0: 36 30 0a 09 76 61 72 69 61 62 6c 65 20 74 72 75  60..variable tru
02b0: 73 74 65 64 5f 63 61 73 20 5b 6c 69 73 74 5d 0a  sted_cas [list].
02c0: 0a 09 70 72 6f 63 20 5f 68 61 73 68 5f 73 65 70  ..proc _hash_sep
02d0: 20 7b 68 61 73 68 20 7b 73 65 70 73 20 34 7d 7d   {hash {seps 4}}
02e0: 20 7b 0a 09 09 66 6f 72 20 7b 73 65 74 20 69 64   {...for {set id
02f0: 78 20 30 7d 20 7b 24 69 64 78 20 3c 20 24 73 65  x 0} {$idx < $se
0300: 70 73 7d 20 7b 69 6e 63 72 20 69 64 78 7d 20 7b  ps} {incr idx} {
0310: 0a 09 09 09 61 70 70 65 6e 64 20 72 65 74 76 61  ....append retva
0320: 6c 20 22 5b 73 74 72 69 6e 67 20 72 61 6e 67 65  l "[string range
0330: 20 24 68 61 73 68 20 5b 65 78 70 72 20 7b 24 69   $hash [expr {$i
0340: 64 78 20 2a 20 32 7d 5d 20 5b 65 78 70 72 20 7b  dx * 2}] [expr {
0350: 28 24 69 64 78 20 2a 20 32 29 20 2b 20 31 7d 5d  ($idx * 2) + 1}]
0360: 5d 2f 22 0a 09 09 7d 0a 09 09 61 70 70 65 6e 64  ]/"...}...append
0370: 20 72 65 74 76 61 6c 20 22 5b 73 74 72 69 6e 67   retval "[string
0380: 20 72 61 6e 67 65 20 24 68 61 73 68 20 5b 65 78   range $hash [ex
0390: 70 72 20 7b 24 69 64 78 20 2a 20 32 7d 5d 20 65  pr {$idx * 2}] e
03a0: 6e 64 5d 22 0a 0a 09 09 72 65 74 75 72 6e 20 24  nd]"....return $
03b0: 72 65 74 76 61 6c 0a 09 7d 0a 0a 09 70 72 6f 63  retval..}...proc
03c0: 20 5f 63 61 63 68 65 66 69 6c 65 20 7b 75 72 6c   _cachefile {url
03d0: 20 6b 65 79 20 7b 6b 65 79 49 73 48 61 73 68 20   key {keyIsHash 
03e0: 31 7d 7d 20 7b 0a 09 09 73 65 74 20 66 69 6c 65  1}} {...set file
03f0: 6b 65 79 20 24 6b 65 79 0a 09 09 69 66 20 7b 24  key $key...if {$
0400: 6b 65 79 49 73 48 61 73 68 7d 20 7b 0a 09 09 09  keyIsHash} {....
0410: 73 65 74 20 66 69 6c 65 6b 65 79 20 5b 5f 68 61  set filekey [_ha
0420: 73 68 5f 73 65 70 20 24 66 69 6c 65 6b 65 79 5d  sh_sep $filekey]
0430: 0a 09 09 7d 0a 0a 09 09 73 65 74 20 66 69 6c 65  ...}....set file
0440: 20 5b 66 69 6c 65 20 6a 6f 69 6e 20 24 3a 3a 61   [file join $::a
0450: 70 70 66 73 3a 3a 63 61 63 68 65 64 69 72 20 24  ppfs::cachedir $
0460: 66 69 6c 65 6b 65 79 5d 0a 0a 09 09 66 69 6c 65  filekey]....file
0470: 20 6d 6b 64 69 72 20 5b 66 69 6c 65 20 64 69 72   mkdir [file dir
0480: 6e 61 6d 65 20 24 66 69 6c 65 5d 0a 0a 09 09 69  name $file]....i
0490: 66 20 7b 5b 66 69 6c 65 20 65 78 69 73 74 73 20  f {[file exists 
04a0: 24 66 69 6c 65 5d 7d 20 7b 0a 09 09 09 72 65 74  $file]} {....ret
04b0: 75 72 6e 20 24 66 69 6c 65 0a 09 09 7d 0a 0a 09  urn $file...}...
04c0: 09 73 65 74 20 74 6d 70 66 69 6c 65 20 22 24 7b  .set tmpfile "${
04d0: 66 69 6c 65 7d 2e 5b 65 78 70 72 20 7b 72 61 6e  file}.[expr {ran
04e0: 64 28 29 7d 5d 5b 63 6c 6f 63 6b 20 63 6c 69 63  d()}][clock clic
04f0: 6b 73 5d 22 0a 0a 09 09 73 65 74 20 66 64 20 5b  ks]"....set fd [
0500: 6f 70 65 6e 20 24 74 6d 70 66 69 6c 65 20 22 77  open $tmpfile "w
0510: 22 5d 0a 09 09 66 63 6f 6e 66 69 67 75 72 65 20  "]...fconfigure 
0520: 24 66 64 20 2d 74 72 61 6e 73 6c 61 74 69 6f 6e  $fd -translation
0530: 20 62 69 6e 61 72 79 0a 0a 09 09 63 61 74 63 68   binary....catch
0540: 20 7b 0a 09 09 09 73 65 74 20 74 6f 6b 65 6e 20   {....set token 
0550: 5b 3a 3a 68 74 74 70 3a 3a 67 65 74 75 72 6c 20  [::http::geturl 
0560: 24 75 72 6c 20 2d 63 68 61 6e 6e 65 6c 20 24 66  $url -channel $f
0570: 64 20 2d 62 69 6e 61 72 79 20 74 72 75 65 5d 0a  d -binary true].
0580: 09 09 7d 0a 0a 09 09 69 66 20 7b 5b 69 6e 66 6f  ..}....if {[info
0590: 20 65 78 69 73 74 73 20 74 6f 6b 65 6e 5d 7d 20   exists token]} 
05a0: 7b 0a 09 09 09 73 65 74 20 6e 63 6f 64 65 20 5b  {....set ncode [
05b0: 3a 3a 68 74 74 70 3a 3a 6e 63 6f 64 65 20 24 74  ::http::ncode $t
05c0: 6f 6b 65 6e 5d 0a 09 09 09 3a 3a 68 74 74 70 3a  oken]....::http:
05d0: 3a 72 65 73 65 74 20 24 74 6f 6b 65 6e 0a 09 09  :reset $token...
05e0: 7d 20 65 6c 73 65 20 7b 0a 09 09 09 73 65 74 20  } else {....set 
05f0: 6e 63 6f 64 65 20 22 39 30 30 22 0a 09 09 7d 0a  ncode "900"...}.
0600: 0a 09 09 63 6c 6f 73 65 20 24 66 64 0a 0a 09 09  ...close $fd....
0610: 69 66 20 7b 24 6b 65 79 49 73 48 61 73 68 7d 20  if {$keyIsHash} 
0620: 7b 0a 09 09 09 73 65 74 20 68 61 73 68 20 5b 73  {....set hash [s
0630: 74 72 69 6e 67 20 74 6f 6c 6f 77 65 72 20 5b 73  tring tolower [s
0640: 68 61 31 3a 3a 73 68 61 31 20 2d 68 65 78 20 2d  ha1::sha1 -hex -
0650: 66 69 6c 65 20 24 74 6d 70 66 69 6c 65 5d 5d 0a  file $tmpfile]].
0660: 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 73 65  ..} else {....se
0670: 74 20 68 61 73 68 20 24 6b 65 79 0a 09 09 7d 0a  t hash $key...}.
0680: 0a 09 09 69 66 20 7b 24 6e 63 6f 64 65 20 3d 3d  ...if {$ncode ==
0690: 20 22 32 30 30 22 20 26 26 20 24 68 61 73 68 20   "200" && $hash 
06a0: 3d 3d 20 24 6b 65 79 7d 20 7b 0a 09 09 09 66 69  == $key} {....fi
06b0: 6c 65 20 72 65 6e 61 6d 65 20 2d 66 6f 72 63 65  le rename -force
06c0: 20 2d 2d 20 24 74 6d 70 66 69 6c 65 20 24 66 69   -- $tmpfile $fi
06d0: 6c 65 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09  le...} else {...
06e0: 09 66 69 6c 65 20 64 65 6c 65 74 65 20 2d 66 6f  .file delete -fo
06f0: 72 63 65 20 2d 2d 20 24 74 6d 70 66 69 6c 65 0a  rce -- $tmpfile.
0700: 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e 20 24 66  ..}....return $f
0710: 69 6c 65 0a 09 7d 0a 0a 0a 09 70 72 6f 63 20 5f  ile..}....proc _
0720: 69 73 48 61 73 68 20 7b 76 61 6c 75 65 7d 20 7b  isHash {value} {
0730: 0a 09 09 73 65 74 20 76 61 6c 75 65 20 5b 73 74  ...set value [st
0740: 72 69 6e 67 20 74 6f 6c 6f 77 65 72 20 24 76 61  ring tolower $va
0750: 6c 75 65 5d 0a 0a 09 09 69 66 20 7b 5b 73 74 72  lue]....if {[str
0760: 69 6e 67 20 6c 65 6e 67 74 68 20 24 76 61 6c 75  ing length $valu
0770: 65 5d 20 21 3d 20 34 30 7d 20 7b 0a 09 09 09 72  e] != 40} {....r
0780: 65 74 75 72 6e 20 66 61 6c 73 65 0a 09 09 7d 0a  eturn false...}.
0790: 0a 09 09 69 66 20 7b 21 5b 72 65 67 65 78 70 20  ...if {![regexp 
07a0: 7b 5e 5b 30 2d 39 61 2d 66 5d 2a 24 7d 20 24 76  {^[0-9a-f]*$} $v
07b0: 61 6c 75 65 5d 7d 20 7b 0a 09 09 09 72 65 74 75  alue]} {....retu
07c0: 72 6e 20 66 61 6c 73 65 0a 09 09 7d 0a 0a 09 09  rn false...}....
07d0: 72 65 74 75 72 6e 20 74 72 75 65 0a 09 7d 0a 0a  return true..}..
07e0: 09 70 72 6f 63 20 5f 76 65 72 69 66 79 53 69 67  .proc _verifySig
07f0: 6e 61 74 75 72 65 41 6e 64 43 65 72 74 69 66 69  natureAndCertifi
0800: 63 61 74 65 20 7b 68 6f 73 74 6e 61 6d 65 20 63  cate {hostname c
0810: 65 72 74 69 66 69 63 61 74 65 20 73 69 67 6e 61  ertificate signa
0820: 74 75 72 65 20 68 61 73 68 7d 20 7b 0a 09 09 73  ture hash} {...s
0830: 65 74 20 63 65 72 74 69 66 69 63 61 74 65 20 5b  et certificate [
0840: 62 69 6e 61 72 79 20 66 6f 72 6d 61 74 20 22 48  binary format "H
0850: 2a 22 20 24 63 65 72 74 69 66 69 63 61 74 65 5d  *" $certificate]
0860: 0a 09 09 73 65 74 20 73 69 67 6e 61 74 75 72 65  ...set signature
0870: 20 20 20 5b 62 69 6e 61 72 79 20 66 6f 72 6d 61     [binary forma
0880: 74 20 22 48 2a 22 20 24 73 69 67 6e 61 74 75 72  t "H*" $signatur
0890: 65 5d 0a 0a 09 09 73 65 74 20 63 65 72 74 69 66  e]....set certif
08a0: 69 63 61 74 65 20 5b 3a 3a 70 6b 69 3a 3a 78 35  icate [::pki::x5
08b0: 30 39 3a 3a 70 61 72 73 65 5f 63 65 72 74 20 24  09::parse_cert $
08c0: 63 65 72 74 69 66 69 63 61 74 65 5d 0a 0a 09 09  certificate]....
08d0: 61 72 72 61 79 20 73 65 74 20 63 65 72 74 69 66  array set certif
08e0: 69 63 61 74 65 5f 61 72 72 20 24 63 65 72 74 69  icate_arr $certi
08f0: 66 69 63 61 74 65 0a 09 09 73 65 74 20 63 65 72  ficate...set cer
0900: 74 69 66 69 63 61 74 65 5f 63 6e 20 5b 3a 3a 70  tificate_cn [::p
0910: 6b 69 3a 3a 78 35 30 39 3a 3a 5f 64 6e 5f 74 6f  ki::x509::_dn_to
0920: 5f 63 6e 20 24 63 65 72 74 69 66 69 63 61 74 65  _cn $certificate
0930: 5f 61 72 72 28 73 75 62 6a 65 63 74 29 5d 0a 0a  _arr(subject)]..
0940: 09 09 69 66 20 7b 21 5b 3a 3a 70 6b 69 3a 3a 76  ..if {![::pki::v
0950: 65 72 69 66 79 20 24 73 69 67 6e 61 74 75 72 65  erify $signature
0960: 20 22 24 68 61 73 68 2c 73 68 61 31 22 20 24 63   "$hash,sha1" $c
0970: 65 72 74 69 66 69 63 61 74 65 5d 7d 20 7b 0a 09  ertificate]} {..
0980: 09 09 72 65 74 75 72 6e 20 66 61 6c 73 65 0a 09  ..return false..
0990: 09 7d 0a 0a 09 09 69 66 20 7b 5b 73 74 72 69 6e  .}....if {[strin
09a0: 67 20 74 6f 6c 6f 77 65 72 20 24 63 65 72 74 69  g tolower $certi
09b0: 66 69 63 61 74 65 5f 63 6e 5d 20 21 3d 20 5b 73  ficate_cn] != [s
09c0: 74 72 69 6e 67 20 74 6f 6c 6f 77 65 72 20 24 68  tring tolower $h
09d0: 6f 73 74 6e 61 6d 65 5d 7d 20 7b 0a 09 09 09 72  ostname]} {....r
09e0: 65 74 75 72 6e 20 66 61 6c 73 65 0a 09 09 7d 0a  eturn false...}.
09f0: 0a 09 09 69 66 20 7b 21 5b 3a 3a 70 6b 69 3a 3a  ...if {![::pki::
0a00: 78 35 30 39 3a 3a 76 65 72 69 66 79 5f 63 65 72  x509::verify_cer
0a10: 74 20 24 63 65 72 74 69 66 69 63 61 74 65 20 24  t $certificate $
0a20: 3a 3a 61 70 70 66 73 3a 3a 74 72 75 73 74 65 64  ::appfs::trusted
0a30: 5f 63 61 73 5d 7d 20 7b 0a 09 09 09 72 65 74 75  _cas]} {....retu
0a40: 72 6e 20 66 61 6c 73 65 0a 09 09 7d 0a 0a 09 09  rn false...}....
0a50: 72 65 74 75 72 6e 20 74 72 75 65 0a 09 7d 0a 0a  return true..}..
0a60: 09 70 72 6f 63 20 5f 6e 6f 72 6d 61 6c 69 7a 65  .proc _normalize
0a70: 4f 53 20 7b 6f 73 7d 20 7b 0a 09 09 73 65 74 20  OS {os} {...set 
0a80: 6f 73 20 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77  os [string tolow
0a90: 65 72 20 5b 73 74 72 69 6e 67 20 74 72 69 6d 20  er [string trim 
0aa0: 24 6f 73 5d 5d 0a 0a 09 09 73 77 69 74 63 68 20  $os]]....switch 
0ab0: 2d 2d 20 24 6f 73 20 7b 0a 09 09 09 22 6c 69 6e  -- $os {...."lin
0ac0: 75 78 22 20 2d 20 22 66 72 65 65 62 73 64 22 20  ux" - "freebsd" 
0ad0: 2d 20 22 6f 70 65 6e 62 73 64 22 20 2d 20 22 6e  - "openbsd" - "n
0ae0: 65 74 62 73 64 22 20 7b 0a 09 09 09 09 72 65 74  etbsd" {.....ret
0af0: 75 72 6e 20 24 6f 73 0a 09 09 09 7d 0a 09 09 09  urn $os....}....
0b00: 22 73 75 6e 6f 73 22 20 7b 0a 09 09 09 09 72 65  "sunos" {.....re
0b10: 74 75 72 6e 20 22 73 6f 6c 61 72 69 73 22 0a 09  turn "solaris"..
0b20: 09 09 7d 0a 09 09 09 22 6e 6f 61 72 63 68 22 20  ..}...."noarch" 
0b30: 2d 20 22 6e 6f 6e 65 22 20 2d 20 22 61 6e 79 22  - "none" - "any"
0b40: 20 2d 20 22 61 6c 6c 22 20 7b 0a 09 09 09 09 72   - "all" {.....r
0b50: 65 74 75 72 6e 20 22 6e 6f 61 72 63 68 22 0a 09  eturn "noarch"..
0b60: 09 09 7d 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72  ..}...}....retur
0b70: 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55  n -code error "U
0b80: 6e 61 62 6c 65 20 74 6f 20 6e 6f 72 6d 61 6c 69  nable to normali
0b90: 7a 65 20 4f 53 3a 20 24 6f 73 22 0a 09 7d 0a 0a  ze OS: $os"..}..
0ba0: 09 70 72 6f 63 20 5f 6e 6f 72 6d 61 6c 69 7a 65  .proc _normalize
0bb0: 43 50 55 20 7b 63 70 75 7d 20 7b 0a 09 09 73 65  CPU {cpu} {...se
0bc0: 74 20 63 70 75 20 5b 73 74 72 69 6e 67 20 74 6f  t cpu [string to
0bd0: 6c 6f 77 65 72 20 5b 73 74 72 69 6e 67 20 74 72  lower [string tr
0be0: 69 6d 20 24 63 70 75 5d 5d 0a 0a 09 09 73 77 69  im $cpu]]....swi
0bf0: 74 63 68 20 2d 67 6c 6f 62 20 2d 2d 20 24 63 70  tch -glob -- $cp
0c00: 75 20 7b 0a 09 09 09 22 69 3f 38 36 22 20 7b 0a  u {...."i?86" {.
0c10: 09 09 09 09 72 65 74 75 72 6e 20 22 69 78 38 36  ....return "ix86
0c20: 22 0a 09 09 09 7d 0a 09 09 09 22 78 38 36 5f 36  "....}...."x86_6
0c30: 34 22 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20  4" {.....return 
0c40: 24 63 70 75 0a 09 09 09 7d 0a 09 09 09 22 6e 6f  $cpu....}...."no
0c50: 61 72 63 68 22 20 2d 20 22 6e 6f 6e 65 22 20 2d  arch" - "none" -
0c60: 20 22 61 6e 79 22 20 2d 20 22 61 6c 6c 22 20 7b   "any" - "all" {
0c70: 0a 09 09 09 09 72 65 74 75 72 6e 20 22 6e 6f 61  .....return "noa
0c80: 72 63 68 22 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09  rch"....}...}...
0c90: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
0ca0: 72 6f 72 20 22 55 6e 61 62 6c 65 20 74 6f 20 6e  ror "Unable to n
0cb0: 6f 72 6d 61 6c 69 7a 65 20 43 50 55 3a 20 24 63  ormalize CPU: $c
0cc0: 70 75 22 0a 09 7d 0a 0a 09 70 72 6f 63 20 5f 61  pu"..}...proc _a
0cd0: 73 5f 75 73 65 72 20 7b 63 6f 64 65 7d 20 7b 0a  s_user {code} {.
0ce0: 09 09 3a 3a 61 70 70 66 73 64 3a 3a 73 69 6d 75  ..::appfsd::simu
0cf0: 6c 61 74 65 5f 75 73 65 72 5f 66 73 5f 65 6e 74  late_user_fs_ent
0d00: 65 72 0a 0a 09 09 73 65 74 20 72 65 74 63 6f 64  er....set retcod
0d10: 65 20 5b 63 61 74 63 68 20 5b 6c 69 73 74 20 75  e [catch [list u
0d20: 70 6c 65 76 65 6c 20 24 63 6f 64 65 5d 20 72 65  plevel $code] re
0d30: 74 73 74 72 5d 0a 0a 09 09 3a 3a 61 70 70 66 73  tstr]....::appfs
0d40: 64 3a 3a 73 69 6d 75 6c 61 74 65 5f 75 73 65 72  d::simulate_user
0d50: 5f 66 73 5f 6c 65 61 76 65 0a 0a 09 09 72 65 74  _fs_leave....ret
0d60: 75 72 6e 20 2d 63 6f 64 65 20 24 72 65 74 63 6f  urn -code $retco
0d70: 64 65 20 24 72 65 74 73 74 72 0a 09 7d 0a 0a 09  de $retstr..}...
0d80: 70 72 6f 63 20 69 6e 69 74 20 7b 7d 20 7b 0a 09  proc init {} {..
0d90: 09 69 66 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74  .if {[info exist
0da0: 73 20 3a 3a 61 70 70 66 73 3a 3a 69 6e 69 74 5f  s ::appfs::init_
0db0: 63 61 6c 6c 65 64 5d 7d 20 7b 0a 09 09 09 72 65  called]} {....re
0dc0: 74 75 72 6e 0a 09 09 7d 0a 0a 09 09 23 20 46 6f  turn...}....# Fo
0dd0: 72 63 65 20 5b 70 61 72 72 61 79 5d 20 61 6e 64  rce [parray] and
0de0: 20 5b 63 6c 6f 63 6b 5d 20 74 6f 20 62 65 20 6c   [clock] to be l
0df0: 6f 61 64 65 64 0a 09 09 63 61 74 63 68 20 7b 0a  oaded...catch {.
0e00: 09 09 09 70 61 72 72 61 79 20 64 6f 65 73 5f 6e  ...parray does_n
0e10: 6f 74 5f 65 78 69 73 74 0a 09 09 7d 0a 09 09 63  ot_exist...}...c
0e20: 61 74 63 68 20 7b 0a 09 09 09 63 6c 6f 63 6b 20  atch {....clock 
0e30: 73 65 63 6f 6e 64 73 0a 09 09 7d 0a 09 09 63 61  seconds...}...ca
0e40: 74 63 68 20 7b 0a 09 09 09 63 6c 6f 63 6b 20 61  tch {....clock a
0e50: 64 64 20 5b 63 6c 6f 63 6b 20 73 65 63 6f 6e 64  dd [clock second
0e60: 73 5d 20 33 20 73 65 63 6f 6e 64 73 0a 09 09 7d  s] 3 seconds...}
0e70: 0a 0a 09 09 73 65 74 20 3a 3a 61 70 70 66 73 3a  ....set ::appfs:
0e80: 3a 69 6e 69 74 5f 63 61 6c 6c 65 64 20 31 0a 0a  :init_called 1..
0e90: 09 09 23 20 41 64 64 20 61 20 64 65 66 61 75 6c  ..# Add a defaul
0ea0: 74 20 43 41 20 74 6f 20 6c 69 73 74 20 6f 66 20  t CA to list of 
0eb0: 74 72 75 73 74 65 64 20 43 41 73 0a 09 09 6c 61  trusted CAs...la
0ec0: 70 70 65 6e 64 20 3a 3a 61 70 70 66 73 3a 3a 74  ppend ::appfs::t
0ed0: 72 75 73 74 65 64 5f 63 61 73 20 5b 3a 3a 70 6b  rusted_cas [::pk
0ee0: 69 3a 3a 78 35 30 39 3a 3a 70 61 72 73 65 5f 63  i::x509::parse_c
0ef0: 65 72 74 20 7b 0a 2d 2d 2d 2d 2d 42 45 47 49 4e  ert {.-----BEGIN
0f00: 20 43 45 52 54 49 46 49 43 41 54 45 2d 2d 2d 2d   CERTIFICATE----
0f10: 2d 0a 4d 49 49 43 37 44 43 43 41 64 53 67 41 77  -.MIIC7DCCAdSgAw
0f20: 49 42 41 67 49 42 41 54 41 4e 42 67 6b 71 68 6b  IBAgIBATANBgkqhk
0f30: 69 47 39 77 30 42 41 51 55 46 41 44 41 76 4d 52  iG9w0BAQUFADAvMR
0f40: 49 77 45 41 59 44 56 51 51 4b 45 77 6c 53 62 33  IwEAYDVQQKEwlSb3
0f50: 6b 67 0a 53 32 56 6c 62 6d 55 78 47 54 41 58 42  kg.S2VlbmUxGTAXB
0f60: 67 4e 56 42 41 4d 54 45 45 46 77 63 45 5a 54 49  gNVBAMTEEFwcEZTI
0f70: 45 74 6c 65 53 42 4e 59 58 4e 30 5a 58 49 77 48  EtleSBNYXN0ZXIwH
0f80: 68 63 4e 4d 54 51 78 4d 54 45 33 4d 6a 41 78 4e  hcNMTQxMTE3MjAxN
0f90: 7a 49 34 0a 57 68 63 4e 4d 54 6b 78 4d 54 45 33  zI4.WhcNMTkxMTE3
0fa0: 4d 6a 41 78 4e 7a 49 34 57 6a 41 76 4d 52 49 77  MjAxNzI4WjAvMRIw
0fb0: 45 41 59 44 56 51 51 4b 45 77 6c 53 62 33 6b 67  EAYDVQQKEwlSb3kg
0fc0: 53 32 56 6c 62 6d 55 78 47 54 41 58 42 67 4e 56  S2VlbmUxGTAXBgNV
0fd0: 42 41 4d 54 0a 45 45 46 77 63 45 5a 54 49 45 74  BAMT.EEFwcEZTIEt
0fe0: 6c 65 53 42 4e 59 58 4e 30 5a 58 49 77 67 67 45  leSBNYXN0ZXIwggE
0ff0: 69 4d 41 30 47 43 53 71 47 53 49 62 33 44 51 45  iMA0GCSqGSIb3DQE
1000: 42 41 51 55 41 41 34 49 42 44 77 41 77 67 67 45  BAQUAA4IBDwAwggE
1010: 4b 41 6f 49 42 0a 41 51 43 71 36 75 53 4b 34 36  KAoIB.AQCq6uSK46
1020: 79 47 35 62 36 52 4a 57 77 52 6c 76 77 35 67 6c  yG5b6RJWwRlvw5gl
1030: 41 6e 6a 73 63 31 47 69 58 33 64 75 58 41 30 76  Anjsc1GiX3duXA0v
1040: 47 34 71 6e 4b 55 6e 44 74 6c 2f 6a 63 4d 6d 71  G4qnKUnDtl/jcMmq
1050: 32 47 4d 4f 42 39 0a 49 79 31 74 6a 61 62 45 48  2GMOB9.Iy1tjabEH
1060: 41 30 4d 68 57 32 6a 37 56 77 65 2f 4f 39 4d 4c  A0MhW2j7Vwe/O9ML
1070: 46 4a 6b 4a 33 30 4d 31 50 56 44 37 59 5a 52 52  FJkJ30M1PVD7YZRR
1080: 4e 61 41 73 7a 33 55 57 49 4b 45 6a 50 49 37 42  NaAsz3UWIKEjPI7B
1090: 42 63 33 32 4b 4f 6d 0a 42 4c 33 43 54 58 43 43  Bc32KOm.BL3CTXCC
10a0: 64 7a 6c 6c 4c 31 48 68 56 62 6e 4d 35 69 43 41  dzllL1HhVbnM5iCA
10b0: 6d 67 48 63 67 31 44 55 6b 2f 45 76 57 58 76 6e  mgHcg1DUk/EvWXvn
10c0: 45 44 78 58 52 79 32 6c 56 39 6d 51 73 6d 44 65  EDxXRy2lV9mQsmDe
10d0: 64 72 66 66 59 37 57 6c 0a 4f 72 35 37 6e 6c 63  drffY7Wl.Or57nlc
10e0: 7a 61 4d 75 50 4c 70 79 52 53 6b 76 37 35 50 41  zaMuPLpyRSkv75PA
10f0: 6e 6a 51 4a 78 54 33 73 57 6c 42 70 79 2b 2f 48  njQJxT3sWlBpy+/H
1100: 39 49 6d 75 64 51 64 70 4a 4e 66 2f 46 74 78 63  9ImudQdpJNf/Ftxc
1110: 71 4e 37 69 44 77 48 35 42 0a 76 49 63 65 59 45  qN7iDwH5B.vIceYE
1120: 74 44 56 78 46 73 76 6f 35 48 4f 56 6b 53 6c 39  tDVxFsvo5HOVkSl9
1130: 6a 65 6f 35 45 34 47 70 65 33 77 79 66 52 68 6f  jeo5E4Gpe3wyfRho
1140: 71 42 32 55 6b 61 57 31 4b 71 30 69 48 35 52 2b  qB2UkaW1Kq0iH5R+
1150: 30 30 53 37 36 30 78 51 4d 78 0a 4c 4c 39 4c 31  00S760xQMx.LL9L1
1160: 64 75 68 75 31 64 4c 37 48 73 6d 45 77 37 49 65  duhu1dL7HsmEw7Ie
1170: 59 55 52 41 67 4d 42 41 41 47 6a 45 7a 41 52 4d  YURAgMBAAGjEzARM
1180: 41 38 47 41 31 55 64 45 77 45 42 2f 77 51 46 4d  A8GA1UdEwEB/wQFM
1190: 41 4d 42 41 66 38 77 44 51 59 4a 0a 4b 6f 5a 49  AMBAf8wDQYJ.KoZI
11a0: 68 76 63 4e 41 51 45 46 42 51 41 44 67 67 45 42  hvcNAQEFBQADggEB
11b0: 41 4b 68 4f 34 5a 53 7a 59 50 33 37 42 71 69 78  AKhO4ZSzYP37Bqix
11c0: 4e 48 4b 4b 39 2b 67 53 65 43 36 46 67 61 38 35  NHKK9+gSeC6Fga85
11d0: 69 4c 57 68 77 70 50 57 30 6b 53 6c 0a 7a 30 33  iLWhwpPW0kSl.z03
11e0: 68 61 6c 38 30 4b 5a 2b 6b 50 4d 7a 62 38 43 35  hal80KZ+kPMzb8C5
11f0: 32 4e 32 38 33 74 51 4e 41 71 4a 39 51 38 61 6b  2N283tQNAqJ9Q8ak
1200: 44 50 5a 78 53 7a 7a 4d 55 56 4f 47 70 47 77 32  DPZxSzzMUVOGpGw2
1210: 70 4a 37 5a 73 77 4b 44 7a 30 5a 54 61 0a 30 65  pJ7ZswKDz0ZTa.0e
1220: 64 71 2f 67 64 54 2f 48 72 64 65 67 76 4e 74 44  dq/gdT/HrdegvNtD
1230: 50 63 32 6a 6f 6e 61 35 46 56 4f 59 71 77 64 63  Pc2jona5FVOYqwdc
1240: 7a 35 6b 62 6c 31 55 57 42 61 42 70 33 56 58 55  z5kbl1UWBaBp3VXU
1250: 67 63 59 6a 58 53 52 61 42 4b 34 33 57 64 0a 63  gcYjXSRaBK43Wd.c
1260: 76 65 69 44 55 65 5a 77 37 67 48 71 52 53 4e 2f  veiDUeZw7gHqRSN/
1270: 41 79 59 55 43 74 4a 7a 57 6d 76 47 73 4a 75 49  AyYUCtJzWmvGsJuI
1280: 46 68 4d 42 6f 6e 75 7a 38 6a 79 6c 68 79 4d 4a  FhMBonuz8jylhyMJ
1290: 43 59 4a 46 54 34 69 4d 55 43 38 4d 4e 49 77 0a  CYJFT4iMUC8MNIw.
12a0: 6e 69 58 31 78 78 2b 4e 75 36 66 50 56 35 5a 5a  niX1xx+Nu6fPV5ZZ
12b0: 48 6a 39 72 62 68 69 42 61 4c 6a 6d 2b 74 6b 44  Hj9rbhiBaLjm+tkD
12c0: 77 74 50 67 41 33 6a 32 70 78 76 48 4b 59 70 74  wtPgA3j2pxvHKYpt
12d0: 75 57 78 65 59 4f 2b 39 44 44 4e 61 39 73 43 62  uWxeYO+9DDNa9sCb
12e0: 0a 45 35 41 6e 4a 49 6c 4f 6e 64 2f 74 47 65 30  .E5AnJIlOnd/tGe0
12f0: 43 68 66 30 73 46 51 67 2b 6c 39 6e 4e 69 4e 72  Chf0sFQg+l9nNiNr
1300: 57 47 67 7a 64 64 39 5a 50 4a 4b 34 3d 0a 2d 2d  WGgzdd9ZPJK4=.--
1310: 2d 2d 2d 45 4e 44 20 43 45 52 54 49 46 49 43 41  ---END CERTIFICA
1320: 54 45 2d 2d 2d 2d 2d 0a 7d 5d 0a 0a 09 09 23 20  TE-----.}]....# 
1330: 4c 6f 61 64 20 63 6f 6e 66 69 67 75 72 61 74 69  Load configurati
1340: 6f 6e 20 66 69 6c 65 0a 09 09 73 65 74 20 63 6f  on file...set co
1350: 6e 66 69 67 5f 66 69 6c 65 20 5b 66 69 6c 65 20  nfig_file [file 
1360: 6a 6f 69 6e 20 24 3a 3a 61 70 70 66 73 3a 3a 63  join $::appfs::c
1370: 61 63 68 65 64 69 72 20 63 6f 6e 66 69 67 5d 0a  achedir config].
1380: 09 09 69 66 20 7b 5b 66 69 6c 65 20 65 78 69 73  ..if {[file exis
1390: 74 73 20 24 63 6f 6e 66 69 67 5f 66 69 6c 65 5d  ts $config_file]
13a0: 7d 20 7b 0a 09 09 09 73 6f 75 72 63 65 20 24 63  } {....source $c
13b0: 6f 6e 66 69 67 5f 66 69 6c 65 0a 09 09 7d 0a 0a  onfig_file...}..
13c0: 09 09 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69  ..if {![info exi
13d0: 73 74 73 20 3a 3a 61 70 70 66 73 3a 3a 64 62 5d  sts ::appfs::db]
13e0: 7d 20 7b 0a 09 09 09 66 69 6c 65 20 6d 6b 64 69  } {....file mkdi
13f0: 72 20 24 3a 3a 61 70 70 66 73 3a 3a 63 61 63 68  r $::appfs::cach
1400: 65 64 69 72 0a 0a 09 09 09 73 71 6c 69 74 65 33  edir.....sqlite3
1410: 20 3a 3a 61 70 70 66 73 3a 3a 64 62 20 5b 66 69   ::appfs::db [fi
1420: 6c 65 20 6a 6f 69 6e 20 24 3a 3a 61 70 70 66 73  le join $::appfs
1430: 3a 3a 63 61 63 68 65 64 69 72 20 63 61 63 68 65  ::cachedir cache
1440: 2e 64 62 5d 0a 0a 09 09 09 3a 3a 61 70 70 66 73  .db].....::appfs
1450: 3a 3a 64 62 20 74 69 6d 65 6f 75 74 20 33 30 30  ::db timeout 300
1460: 30 30 0a 09 09 7d 0a 0a 09 09 23 20 43 72 65 61  00...}....# Crea
1470: 74 65 20 74 61 62 6c 65 73 0a 09 09 64 62 20 65  te tables...db e
1480: 76 61 6c 20 7b 43 52 45 41 54 45 20 54 41 42 4c  val {CREATE TABL
1490: 45 20 49 46 20 4e 4f 54 20 45 58 49 53 54 53 20  E IF NOT EXISTS 
14a0: 73 69 74 65 73 28 68 6f 73 74 6e 61 6d 65 20 50  sites(hostname P
14b0: 52 49 4d 41 52 59 20 4b 45 59 2c 20 6c 61 73 74  RIMARY KEY, last
14c0: 55 70 64 61 74 65 2c 20 74 74 6c 29 3b 7d 0a 09  Update, ttl);}..
14d0: 09 64 62 20 65 76 61 6c 20 7b 43 52 45 41 54 45  .db eval {CREATE
14e0: 20 54 41 42 4c 45 20 49 46 20 4e 4f 54 20 45 58   TABLE IF NOT EX
14f0: 49 53 54 53 20 70 61 63 6b 61 67 65 73 28 68 6f  ISTS packages(ho
1500: 73 74 6e 61 6d 65 2c 20 73 68 61 31 2c 20 70 61  stname, sha1, pa
1510: 63 6b 61 67 65 2c 20 76 65 72 73 69 6f 6e 2c 20  ckage, version, 
1520: 6f 73 2c 20 63 70 75 41 72 63 68 2c 20 69 73 4c  os, cpuArch, isL
1530: 61 74 65 73 74 2c 20 68 61 76 65 4d 61 6e 69 66  atest, haveManif
1540: 65 73 74 29 3b 7d 0a 09 09 64 62 20 65 76 61 6c  est);}...db eval
1550: 20 7b 43 52 45 41 54 45 20 54 41 42 4c 45 20 49   {CREATE TABLE I
1560: 46 20 4e 4f 54 20 45 58 49 53 54 53 20 66 69 6c  F NOT EXISTS fil
1570: 65 73 28 70 61 63 6b 61 67 65 5f 73 68 61 31 2c  es(package_sha1,
1580: 20 74 79 70 65 2c 20 74 69 6d 65 2c 20 73 6f 75   type, time, sou
1590: 72 63 65 2c 20 73 69 7a 65 2c 20 70 65 72 6d 73  rce, size, perms
15a0: 2c 20 66 69 6c 65 5f 73 68 61 31 2c 20 66 69 6c  , file_sha1, fil
15b0: 65 5f 6e 61 6d 65 2c 20 66 69 6c 65 5f 64 69 72  e_name, file_dir
15c0: 65 63 74 6f 72 79 29 3b 7d 0a 0a 09 09 23 20 43  ectory);}....# C
15d0: 72 65 61 74 65 20 69 6e 64 65 78 65 73 0a 09 09  reate indexes...
15e0: 64 62 20 65 76 61 6c 20 7b 43 52 45 41 54 45 20  db eval {CREATE 
15f0: 49 4e 44 45 58 20 49 46 20 4e 4f 54 20 45 58 49  INDEX IF NOT EXI
1600: 53 54 53 20 73 69 74 65 73 5f 69 6e 64 65 78 20  STS sites_index 
1610: 4f 4e 20 73 69 74 65 73 20 28 68 6f 73 74 6e 61  ON sites (hostna
1620: 6d 65 29 3b 7d 0a 09 09 64 62 20 65 76 61 6c 20  me);}...db eval 
1630: 7b 43 52 45 41 54 45 20 49 4e 44 45 58 20 49 46  {CREATE INDEX IF
1640: 20 4e 4f 54 20 45 58 49 53 54 53 20 70 61 63 6b   NOT EXISTS pack
1650: 61 67 65 73 5f 69 6e 64 65 78 20 4f 4e 20 70 61  ages_index ON pa
1660: 63 6b 61 67 65 73 20 28 68 6f 73 74 6e 61 6d 65  ckages (hostname
1670: 2c 20 73 68 61 31 2c 20 70 61 63 6b 61 67 65 2c  , sha1, package,
1680: 20 76 65 72 73 69 6f 6e 2c 20 6f 73 2c 20 63 70   version, os, cp
1690: 75 41 72 63 68 29 3b 7d 0a 09 09 64 62 20 65 76  uArch);}...db ev
16a0: 61 6c 20 7b 43 52 45 41 54 45 20 49 4e 44 45 58  al {CREATE INDEX
16b0: 20 49 46 20 4e 4f 54 20 45 58 49 53 54 53 20 66   IF NOT EXISTS f
16c0: 69 6c 65 73 5f 69 6e 64 65 78 20 4f 4e 20 66 69  iles_index ON fi
16d0: 6c 65 73 20 28 70 61 63 6b 61 67 65 5f 73 68 61  les (package_sha
16e0: 31 2c 20 66 69 6c 65 5f 6e 61 6d 65 2c 20 66 69  1, file_name, fi
16f0: 6c 65 5f 64 69 72 65 63 74 6f 72 79 29 3b 7d 0a  le_directory);}.
1700: 09 7d 0a 0a 09 70 72 6f 63 20 64 6f 77 6e 6c 6f  .}...proc downlo
1710: 61 64 20 7b 68 6f 73 74 6e 61 6d 65 20 68 61 73  ad {hostname has
1720: 68 20 7b 6d 65 74 68 6f 64 20 73 68 61 31 7d 7d  h {method sha1}}
1730: 20 7b 0a 09 09 73 65 74 20 75 72 6c 20 5b 3a 3a   {...set url [::
1740: 61 70 70 66 73 3a 3a 75 73 65 72 3a 3a 63 6f 6e  appfs::user::con
1750: 73 74 72 75 63 74 5f 75 72 6c 20 24 68 6f 73 74  struct_url $host
1760: 6e 61 6d 65 20 24 68 61 73 68 20 24 6d 65 74 68  name $hash $meth
1770: 6f 64 5d 0a 09 09 73 65 74 20 66 69 6c 65 20 5b  od]...set file [
1780: 5f 63 61 63 68 65 66 69 6c 65 20 24 75 72 6c 20  _cachefile $url 
1790: 24 68 61 73 68 5d 0a 0a 09 09 69 66 20 7b 21 5b  $hash]....if {![
17a0: 66 69 6c 65 20 65 78 69 73 74 73 20 24 66 69 6c  file exists $fil
17b0: 65 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  e]} {....return 
17c0: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55 6e 61  -code error "Una
17d0: 62 6c 65 20 74 6f 20 66 65 74 63 68 20 28 66 69  ble to fetch (fi
17e0: 6c 65 20 64 6f 65 73 20 6e 6f 74 20 65 78 69 73  le does not exis
17f0: 74 3a 20 24 66 69 6c 65 29 22 0a 09 09 7d 0a 0a  t: $file)"...}..
1800: 09 09 72 65 74 75 72 6e 20 24 66 69 6c 65 0a 09  ..return $file..
1810: 7d 0a 0a 09 70 72 6f 63 20 67 65 74 69 6e 64 65  }...proc getinde
1820: 78 20 7b 68 6f 73 74 6e 61 6d 65 7d 20 7b 0a 09  x {hostname} {..
1830: 09 73 65 74 20 6e 6f 77 20 5b 63 6c 6f 63 6b 20  .set now [clock 
1840: 73 65 63 6f 6e 64 73 5d 0a 0a 09 09 73 65 74 20  seconds]....set 
1850: 6c 61 73 74 55 70 64 61 74 65 73 20 5b 64 62 20  lastUpdates [db 
1860: 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 6c 61 73  eval {SELECT las
1870: 74 55 70 64 61 74 65 2c 20 74 74 6c 20 46 52 4f  tUpdate, ttl FRO
1880: 4d 20 73 69 74 65 73 20 57 48 45 52 45 20 68 6f  M sites WHERE ho
1890: 73 74 6e 61 6d 65 20 3d 20 24 68 6f 73 74 6e 61  stname = $hostna
18a0: 6d 65 20 4c 49 4d 49 54 20 31 3b 7d 5d 0a 09 09  me LIMIT 1;}]...
18b0: 69 66 20 7b 5b 6c 6c 65 6e 67 74 68 20 24 6c 61  if {[llength $la
18c0: 73 74 55 70 64 61 74 65 73 5d 20 3d 3d 20 30 7d  stUpdates] == 0}
18d0: 20 7b 0a 09 09 09 73 65 74 20 6c 61 73 74 55 70   {....set lastUp
18e0: 64 61 74 65 20 30 0a 09 09 09 73 65 74 20 74 74  date 0....set tt
18f0: 6c 20 30 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09  l 0...} else {..
1900: 09 09 73 65 74 20 6c 61 73 74 55 70 64 61 74 65  ..set lastUpdate
1910: 20 5b 6c 69 6e 64 65 78 20 24 6c 61 73 74 55 70   [lindex $lastUp
1920: 64 61 74 65 73 20 30 5d 0a 09 09 09 73 65 74 20  dates 0]....set 
1930: 74 74 6c 20 5b 6c 69 6e 64 65 78 20 24 6c 61 73  ttl [lindex $las
1940: 74 55 70 64 61 74 65 73 20 31 5d 0a 09 09 7d 0a  tUpdates 1]...}.
1950: 0a 09 09 69 66 20 7b 24 6e 6f 77 20 3c 20 28 24  ...if {$now < ($
1960: 6c 61 73 74 55 70 64 61 74 65 20 2b 20 24 74 74  lastUpdate + $tt
1970: 6c 29 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  l)} {....return 
1980: 43 4f 4d 50 4c 45 54 45 0a 09 09 7d 0a 0a 09 09  COMPLETE...}....
1990: 69 66 20 7b 5b 73 74 72 69 6e 67 20 6d 61 74 63  if {[string matc
19a0: 68 20 22 2a 5c 5b 2f 7e 5c 5d 2a 22 20 24 68 6f  h "*\[/~\]*" $ho
19b0: 73 74 6e 61 6d 65 5d 7d 20 7b 0a 09 09 09 72 65  stname]} {....re
19c0: 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72  turn -code error
19d0: 20 22 49 6e 76 61 6c 69 64 20 68 6f 73 74 6e 61   "Invalid hostna
19e0: 6d 65 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20 75  me"...}....set u
19f0: 72 6c 20 22 68 74 74 70 3a 2f 2f 24 68 6f 73 74  rl "http://$host
1a00: 6e 61 6d 65 2f 61 70 70 66 73 2f 69 6e 64 65 78  name/appfs/index
1a10: 22 0a 0a 09 09 63 61 74 63 68 20 7b 0a 09 09 09  "....catch {....
1a20: 73 65 74 20 74 6f 6b 65 6e 20 5b 3a 3a 68 74 74  set token [::htt
1a30: 70 3a 3a 67 65 74 75 72 6c 20 24 75 72 6c 5d 0a  p::geturl $url].
1a40: 09 09 09 69 66 20 7b 5b 3a 3a 68 74 74 70 3a 3a  ...if {[::http::
1a50: 6e 63 6f 64 65 20 24 74 6f 6b 65 6e 5d 20 3d 3d  ncode $token] ==
1a60: 20 22 32 30 30 22 7d 20 7b 0a 09 09 09 09 73 65   "200"} {.....se
1a70: 74 20 69 6e 64 65 78 68 61 73 68 5f 64 61 74 61  t indexhash_data
1a80: 20 5b 3a 3a 68 74 74 70 3a 3a 64 61 74 61 20 24   [::http::data $
1a90: 74 6f 6b 65 6e 5d 0a 09 09 09 7d 0a 09 09 09 3a  token]....}....:
1aa0: 3a 68 74 74 70 3a 3a 72 65 73 65 74 20 24 74 6f  :http::reset $to
1ab0: 6b 65 6e 0a 09 09 09 3a 3a 68 74 74 70 3a 3a 63  ken....::http::c
1ac0: 6c 65 61 6e 75 70 20 24 74 6f 6b 65 6e 0a 09 09  leanup $token...
1ad0: 7d 0a 0a 09 09 69 66 20 7b 21 5b 69 6e 66 6f 20  }....if {![info 
1ae0: 65 78 69 73 74 73 20 69 6e 64 65 78 68 61 73 68  exists indexhash
1af0: 5f 64 61 74 61 5d 7d 20 7b 0a 09 09 09 23 20 43  _data]} {....# C
1b00: 61 63 68 65 20 74 68 69 73 20 72 65 73 75 6c 74  ache this result
1b10: 20 66 6f 72 20 36 30 20 73 65 63 6f 6e 64 73 0a   for 60 seconds.
1b20: 09 09 09 64 62 20 65 76 61 6c 20 7b 49 4e 53 45  ...db eval {INSE
1b30: 52 54 20 4f 52 20 52 45 50 4c 41 43 45 20 49 4e  RT OR REPLACE IN
1b40: 54 4f 20 73 69 74 65 73 20 28 68 6f 73 74 6e 61  TO sites (hostna
1b50: 6d 65 2c 20 6c 61 73 74 55 70 64 61 74 65 2c 20  me, lastUpdate, 
1b60: 74 74 6c 29 20 56 41 4c 55 45 53 20 28 24 68 6f  ttl) VALUES ($ho
1b70: 73 74 6e 61 6d 65 2c 20 24 6e 6f 77 2c 20 24 3a  stname, $now, $:
1b80: 3a 61 70 70 66 73 3a 3a 6e 74 74 6c 29 3b 7d 0a  :appfs::nttl);}.
1b90: 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65  ....return -code
1ba0: 20 65 72 72 6f 72 20 22 55 6e 61 62 6c 65 20 74   error "Unable t
1bb0: 6f 20 66 65 74 63 68 20 24 75 72 6c 22 0a 09 09  o fetch $url"...
1bc0: 7d 0a 0a 09 09 73 65 74 20 69 6e 64 65 78 68 61  }....set indexha
1bd0: 73 68 5f 64 61 74 61 20 5b 73 74 72 69 6e 67 20  sh_data [string 
1be0: 74 72 69 6d 20 24 69 6e 64 65 78 68 61 73 68 5f  trim $indexhash_
1bf0: 64 61 74 61 20 22 5c 72 5c 6e 22 5d 0a 09 09 73  data "\r\n"]...s
1c00: 65 74 20 69 6e 64 65 78 68 61 73 68 5f 64 61 74  et indexhash_dat
1c10: 61 20 5b 73 70 6c 69 74 20 24 69 6e 64 65 78 68  a [split $indexh
1c20: 61 73 68 5f 64 61 74 61 20 22 2c 22 5d 0a 09 09  ash_data ","]...
1c30: 73 65 74 20 69 6e 64 65 78 68 61 73 68 20 20 20  set indexhash   
1c40: 20 20 20 20 5b 6c 69 6e 64 65 78 20 24 69 6e 64      [lindex $ind
1c50: 65 78 68 61 73 68 5f 64 61 74 61 20 30 5d 0a 09  exhash_data 0]..
1c60: 09 73 65 74 20 69 6e 64 65 78 68 61 73 68 6d 65  .set indexhashme
1c70: 74 68 6f 64 20 5b 6c 69 6e 64 65 78 20 24 69 6e  thod [lindex $in
1c80: 64 65 78 68 61 73 68 5f 64 61 74 61 20 31 5d 0a  dexhash_data 1].
1c90: 09 09 73 65 74 20 69 6e 64 65 78 68 61 73 68 63  ..set indexhashc
1ca0: 65 72 74 20 20 20 5b 6c 69 6e 64 65 78 20 24 69  ert   [lindex $i
1cb0: 6e 64 65 78 68 61 73 68 5f 64 61 74 61 20 32 5d  ndexhash_data 2]
1cc0: 0a 09 09 73 65 74 20 69 6e 64 65 78 68 61 73 68  ...set indexhash
1cd0: 73 69 67 20 20 20 20 5b 6c 69 6e 64 65 78 20 24  sig    [lindex $
1ce0: 69 6e 64 65 78 68 61 73 68 5f 64 61 74 61 20 33  indexhash_data 3
1cf0: 5d 0a 0a 09 09 69 66 20 7b 21 5b 5f 69 73 48 61  ]....if {![_isHa
1d00: 73 68 20 24 69 6e 64 65 78 68 61 73 68 5d 7d 20  sh $indexhash]} 
1d10: 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64  {....return -cod
1d20: 65 20 65 72 72 6f 72 20 22 49 6e 76 61 6c 69 64  e error "Invalid
1d30: 20 68 61 73 68 3a 20 24 69 6e 64 65 78 68 61 73   hash: $indexhas
1d40: 68 22 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 21 5b  h"...}....if {![
1d50: 5f 76 65 72 69 66 79 53 69 67 6e 61 74 75 72 65  _verifySignature
1d60: 41 6e 64 43 65 72 74 69 66 69 63 61 74 65 20 24  AndCertificate $
1d70: 68 6f 73 74 6e 61 6d 65 20 24 69 6e 64 65 78 68  hostname $indexh
1d80: 61 73 68 63 65 72 74 20 24 69 6e 64 65 78 68 61  ashcert $indexha
1d90: 73 68 73 69 67 20 24 69 6e 64 65 78 68 61 73 68  shsig $indexhash
1da0: 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d  ]} {....return -
1db0: 63 6f 64 65 20 65 72 72 6f 72 20 22 49 6e 76 61  code error "Inva
1dc0: 6c 69 64 20 73 69 67 6e 61 74 75 72 65 20 6f 72  lid signature or
1dd0: 20 63 65 72 74 69 66 69 63 61 74 65 20 66 72 6f   certificate fro
1de0: 6d 20 24 68 6f 73 74 6e 61 6d 65 22 0a 09 09 7d  m $hostname"...}
1df0: 0a 0a 09 09 73 65 74 20 66 69 6c 65 20 5b 64 6f  ....set file [do
1e00: 77 6e 6c 6f 61 64 20 24 68 6f 73 74 6e 61 6d 65  wnload $hostname
1e10: 20 24 69 6e 64 65 78 68 61 73 68 5d 0a 09 09 73   $indexhash]...s
1e20: 65 74 20 66 64 20 5b 6f 70 65 6e 20 24 66 69 6c  et fd [open $fil
1e30: 65 5d 0a 09 09 73 65 74 20 64 61 74 61 20 5b 72  e]...set data [r
1e40: 65 61 64 20 24 66 64 5d 0a 09 09 63 6c 6f 73 65  ead $fd]...close
1e50: 20 24 66 64 0a 0a 09 09 73 65 74 20 63 75 72 72   $fd....set curr
1e60: 5f 70 61 63 6b 61 67 65 73 20 5b 6c 69 73 74 5d  _packages [list]
1e70: 0a 09 09 66 6f 72 65 61 63 68 20 6c 69 6e 65 20  ...foreach line 
1e80: 5b 73 70 6c 69 74 20 24 64 61 74 61 20 22 5c 6e  [split $data "\n
1e90: 22 5d 20 7b 0a 09 09 09 73 65 74 20 6c 69 6e 65  "] {....set line
1ea0: 20 5b 73 74 72 69 6e 67 20 74 72 69 6d 20 24 6c   [string trim $l
1eb0: 69 6e 65 5d 0a 0a 09 09 09 69 66 20 7b 5b 73 74  ine].....if {[st
1ec0: 72 69 6e 67 20 6d 61 74 63 68 20 22 2a 2f 2a 22  ring match "*/*"
1ed0: 20 24 6c 69 6e 65 5d 7d 20 7b 0a 09 09 09 09 63   $line]} {.....c
1ee0: 6f 6e 74 69 6e 75 65 0a 09 09 09 7d 0a 0a 09 09  ontinue....}....
1ef0: 09 69 66 20 7b 24 6c 69 6e 65 20 3d 3d 20 22 22  .if {$line == ""
1f00: 7d 20 7b 0a 09 09 09 09 63 6f 6e 74 69 6e 75 65  } {.....continue
1f10: 0a 09 09 09 7d 0a 0a 09 09 09 73 65 74 20 77 6f  ....}.....set wo
1f20: 72 6b 20 5b 73 70 6c 69 74 20 24 6c 69 6e 65 20  rk [split $line 
1f30: 22 2c 22 5d 0a 0a 09 09 09 75 6e 73 65 74 20 2d  ","].....unset -
1f40: 6e 6f 63 6f 6d 70 6c 61 69 6e 20 70 6b 67 49 6e  nocomplain pkgIn
1f50: 66 6f 0a 09 09 09 69 66 20 7b 5b 63 61 74 63 68  fo....if {[catch
1f60: 20 7b 0a 09 09 09 09 73 65 74 20 70 6b 67 49 6e   {.....set pkgIn
1f70: 66 6f 28 70 61 63 6b 61 67 65 29 20 20 5b 6c 69  fo(package)  [li
1f80: 6e 64 65 78 20 24 77 6f 72 6b 20 30 5d 0a 09 09  ndex $work 0]...
1f90: 09 09 73 65 74 20 70 6b 67 49 6e 66 6f 28 76 65  ..set pkgInfo(ve
1fa0: 72 73 69 6f 6e 29 20 20 5b 6c 69 6e 64 65 78 20  rsion)  [lindex 
1fb0: 24 77 6f 72 6b 20 31 5d 0a 09 09 09 09 73 65 74  $work 1].....set
1fc0: 20 70 6b 67 49 6e 66 6f 28 6f 73 29 20 20 20 20   pkgInfo(os)    
1fd0: 20 20 20 5b 5f 6e 6f 72 6d 61 6c 69 7a 65 4f 53     [_normalizeOS
1fe0: 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 32   [lindex $work 2
1ff0: 5d 5d 0a 09 09 09 09 73 65 74 20 70 6b 67 49 6e  ]].....set pkgIn
2000: 66 6f 28 63 70 75 41 72 63 68 29 20 20 5b 5f 6e  fo(cpuArch)  [_n
2010: 6f 72 6d 61 6c 69 7a 65 43 50 55 20 5b 6c 69 6e  ormalizeCPU [lin
2020: 64 65 78 20 24 77 6f 72 6b 20 33 5d 5d 0a 09 09  dex $work 3]]...
2030: 09 09 73 65 74 20 70 6b 67 49 6e 66 6f 28 68 61  ..set pkgInfo(ha
2040: 73 68 29 20 20 20 20 20 5b 73 74 72 69 6e 67 20  sh)     [string 
2050: 74 6f 6c 6f 77 65 72 20 5b 6c 69 6e 64 65 78 20  tolower [lindex 
2060: 24 77 6f 72 6b 20 34 5d 5d 0a 09 09 09 09 73 65  $work 4]].....se
2070: 74 20 70 6b 67 49 6e 66 6f 28 68 61 73 68 5f 74  t pkgInfo(hash_t
2080: 79 70 65 29 20 22 73 68 61 31 22 0a 09 09 09 09  ype) "sha1".....
2090: 73 65 74 20 70 6b 67 49 6e 66 6f 28 69 73 4c 61  set pkgInfo(isLa
20a0: 74 65 73 74 29 20 5b 65 78 70 72 20 7b 21 21 5b  test) [expr {!![
20b0: 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 35 5d 7d  lindex $work 5]}
20c0: 5d 0a 09 09 09 7d 5d 7d 20 7b 0a 09 09 09 09 63  ]....}]} {.....c
20d0: 6f 6e 74 69 6e 75 65 0a 09 09 09 7d 0a 0a 09 09  ontinue....}....
20e0: 09 69 66 20 7b 21 5b 5f 69 73 48 61 73 68 20 24  .if {![_isHash $
20f0: 70 6b 67 49 6e 66 6f 28 68 61 73 68 29 5d 7d 20  pkgInfo(hash)]} 
2100: 7b 0a 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a 09  {.....continue..
2110: 09 09 7d 0a 0a 09 09 09 6c 61 70 70 65 6e 64 20  ..}.....lappend 
2120: 63 75 72 72 5f 70 61 63 6b 61 67 65 73 20 24 70  curr_packages $p
2130: 6b 67 49 6e 66 6f 28 68 61 73 68 29 0a 0a 09 09  kgInfo(hash)....
2140: 09 23 20 44 6f 20 6e 6f 74 20 64 6f 20 61 6e 79  .# Do not do any
2150: 20 61 64 64 69 74 69 6f 6e 61 6c 20 77 6f 72 6b   additional work
2160: 20 69 66 20 77 65 20 61 6c 72 65 61 64 79 20 68   if we already h
2170: 61 76 65 20 74 68 69 73 20 70 61 63 6b 61 67 65  ave this package
2180: 0a 09 09 09 73 65 74 20 65 78 69 73 74 69 6e 67  ....set existing
2190: 5f 70 61 63 6b 61 67 65 73 20 5b 64 62 20 65 76  _packages [db ev
21a0: 61 6c 20 7b 53 45 4c 45 43 54 20 70 61 63 6b 61  al {SELECT packa
21b0: 67 65 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73  ge FROM packages
21c0: 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20   WHERE hostname 
21d0: 3d 20 24 68 6f 73 74 6e 61 6d 65 20 41 4e 44 20  = $hostname AND 
21e0: 73 68 61 31 20 3d 20 24 70 6b 67 49 6e 66 6f 28  sha1 = $pkgInfo(
21f0: 68 61 73 68 29 3b 7d 5d 0a 09 09 09 69 66 20 7b  hash);}]....if {
2200: 5b 6c 73 65 61 72 63 68 20 2d 65 78 61 63 74 20  [lsearch -exact 
2210: 24 65 78 69 73 74 69 6e 67 5f 70 61 63 6b 61 67  $existing_packag
2220: 65 73 20 24 70 6b 67 49 6e 66 6f 28 70 61 63 6b  es $pkgInfo(pack
2230: 61 67 65 29 5d 20 21 3d 20 2d 31 7d 20 7b 0a 09  age)] != -1} {..
2240: 09 09 09 63 6f 6e 74 69 6e 75 65 0a 09 09 09 7d  ...continue....}
2250: 0a 0a 09 09 09 69 66 20 7b 24 70 6b 67 49 6e 66  .....if {$pkgInf
2260: 6f 28 69 73 4c 61 74 65 73 74 29 7d 20 7b 0a 09  o(isLatest)} {..
2270: 09 09 09 64 62 20 65 76 61 6c 20 7b 55 50 44 41  ...db eval {UPDA
2280: 54 45 20 70 61 63 6b 61 67 65 73 20 53 45 54 20  TE packages SET 
2290: 69 73 4c 61 74 65 73 74 20 3d 20 30 20 57 48 45  isLatest = 0 WHE
22a0: 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 68  RE hostname = $h
22b0: 6f 73 74 6e 61 6d 65 20 41 4e 44 20 70 61 63 6b  ostname AND pack
22c0: 61 67 65 20 3d 20 24 70 6b 67 49 6e 66 6f 28 24  age = $pkgInfo($
22d0: 70 61 63 6b 61 67 65 29 20 41 4e 44 20 6f 73 20  package) AND os 
22e0: 3d 20 24 70 6b 67 49 6e 66 6f 28 24 70 61 63 6b  = $pkgInfo($pack
22f0: 61 67 65 29 20 41 4e 44 20 63 70 75 41 72 63 68  age) AND cpuArch
2300: 20 3d 20 24 70 6b 67 49 6e 66 6f 28 63 70 75 41   = $pkgInfo(cpuA
2310: 72 63 68 29 3b 7d 0a 09 09 09 7d 0a 0a 09 09 09  rch);}....}.....
2320: 64 62 20 65 76 61 6c 20 7b 49 4e 53 45 52 54 20  db eval {INSERT 
2330: 49 4e 54 4f 20 70 61 63 6b 61 67 65 73 20 28 68  INTO packages (h
2340: 6f 73 74 6e 61 6d 65 2c 20 73 68 61 31 2c 20 70  ostname, sha1, p
2350: 61 63 6b 61 67 65 2c 20 76 65 72 73 69 6f 6e 2c  ackage, version,
2360: 20 6f 73 2c 20 63 70 75 41 72 63 68 2c 20 69 73   os, cpuArch, is
2370: 4c 61 74 65 73 74 2c 20 68 61 76 65 4d 61 6e 69  Latest, haveMani
2380: 66 65 73 74 29 20 56 41 4c 55 45 53 20 28 24 68  fest) VALUES ($h
2390: 6f 73 74 6e 61 6d 65 2c 20 24 70 6b 67 49 6e 66  ostname, $pkgInf
23a0: 6f 28 68 61 73 68 29 2c 20 24 70 6b 67 49 6e 66  o(hash), $pkgInf
23b0: 6f 28 70 61 63 6b 61 67 65 29 2c 20 24 70 6b 67  o(package), $pkg
23c0: 49 6e 66 6f 28 76 65 72 73 69 6f 6e 29 2c 20 24  Info(version), $
23d0: 70 6b 67 49 6e 66 6f 28 6f 73 29 2c 20 24 70 6b  pkgInfo(os), $pk
23e0: 67 49 6e 66 6f 28 63 70 75 41 72 63 68 29 2c 20  gInfo(cpuArch), 
23f0: 24 70 6b 67 49 6e 66 6f 28 69 73 4c 61 74 65 73  $pkgInfo(isLates
2400: 74 29 2c 20 30 29 3b 7d 0a 0a 09 09 7d 0a 0a 09  t), 0);}....}...
2410: 09 23 20 4c 6f 6f 6b 20 66 6f 72 20 70 61 63 6b  .# Look for pack
2420: 61 67 65 73 20 74 68 61 74 20 68 61 76 65 20 62  ages that have b
2430: 65 65 6e 20 64 65 6c 65 74 65 64 0a 09 09 73 65  een deleted...se
2440: 74 20 66 6f 75 6e 64 5f 70 61 63 6b 61 67 65 73  t found_packages
2450: 20 5b 64 62 20 65 76 61 6c 20 7b 53 45 4c 45 43   [db eval {SELEC
2460: 54 20 73 68 61 31 20 46 52 4f 4d 20 70 61 63 6b  T sha1 FROM pack
2470: 61 67 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e  ages WHERE hostn
2480: 61 6d 65 20 3d 20 24 68 6f 73 74 6e 61 6d 65 3b  ame = $hostname;
2490: 7d 5d 0a 09 09 66 6f 72 65 61 63 68 20 70 61 63  }]...foreach pac
24a0: 6b 61 67 65 20 24 66 6f 75 6e 64 5f 70 61 63 6b  kage $found_pack
24b0: 61 67 65 73 20 7b 0a 09 09 09 73 65 74 20 66 6f  ages {....set fo
24c0: 75 6e 64 5f 70 61 63 6b 61 67 65 73 5f 61 72 72  und_packages_arr
24d0: 28 24 70 61 63 6b 61 67 65 29 20 31 0a 09 09 7d  ($package) 1...}
24e0: 0a 0a 09 09 66 6f 72 65 61 63 68 20 70 61 63 6b  ....foreach pack
24f0: 61 67 65 20 24 63 75 72 72 5f 70 61 63 6b 61 67  age $curr_packag
2500: 65 73 20 7b 0a 09 09 09 75 6e 73 65 74 20 2d 6e  es {....unset -n
2510: 6f 63 6f 6d 70 6c 61 69 6e 20 66 6f 75 6e 64 5f  ocomplain found_
2520: 70 61 63 6b 61 67 65 73 5f 61 72 72 28 24 70 61  packages_arr($pa
2530: 63 6b 61 67 65 29 0a 09 09 7d 0a 0a 09 09 66 6f  ckage)...}....fo
2540: 72 65 61 63 68 20 70 61 63 6b 61 67 65 20 5b 61  reach package [a
2550: 72 72 61 79 20 6e 61 6d 65 73 20 66 6f 75 6e 64  rray names found
2560: 5f 70 61 63 6b 61 67 65 73 5f 61 72 72 5d 20 7b  _packages_arr] {
2570: 0a 09 09 09 64 62 20 65 76 61 6c 20 7b 44 45 4c  ....db eval {DEL
2580: 45 54 45 20 46 52 4f 4d 20 70 61 63 6b 61 67 65  ETE FROM package
2590: 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65  s WHERE hostname
25a0: 20 3d 20 24 68 6f 73 74 6e 61 6d 65 20 41 4e 44   = $hostname AND
25b0: 20 73 68 61 31 20 3d 20 24 70 61 63 6b 61 67 65   sha1 = $package
25c0: 3b 7d 0a 09 09 7d 0a 0a 09 09 64 62 20 65 76 61  ;}...}....db eva
25d0: 6c 20 7b 49 4e 53 45 52 54 20 4f 52 20 52 45 50  l {INSERT OR REP
25e0: 4c 41 43 45 20 49 4e 54 4f 20 73 69 74 65 73 20  LACE INTO sites 
25f0: 28 68 6f 73 74 6e 61 6d 65 2c 20 6c 61 73 74 55  (hostname, lastU
2600: 70 64 61 74 65 2c 20 74 74 6c 29 20 56 41 4c 55  pdate, ttl) VALU
2610: 45 53 20 28 24 68 6f 73 74 6e 61 6d 65 2c 20 24  ES ($hostname, $
2620: 6e 6f 77 2c 20 24 3a 3a 61 70 70 66 73 3a 3a 74  now, $::appfs::t
2630: 74 6c 29 3b 7d 0a 0a 09 09 61 70 70 66 73 64 3a  tl);}....appfsd:
2640: 3a 67 65 74 5f 70 61 74 68 5f 69 6e 66 6f 5f 63  :get_path_info_c
2650: 61 63 68 65 5f 66 6c 75 73 68 0a 0a 09 09 72 65  ache_flush....re
2660: 74 75 72 6e 20 43 4f 4d 50 4c 45 54 45 0a 09 7d  turn COMPLETE..}
2670: 0a 0a 09 70 72 6f 63 20 67 65 74 70 6b 67 6d 61  ...proc getpkgma
2680: 6e 69 66 65 73 74 20 7b 68 6f 73 74 6e 61 6d 65  nifest {hostname
2690: 20 70 61 63 6b 61 67 65 5f 73 68 61 31 7d 20 7b   package_sha1} {
26a0: 0a 09 09 73 65 74 20 68 61 76 65 4d 61 6e 69 66  ...set haveManif
26b0: 65 73 74 20 5b 64 62 20 6f 6e 65 63 6f 6c 75 6d  est [db onecolum
26c0: 6e 20 7b 53 45 4c 45 43 54 20 68 61 76 65 4d 61  n {SELECT haveMa
26d0: 6e 69 66 65 73 74 20 46 52 4f 4d 20 70 61 63 6b  nifest FROM pack
26e0: 61 67 65 73 20 57 48 45 52 45 20 73 68 61 31 20  ages WHERE sha1 
26f0: 3d 20 24 70 61 63 6b 61 67 65 5f 73 68 61 31 20  = $package_sha1 
2700: 4c 49 4d 49 54 20 31 3b 7d 5d 0a 0a 09 09 69 66  LIMIT 1;}]....if
2710: 20 7b 24 68 61 76 65 4d 61 6e 69 66 65 73 74 20   {$haveManifest 
2720: 3d 3d 20 22 31 22 7d 20 7b 0a 09 09 09 72 65 74  == "1"} {....ret
2730: 75 72 6e 20 43 4f 4d 50 4c 45 54 45 0a 09 09 7d  urn COMPLETE...}
2740: 0a 0a 09 09 69 66 20 7b 21 5b 5f 69 73 48 61 73  ....if {![_isHas
2750: 68 20 24 70 61 63 6b 61 67 65 5f 73 68 61 31 5d  h $package_sha1]
2760: 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 46 41  } {....return FA
2770: 49 4c 0a 09 09 7d 0a 0a 09 09 73 65 74 20 66 69  IL...}....set fi
2780: 6c 65 20 5b 64 6f 77 6e 6c 6f 61 64 20 24 68 6f  le [download $ho
2790: 73 74 6e 61 6d 65 20 24 70 61 63 6b 61 67 65 5f  stname $package_
27a0: 73 68 61 31 5d 0a 09 09 73 65 74 20 66 64 20 5b  sha1]...set fd [
27b0: 6f 70 65 6e 20 24 66 69 6c 65 5d 0a 09 09 73 65  open $file]...se
27c0: 74 20 70 6b 67 64 61 74 61 20 5b 72 65 61 64 20  t pkgdata [read 
27d0: 24 66 64 5d 0a 09 09 63 6c 6f 73 65 20 24 66 64  $fd]...close $fd
27e0: 0a 0a 09 09 64 62 20 74 72 61 6e 73 61 63 74 69  ....db transacti
27f0: 6f 6e 20 7b 0a 09 09 09 66 6f 72 65 61 63 68 20  on {....foreach 
2800: 6c 69 6e 65 20 5b 73 70 6c 69 74 20 24 70 6b 67  line [split $pkg
2810: 64 61 74 61 20 22 5c 6e 22 5d 20 7b 0a 09 09 09  data "\n"] {....
2820: 09 73 65 74 20 6c 69 6e 65 20 5b 73 74 72 69 6e  .set line [strin
2830: 67 20 74 72 69 6d 20 24 6c 69 6e 65 5d 0a 0a 09  g trim $line]...
2840: 09 09 09 69 66 20 7b 24 6c 69 6e 65 20 3d 3d 20  ...if {$line == 
2850: 22 22 7d 20 7b 0a 09 09 09 09 09 63 6f 6e 74 69  ""} {......conti
2860: 6e 75 65 0a 09 09 09 09 7d 0a 0a 09 09 09 09 73  nue.....}......s
2870: 65 74 20 77 6f 72 6b 20 5b 73 70 6c 69 74 20 24  et work [split $
2880: 6c 69 6e 65 20 22 2c 22 5d 0a 0a 09 09 09 09 75  line ","]......u
2890: 6e 73 65 74 20 2d 6e 6f 63 6f 6d 70 6c 61 69 6e  nset -nocomplain
28a0: 20 66 69 6c 65 49 6e 66 6f 0a 09 09 09 09 73 65   fileInfo.....se
28b0: 74 20 66 69 6c 65 49 6e 66 6f 28 74 79 70 65 29  t fileInfo(type)
28c0: 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 30   [lindex $work 0
28d0: 5d 0a 09 09 09 09 73 65 74 20 66 69 6c 65 49 6e  ].....set fileIn
28e0: 66 6f 28 74 69 6d 65 29 20 5b 6c 69 6e 64 65 78  fo(time) [lindex
28f0: 20 24 77 6f 72 6b 20 31 5d 0a 0a 09 09 09 09 73   $work 1]......s
2900: 65 74 20 77 6f 72 6b 20 5b 6c 72 61 6e 67 65 20  et work [lrange 
2910: 24 77 6f 72 6b 20 32 20 65 6e 64 5d 0a 09 09 09  $work 2 end]....
2920: 09 73 77 69 74 63 68 20 2d 2d 20 24 66 69 6c 65  .switch -- $file
2930: 49 6e 66 6f 28 74 79 70 65 29 20 7b 0a 09 09 09  Info(type) {....
2940: 09 09 22 23 6d 61 6e 69 66 65 73 74 6d 65 74 61  .."#manifestmeta
2950: 64 61 74 61 22 20 7b 0a 09 09 09 09 09 09 75 6e  data" {.......un
2960: 73 65 74 20 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20  set -nocomplain 
2970: 66 69 6c 65 49 6e 66 6f 0a 09 09 09 09 09 09 63  fileInfo.......c
2980: 6f 6e 74 69 6e 75 65 0a 09 09 09 09 09 7d 0a 09  ontinue......}..
2990: 09 09 09 09 22 66 69 6c 65 22 20 7b 0a 09 09 09  ...."file" {....
29a0: 09 09 09 73 65 74 20 66 69 6c 65 49 6e 66 6f 28  ...set fileInfo(
29b0: 73 69 7a 65 29 20 5b 6c 69 6e 64 65 78 20 24 77  size) [lindex $w
29c0: 6f 72 6b 20 30 5d 0a 09 09 09 09 09 09 73 65 74  ork 0].......set
29d0: 20 66 69 6c 65 49 6e 66 6f 28 70 65 72 6d 73 29   fileInfo(perms)
29e0: 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 31   [lindex $work 1
29f0: 5d 0a 09 09 09 09 09 09 73 65 74 20 66 69 6c 65  ].......set file
2a00: 49 6e 66 6f 28 73 68 61 31 29 20 5b 6c 69 6e 64  Info(sha1) [lind
2a10: 65 78 20 24 77 6f 72 6b 20 32 5d 0a 0a 09 09 09  ex $work 2].....
2a20: 09 09 09 73 65 74 20 77 6f 72 6b 20 5b 6c 72 61  ...set work [lra
2a30: 6e 67 65 20 24 77 6f 72 6b 20 33 20 65 6e 64 5d  nge $work 3 end]
2a40: 0a 09 09 09 09 09 7d 0a 09 09 09 09 09 22 73 79  ......}......"sy
2a50: 6d 6c 69 6e 6b 22 20 7b 0a 09 09 09 09 09 09 73  mlink" {.......s
2a60: 65 74 20 66 69 6c 65 49 6e 66 6f 28 73 6f 75 72  et fileInfo(sour
2a70: 63 65 29 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72  ce) [lindex $wor
2a80: 6b 20 30 5d 0a 09 09 09 09 09 09 73 65 74 20 77  k 0].......set w
2a90: 6f 72 6b 20 5b 6c 72 61 6e 67 65 20 24 77 6f 72  ork [lrange $wor
2aa0: 6b 20 31 20 65 6e 64 5d 0a 09 09 09 09 09 7d 0a  k 1 end]......}.
2ab0: 09 09 09 09 09 22 64 69 72 65 63 74 6f 72 79 22  ....."directory"
2ac0: 20 7b 0a 09 09 09 09 09 09 23 20 4e 6f 20 65 78   {.......# No ex
2ad0: 74 72 61 20 64 61 74 61 20 72 65 71 75 69 72 65  tra data require
2ae0: 64 0a 09 09 09 09 09 7d 0a 09 09 09 09 09 64 65  d......}......de
2af0: 66 61 75 6c 74 20 7b 0a 09 09 09 09 09 09 23 20  fault {.......# 
2b00: 48 61 6e 64 6c 65 20 75 6e 6b 6e 6f 77 6e 20 74  Handle unknown t
2b10: 79 70 65 73 0a 09 09 09 09 09 09 69 66 20 7b 5b  ypes.......if {[
2b20: 73 74 72 69 6e 67 20 69 6e 64 65 78 20 24 66 69  string index $fi
2b30: 6c 65 49 6e 66 6f 28 74 79 70 65 29 20 30 5d 20  leInfo(type) 0] 
2b40: 3d 3d 20 22 23 22 7d 20 7b 0a 09 09 09 09 09 09  == "#"} {.......
2b50: 09 23 20 4d 65 74 61 64 61 74 61 20 74 79 70 65  .# Metadata type
2b60: 2c 20 69 67 6e 6f 72 65 0a 09 09 09 09 09 09 09  , ignore........
2b70: 23 20 69 74 20 69 66 20 77 65 20 64 6f 6e 27 74  # it if we don't
2b80: 0a 09 09 09 09 09 09 09 23 20 75 6e 64 65 72 73  ........# unders
2b90: 74 61 6e 64 20 74 68 69 73 20 74 79 70 65 0a 09  tand this type..
2ba0: 09 09 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a 09  ......continue..
2bb0: 09 09 09 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09  .....} else {...
2bc0: 09 09 09 09 09 23 20 55 6e 6b 6e 6f 77 6e 20 74  .....# Unknown t
2bd0: 79 70 65 2c 0a 09 09 09 09 09 09 09 23 20 67 65  ype,........# ge
2be0: 6e 65 72 61 74 65 20 61 6e 20 65 72 72 6f 72 0a  nerate an error.
2bf0: 09 09 09 09 09 09 09 65 72 72 6f 72 20 22 4d 61  .......error "Ma
2c00: 6e 69 66 65 73 74 20 63 61 6e 6e 6f 74 20 62 65  nifest cannot be
2c10: 20 70 61 72 73 65 64 22 0a 09 09 09 09 09 09 7d   parsed".......}
2c20: 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 0a 0a 09  ......}.....}...
2c30: 09 09 09 73 65 74 20 66 69 6c 65 49 6e 66 6f 28  ...set fileInfo(
2c40: 6e 61 6d 65 29 20 5b 6a 6f 69 6e 20 24 77 6f 72  name) [join $wor
2c50: 6b 20 22 2c 22 5d 0a 09 09 09 09 73 65 74 20 66  k ","].....set f
2c60: 69 6c 65 49 6e 66 6f 28 6e 61 6d 65 29 20 5b 73  ileInfo(name) [s
2c70: 70 6c 69 74 20 5b 73 74 72 69 6e 67 20 74 72 69  plit [string tri
2c80: 6d 20 24 66 69 6c 65 49 6e 66 6f 28 6e 61 6d 65  m $fileInfo(name
2c90: 29 20 22 2f 22 5d 20 22 2f 22 5d 0a 09 09 09 09  ) "/"] "/"].....
2ca0: 73 65 74 20 66 69 6c 65 49 6e 66 6f 28 64 69 72  set fileInfo(dir
2cb0: 65 63 74 6f 72 79 29 20 5b 6a 6f 69 6e 20 5b 6c  ectory) [join [l
2cc0: 72 61 6e 67 65 20 24 66 69 6c 65 49 6e 66 6f 28  range $fileInfo(
2cd0: 6e 61 6d 65 29 20 30 20 65 6e 64 2d 31 5d 20 22  name) 0 end-1] "
2ce0: 2f 22 5d 0a 09 09 09 09 73 65 74 20 66 69 6c 65  /"].....set file
2cf0: 49 6e 66 6f 28 6e 61 6d 65 29 20 5b 6c 69 6e 64  Info(name) [lind
2d00: 65 78 20 24 66 69 6c 65 49 6e 66 6f 28 6e 61 6d  ex $fileInfo(nam
2d10: 65 29 20 65 6e 64 5d 0a 0a 09 09 09 09 64 62 20  e) end]......db 
2d20: 65 76 61 6c 20 7b 49 4e 53 45 52 54 20 49 4e 54  eval {INSERT INT
2d30: 4f 20 66 69 6c 65 73 20 28 70 61 63 6b 61 67 65  O files (package
2d40: 5f 73 68 61 31 2c 20 74 79 70 65 2c 20 74 69 6d  _sha1, type, tim
2d50: 65 2c 20 73 6f 75 72 63 65 2c 20 73 69 7a 65 2c  e, source, size,
2d60: 20 70 65 72 6d 73 2c 20 66 69 6c 65 5f 73 68 61   perms, file_sha
2d70: 31 2c 20 66 69 6c 65 5f 6e 61 6d 65 2c 20 66 69  1, file_name, fi
2d80: 6c 65 5f 64 69 72 65 63 74 6f 72 79 29 20 56 41  le_directory) VA
2d90: 4c 55 45 53 20 28 24 70 61 63 6b 61 67 65 5f 73  LUES ($package_s
2da0: 68 61 31 2c 20 24 66 69 6c 65 49 6e 66 6f 28 74  ha1, $fileInfo(t
2db0: 79 70 65 29 2c 20 24 66 69 6c 65 49 6e 66 6f 28  ype), $fileInfo(
2dc0: 74 69 6d 65 29 2c 20 24 66 69 6c 65 49 6e 66 6f  time), $fileInfo
2dd0: 28 73 6f 75 72 63 65 29 2c 20 24 66 69 6c 65 49  (source), $fileI
2de0: 6e 66 6f 28 73 69 7a 65 29 2c 20 24 66 69 6c 65  nfo(size), $file
2df0: 49 6e 66 6f 28 70 65 72 6d 73 29 2c 20 24 66 69  Info(perms), $fi
2e00: 6c 65 49 6e 66 6f 28 73 68 61 31 29 2c 20 24 66  leInfo(sha1), $f
2e10: 69 6c 65 49 6e 66 6f 28 6e 61 6d 65 29 2c 20 24  ileInfo(name), $
2e20: 66 69 6c 65 49 6e 66 6f 28 64 69 72 65 63 74 6f  fileInfo(directo
2e30: 72 79 29 20 29 3b 7d 0a 09 09 09 09 64 62 20 65  ry) );}.....db e
2e40: 76 61 6c 20 7b 55 50 44 41 54 45 20 70 61 63 6b  val {UPDATE pack
2e50: 61 67 65 73 20 53 45 54 20 68 61 76 65 4d 61 6e  ages SET haveMan
2e60: 69 66 65 73 74 20 3d 20 31 20 57 48 45 52 45 20  ifest = 1 WHERE 
2e70: 73 68 61 31 20 3d 20 24 70 61 63 6b 61 67 65 5f  sha1 = $package_
2e80: 73 68 61 31 3b 7d 0a 09 09 09 7d 0a 09 09 7d 0a  sha1;}....}...}.
2e90: 0a 09 09 61 70 70 66 73 64 3a 3a 67 65 74 5f 70  ...appfsd::get_p
2ea0: 61 74 68 5f 69 6e 66 6f 5f 63 61 63 68 65 5f 66  ath_info_cache_f
2eb0: 6c 75 73 68 0a 0a 09 09 72 65 74 75 72 6e 20 43  lush....return C
2ec0: 4f 4d 50 4c 45 54 45 0a 09 7d 0a 0a 09 70 72 6f  OMPLETE..}...pro
2ed0: 63 20 5f 6c 6f 63 61 6c 70 61 74 68 20 7b 70 61  c _localpath {pa
2ee0: 63 6b 61 67 65 20 68 6f 73 74 6e 61 6d 65 20 66  ckage hostname f
2ef0: 69 6c 65 7d 20 7b 0a 09 09 73 65 74 20 64 69 72  ile} {...set dir
2f00: 20 22 22 0a 09 09 63 61 74 63 68 20 7b 0a 09 09   ""...catch {...
2f10: 09 73 65 74 20 68 6f 6d 65 64 69 72 20 5b 3a 3a  .set homedir [::
2f20: 61 70 70 66 73 3a 3a 75 73 65 72 3a 3a 67 65 74  appfs::user::get
2f30: 5f 68 6f 6d 65 64 69 72 5d 0a 09 09 09 73 65 74  _homedir]....set
2f40: 20 64 69 72 20 5b 66 69 6c 65 20 6a 6f 69 6e 20   dir [file join 
2f50: 24 68 6f 6d 65 64 69 72 20 2e 61 70 70 66 73 20  $homedir .appfs 
2f60: 22 2e 2f 24 7b 70 61 63 6b 61 67 65 7d 40 24 7b  "./${package}@${
2f70: 68 6f 73 74 6e 61 6d 65 7d 22 20 22 2e 2f 24 7b  hostname}" "./${
2f80: 66 69 6c 65 7d 22 5d 0a 09 09 7d 0a 09 09 72 65  file}"]...}...re
2f90: 74 75 72 6e 20 24 64 69 72 0a 09 7d 0a 0a 09 70  turn $dir..}...p
2fa0: 72 6f 63 20 5f 77 68 69 74 65 6f 75 74 70 61 74  roc _whiteoutpat
2fb0: 68 20 7b 70 61 63 6b 61 67 65 20 68 6f 73 74 6e  h {package hostn
2fc0: 61 6d 65 20 66 69 6c 65 7d 20 7b 0a 09 09 73 65  ame file} {...se
2fd0: 74 20 64 69 72 20 22 22 0a 09 09 63 61 74 63 68  t dir ""...catch
2fe0: 20 7b 0a 09 09 09 73 65 74 20 68 6f 6d 65 64 69   {....set homedi
2ff0: 72 20 5b 3a 3a 61 70 70 66 73 3a 3a 75 73 65 72  r [::appfs::user
3000: 3a 3a 67 65 74 5f 68 6f 6d 65 64 69 72 5d 0a 09  ::get_homedir]..
3010: 09 09 73 65 74 20 64 69 72 20 5b 66 69 6c 65 20  ..set dir [file 
3020: 6a 6f 69 6e 20 24 68 6f 6d 65 64 69 72 20 2e 61  join $homedir .a
3030: 70 70 66 73 20 22 2e 2f 24 7b 70 61 63 6b 61 67  ppfs "./${packag
3040: 65 7d 40 24 7b 68 6f 73 74 6e 61 6d 65 7d 22 20  e}@${hostname}" 
3050: 22 2e 41 50 50 46 53 2e 57 48 49 54 45 4f 55 54  ".APPFS.WHITEOUT
3060: 22 20 22 2e 2f 24 7b 66 69 6c 65 7d 2e 41 50 50  " "./${file}.APP
3070: 46 53 2e 57 48 49 54 45 4f 55 54 22 5d 0a 09 09  FS.WHITEOUT"]...
3080: 7d 0a 09 09 72 65 74 75 72 6e 20 24 64 69 72 0a  }...return $dir.
3090: 09 7d 0a 0a 09 70 72 6f 63 20 5f 70 61 72 73 65  .}...proc _parse
30a0: 70 61 74 68 20 7b 70 61 74 68 7d 20 7b 0a 09 09  path {path} {...
30b0: 73 65 74 20 70 61 74 68 20 5b 73 74 72 69 6e 67  set path [string
30c0: 20 74 72 69 6d 20 24 70 61 74 68 20 22 2f 22 5d   trim $path "/"]
30d0: 0a 09 09 73 65 74 20 70 61 74 68 20 5b 73 70 6c  ...set path [spl
30e0: 69 74 20 24 70 61 74 68 20 22 2f 22 5d 0a 09 09  it $path "/"]...
30f0: 73 65 74 20 70 61 74 68 6c 65 6e 20 5b 6c 6c 65  set pathlen [lle
3100: 6e 67 74 68 20 24 70 61 74 68 5d 0a 0a 09 09 61  ngth $path]....a
3110: 72 72 61 79 20 73 65 74 20 72 65 74 76 61 6c 20  rray set retval 
3120: 5b 6c 69 73 74 20 5f 63 68 69 6c 64 72 65 6e 20  [list _children 
3130: 73 69 74 65 73 20 5f 74 79 70 65 20 74 6f 70 6c  sites _type topl
3140: 65 76 65 6c 5d 0a 0a 09 09 69 66 20 7b 24 70 61  evel]....if {$pa
3150: 74 68 6c 65 6e 20 3e 20 30 7d 20 7b 0a 09 09 09  thlen > 0} {....
3160: 73 65 74 20 72 65 74 76 61 6c 28 68 6f 73 74 6e  set retval(hostn
3170: 61 6d 65 29 20 5b 6c 69 6e 64 65 78 20 24 70 61  ame) [lindex $pa
3180: 74 68 20 30 5d 0a 09 09 09 73 65 74 20 72 65 74  th 0]....set ret
3190: 76 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29 20 70  val(_children) p
31a0: 61 63 6b 61 67 65 73 0a 09 09 09 73 65 74 20 72  ackages....set r
31b0: 65 74 76 61 6c 28 5f 74 79 70 65 29 20 73 69 74  etval(_type) sit
31c0: 65 73 0a 0a 09 09 09 69 66 20 7b 24 70 61 74 68  es.....if {$path
31d0: 6c 65 6e 20 3e 20 31 7d 20 7b 0a 09 09 09 09 73  len > 1} {.....s
31e0: 65 74 20 70 61 63 6b 61 67 65 20 5b 6c 69 6e 64  et package [lind
31f0: 65 78 20 24 70 61 74 68 20 31 5d 0a 09 09 09 09  ex $path 1].....
3200: 69 66 20 7b 5b 73 74 72 69 6e 67 20 6c 65 6e 67  if {[string leng
3210: 74 68 20 24 70 61 63 6b 61 67 65 5d 20 3d 3d 20  th $package] == 
3220: 22 34 30 22 20 26 26 20 5b 72 65 67 65 78 70 20  "40" && [regexp 
3230: 7b 5e 5b 61 2d 66 41 2d 46 30 2d 39 5d 2a 24 7d  {^[a-fA-F0-9]*$}
3240: 20 24 70 61 63 6b 61 67 65 5d 7d 20 7b 0a 09 09   $package]} {...
3250: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 70 61  ...set retval(pa
3260: 63 6b 61 67 65 5f 73 68 61 31 29 20 24 70 61 63  ckage_sha1) $pac
3270: 6b 61 67 65 0a 09 09 09 09 09 73 65 74 20 72 65  kage......set re
3280: 74 76 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29 20  tval(_children) 
3290: 66 69 6c 65 73 0a 09 09 09 09 09 73 65 74 20 72  files......set r
32a0: 65 74 76 61 6c 28 5f 74 79 70 65 29 20 66 69 6c  etval(_type) fil
32b0: 65 73 0a 0a 09 09 09 09 09 3a 3a 61 70 70 66 73  es.......::appfs
32c0: 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45 4c 45 43  ::db eval {SELEC
32d0: 54 20 70 61 63 6b 61 67 65 2c 20 6f 73 2c 20 63  T package, os, c
32e0: 70 75 41 72 63 68 2c 20 76 65 72 73 69 6f 6e 20  puArch, version 
32f0: 46 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48  FROM packages WH
3300: 45 52 45 20 73 68 61 31 20 3d 20 24 72 65 74 76  ERE sha1 = $retv
3310: 61 6c 28 70 61 63 6b 61 67 65 5f 73 68 61 31 29  al(package_sha1)
3320: 3b 7d 20 70 6b 67 69 6e 66 6f 20 7b 7d 0a 09 09  ;} pkginfo {}...
3330: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 70 61  ...set retval(pa
3340: 63 6b 61 67 65 29 20 24 70 6b 67 69 6e 66 6f 28  ckage) $pkginfo(
3350: 70 61 63 6b 61 67 65 29 0a 09 09 09 09 09 73 65  package)......se
3360: 74 20 72 65 74 76 61 6c 28 6f 73 29 20 24 70 6b  t retval(os) $pk
3370: 67 69 6e 66 6f 28 6f 73 29 0a 09 09 09 09 09 73  ginfo(os)......s
3380: 65 74 20 72 65 74 76 61 6c 28 63 70 75 29 20 24  et retval(cpu) $
3390: 70 6b 67 69 6e 66 6f 28 63 70 75 41 72 63 68 29  pkginfo(cpuArch)
33a0: 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
33b0: 28 76 65 72 73 69 6f 6e 29 20 24 70 6b 67 69 6e  (version) $pkgin
33c0: 66 6f 28 76 65 72 73 69 6f 6e 29 0a 0a 09 09 09  fo(version).....
33d0: 09 09 69 66 20 7b 24 70 61 74 68 6c 65 6e 20 3e  ..if {$pathlen >
33e0: 20 32 7d 20 7b 0a 09 09 09 09 09 09 73 65 74 20   2} {.......set 
33f0: 72 65 74 76 61 6c 28 66 69 6c 65 29 20 5b 6a 6f  retval(file) [jo
3400: 69 6e 20 5b 6c 72 61 6e 67 65 20 24 70 61 74 68  in [lrange $path
3410: 20 32 20 65 6e 64 5d 20 22 2f 22 5d 0a 09 09 09   2 end] "/"]....
3420: 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 09  ..} else {......
3430: 09 73 65 74 20 72 65 74 76 61 6c 28 66 69 6c 65  .set retval(file
3440: 29 20 22 22 0a 09 09 09 09 09 7d 0a 09 09 09 09  ) ""......}.....
3450: 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 09 73 65  } else {......se
3460: 74 20 72 65 74 76 61 6c 28 70 61 63 6b 61 67 65  t retval(package
3470: 29 20 24 70 61 63 6b 61 67 65 0a 09 09 09 09 09  ) $package......
3480: 73 65 74 20 72 65 74 76 61 6c 28 5f 63 68 69 6c  set retval(_chil
3490: 64 72 65 6e 29 20 6f 73 2d 63 70 75 0a 09 09 09  dren) os-cpu....
34a0: 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 74 79  ..set retval(_ty
34b0: 70 65 29 20 70 61 63 6b 61 67 65 73 0a 0a 09 09  pe) packages....
34c0: 09 09 09 69 66 20 7b 24 70 61 74 68 6c 65 6e 20  ...if {$pathlen 
34d0: 3e 20 32 7d 20 7b 0a 09 09 09 09 09 09 73 65 74  > 2} {.......set
34e0: 20 6f 73 5f 63 70 75 20 5b 6c 69 6e 64 65 78 20   os_cpu [lindex 
34f0: 24 70 61 74 68 20 32 5d 0a 09 09 09 09 09 09 73  $path 2].......s
3500: 65 74 20 6f 73 5f 63 70 75 20 5b 73 70 6c 69 74  et os_cpu [split
3510: 20 24 6f 73 5f 63 70 75 20 22 2d 22 5d 0a 0a 09   $os_cpu "-"]...
3520: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
3530: 6f 73 29 20 5b 6c 69 6e 64 65 78 20 24 6f 73 5f  os) [lindex $os_
3540: 63 70 75 20 30 5d 0a 09 09 09 09 09 09 73 65 74  cpu 0].......set
3550: 20 72 65 74 76 61 6c 28 63 70 75 29 20 5b 6c 69   retval(cpu) [li
3560: 6e 64 65 78 20 24 6f 73 5f 63 70 75 20 31 5d 0a  ndex $os_cpu 1].
3570: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
3580: 28 5f 63 68 69 6c 64 72 65 6e 29 20 76 65 72 73  (_children) vers
3590: 69 6f 6e 73 0a 09 09 09 09 09 09 73 65 74 20 72  ions.......set r
35a0: 65 74 76 61 6c 28 5f 74 79 70 65 29 20 6f 73 2d  etval(_type) os-
35b0: 63 70 75 0a 0a 09 09 09 09 09 09 69 66 20 7b 24  cpu........if {$
35c0: 70 61 74 68 6c 65 6e 20 3e 20 33 7d 20 7b 0a 09  pathlen > 3} {..
35d0: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
35e0: 28 76 65 72 73 69 6f 6e 29 20 5b 6c 69 6e 64 65  (version) [linde
35f0: 78 20 24 70 61 74 68 20 33 5d 0a 09 09 09 09 09  x $path 3]......
3600: 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 63 68  ..set retval(_ch
3610: 69 6c 64 72 65 6e 29 20 66 69 6c 65 73 0a 09 09  ildren) files...
3620: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
3630: 5f 74 79 70 65 29 20 76 65 72 73 69 6f 6e 73 0a  _type) versions.
3640: 0a 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
3650: 61 6c 28 70 61 63 6b 61 67 65 5f 73 68 61 31 29  al(package_sha1)
3660: 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20 6f 6e   [::appfs::db on
3670: 65 63 6f 6c 75 6d 6e 20 7b 53 45 4c 45 43 54 20  ecolumn {SELECT 
3680: 73 68 61 31 20 46 52 4f 4d 20 70 61 63 6b 61 67  sha1 FROM packag
3690: 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d  es WHERE hostnam
36a0: 65 20 3d 20 24 72 65 74 76 61 6c 28 68 6f 73 74  e = $retval(host
36b0: 6e 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61 67  name) AND packag
36c0: 65 20 3d 20 24 72 65 74 76 61 6c 28 70 61 63 6b  e = $retval(pack
36d0: 61 67 65 29 20 41 4e 44 20 6f 73 20 3d 20 24 72  age) AND os = $r
36e0: 65 74 76 61 6c 28 6f 73 29 20 41 4e 44 20 63 70  etval(os) AND cp
36f0: 75 41 72 63 68 20 3d 20 24 72 65 74 76 61 6c 28  uArch = $retval(
3700: 63 70 75 29 20 41 4e 44 20 76 65 72 73 69 6f 6e  cpu) AND version
3710: 20 3d 20 24 72 65 74 76 61 6c 28 76 65 72 73 69   = $retval(versi
3720: 6f 6e 29 3b 7d 5d 0a 09 09 09 09 09 09 09 69 66  on);}]........if
3730: 20 7b 24 72 65 74 76 61 6c 28 70 61 63 6b 61 67   {$retval(packag
3740: 65 5f 73 68 61 31 29 20 3d 3d 20 22 22 7d 20 7b  e_sha1) == ""} {
3750: 0a 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74  .........set ret
3760: 76 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29 20 64  val(_children) d
3770: 65 61 64 0a 09 09 09 09 09 09 09 09 72 65 74 75  ead.........retu
3780: 72 6e 20 5b 61 72 72 61 79 20 67 65 74 20 72 65  rn [array get re
3790: 74 76 61 6c 5d 0a 09 09 09 09 09 09 09 7d 0a 0a  tval]........}..
37a0: 09 09 09 09 09 09 09 69 66 20 7b 24 70 61 74 68  .......if {$path
37b0: 6c 65 6e 20 3e 20 34 7d 20 7b 0a 09 09 09 09 09  len > 4} {......
37c0: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 74  ...set retval(_t
37d0: 79 70 65 29 20 66 69 6c 65 73 0a 09 09 09 09 09  ype) files......
37e0: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 66 69  ...set retval(fi
37f0: 6c 65 29 20 5b 6a 6f 69 6e 20 5b 6c 72 61 6e 67  le) [join [lrang
3800: 65 20 24 70 61 74 68 20 34 20 65 6e 64 5d 20 22  e $path 4 end] "
3810: 2f 22 5d 0a 09 09 09 09 09 09 09 7d 20 65 6c 73  /"]........} els
3820: 65 20 7b 0a 09 09 09 09 09 09 09 09 73 65 74 20  e {.........set 
3830: 72 65 74 76 61 6c 28 5f 74 79 70 65 29 20 66 69  retval(_type) fi
3840: 6c 65 73 0a 09 09 09 09 09 09 09 09 73 65 74 20  les.........set 
3850: 72 65 74 76 61 6c 28 66 69 6c 65 29 20 22 22 0a  retval(file) "".
3860: 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09 7d  .......}.......}
3870: 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 0a 09 09  ......}.....}...
3880: 09 7d 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e  .}...}....return
3890: 20 5b 61 72 72 61 79 20 67 65 74 20 72 65 74 76   [array get retv
38a0: 61 6c 5d 0a 09 7d 0a 0a 09 70 72 6f 63 20 67 65  al]..}...proc ge
38b0: 74 63 68 69 6c 64 72 65 6e 20 7b 64 69 72 7d 20  tchildren {dir} 
38c0: 7b 0a 09 09 61 72 72 61 79 20 73 65 74 20 70 61  {...array set pa
38d0: 74 68 69 6e 66 6f 20 5b 5f 70 61 72 73 65 70 61  thinfo [_parsepa
38e0: 74 68 20 24 64 69 72 5d 0a 0a 09 09 73 77 69 74  th $dir]....swit
38f0: 63 68 20 2d 2d 20 24 70 61 74 68 69 6e 66 6f 28  ch -- $pathinfo(
3900: 5f 63 68 69 6c 64 72 65 6e 29 20 7b 0a 09 09 09  _children) {....
3910: 22 73 69 74 65 73 22 20 7b 0a 09 09 09 09 72 65  "sites" {.....re
3920: 74 75 72 6e 20 5b 3a 3a 61 70 70 66 73 3a 3a 64  turn [::appfs::d
3930: 62 20 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 44  b eval {SELECT D
3940: 49 53 54 49 4e 43 54 20 68 6f 73 74 6e 61 6d 65  ISTINCT hostname
3950: 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73 3b 7d   FROM packages;}
3960: 5d 0a 09 09 09 7d 0a 09 09 09 22 70 61 63 6b 61  ]....}...."packa
3970: 67 65 73 22 20 7b 0a 09 09 09 09 63 61 74 63 68  ges" {.....catch
3980: 20 7b 0a 09 09 09 09 09 3a 3a 61 70 70 66 73 3a   {......::appfs:
3990: 3a 67 65 74 69 6e 64 65 78 20 24 70 61 74 68 69  :getindex $pathi
39a0: 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 0a 09 09  nfo(hostname)...
39b0: 09 09 7d 0a 0a 09 09 09 09 72 65 74 75 72 6e 20  ..}......return 
39c0: 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65 76 61  [::appfs::db eva
39d0: 6c 20 7b 53 45 4c 45 43 54 20 44 49 53 54 49 4e  l {SELECT DISTIN
39e0: 43 54 20 70 61 63 6b 61 67 65 20 46 52 4f 4d 20  CT package FROM 
39f0: 70 61 63 6b 61 67 65 73 20 57 48 45 52 45 20 68  packages WHERE h
3a00: 6f 73 74 6e 61 6d 65 20 3d 20 24 70 61 74 68 69  ostname = $pathi
3a10: 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 3b 7d 5d  nfo(hostname);}]
3a20: 0a 09 09 09 7d 0a 09 09 09 22 6f 73 2d 63 70 75  ....}...."os-cpu
3a30: 22 20 7b 0a 09 09 09 09 73 65 74 20 72 65 74 76  " {.....set retv
3a40: 61 6c 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20  al [::appfs::db 
3a50: 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 44 49 53  eval {SELECT DIS
3a60: 54 49 4e 43 54 20 6f 73 20 7c 7c 20 22 2d 22 20  TINCT os || "-" 
3a70: 7c 7c 20 63 70 75 41 72 63 68 20 46 52 4f 4d 20  || cpuArch FROM 
3a80: 70 61 63 6b 61 67 65 73 20 57 48 45 52 45 20 68  packages WHERE h
3a90: 6f 73 74 6e 61 6d 65 20 3d 20 24 70 61 74 68 69  ostname = $pathi
3aa0: 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 41 4e  nfo(hostname) AN
3ab0: 44 20 70 61 63 6b 61 67 65 20 3d 20 24 70 61 74  D package = $pat
3ac0: 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 3b 7d  hinfo(package);}
3ad0: 5d 0a 0a 09 09 09 09 6c 61 70 70 65 6e 64 20 72  ]......lappend r
3ae0: 65 74 76 61 6c 20 22 70 6c 61 74 66 6f 72 6d 22  etval "platform"
3af0: 0a 0a 09 09 09 09 72 65 74 75 72 6e 20 24 72 65  ......return $re
3b00: 74 76 61 6c 0a 09 09 09 7d 0a 09 09 09 22 76 65  tval....}...."ve
3b10: 72 73 69 6f 6e 73 22 20 7b 0a 09 09 09 09 73 65  rsions" {.....se
3b20: 74 20 72 65 74 76 61 6c 20 5b 3a 3a 61 70 70 66  t retval [::appf
3b30: 73 3a 3a 64 62 20 65 76 61 6c 20 7b 0a 09 09 09  s::db eval {....
3b40: 09 09 53 45 4c 45 43 54 20 44 49 53 54 49 4e 43  ..SELECT DISTINC
3b50: 54 20 76 65 72 73 69 6f 6e 20 46 52 4f 4d 20 70  T version FROM p
3b60: 61 63 6b 61 67 65 73 20 57 48 45 52 45 20 68 6f  ackages WHERE ho
3b70: 73 74 6e 61 6d 65 20 3d 20 24 70 61 74 68 69 6e  stname = $pathin
3b80: 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 41 4e 44  fo(hostname) AND
3b90: 20 70 61 63 6b 61 67 65 20 3d 20 24 70 61 74 68   package = $path
3ba0: 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 41 4e  info(package) AN
3bb0: 44 20 6f 73 20 3d 20 24 70 61 74 68 69 6e 66 6f  D os = $pathinfo
3bc0: 28 6f 73 29 20 41 4e 44 20 63 70 75 41 72 63 68  (os) AND cpuArch
3bd0: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 63 70 75   = $pathinfo(cpu
3be0: 29 3b 0a 09 09 09 09 7d 5d 0a 0a 09 09 09 09 3a  );.....}]......:
3bf0: 3a 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c 20  :appfs::db eval 
3c00: 7b 53 45 4c 45 43 54 20 76 65 72 73 69 6f 6e 20  {SELECT version 
3c10: 46 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48  FROM packages WH
3c20: 45 52 45 20 69 73 4c 61 74 65 73 74 20 3d 20 31  ERE isLatest = 1
3c30: 20 41 4e 44 20 68 6f 73 74 6e 61 6d 65 20 3d 20   AND hostname = 
3c40: 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61  $pathinfo(hostna
3c50: 6d 65 29 20 41 4e 44 20 70 61 63 6b 61 67 65 20  me) AND package 
3c60: 3d 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b  = $pathinfo(pack
3c70: 61 67 65 29 20 41 4e 44 20 6f 73 20 3d 20 24 70  age) AND os = $p
3c80: 61 74 68 69 6e 66 6f 28 6f 73 29 20 41 4e 44 20  athinfo(os) AND 
3c90: 63 70 75 41 72 63 68 20 3d 20 24 70 61 74 68 69  cpuArch = $pathi
3ca0: 6e 66 6f 28 63 70 75 29 20 4c 49 4d 49 54 20 31  nfo(cpu) LIMIT 1
3cb0: 3b 7d 20 6c 61 74 65 73 74 5f 69 6e 66 6f 20 7b  ;} latest_info {
3cc0: 7d 0a 0a 09 09 09 09 69 66 20 7b 5b 69 6e 66 6f  }......if {[info
3cd0: 20 65 78 69 73 74 73 20 6c 61 74 65 73 74 5f 69   exists latest_i
3ce0: 6e 66 6f 28 76 65 72 73 69 6f 6e 29 5d 7d 20 7b  nfo(version)]} {
3cf0: 0a 09 09 09 09 09 6c 61 70 70 65 6e 64 20 72 65  ......lappend re
3d00: 74 76 61 6c 20 22 6c 61 74 65 73 74 22 0a 09 09  tval "latest"...
3d10: 09 09 7d 0a 0a 09 09 09 09 72 65 74 75 72 6e 20  ..}......return 
3d20: 24 72 65 74 76 61 6c 0a 09 09 09 7d 0a 09 09 09  $retval....}....
3d30: 22 66 69 6c 65 73 22 20 7b 0a 09 09 09 09 63 61  "files" {.....ca
3d40: 74 63 68 20 7b 0a 09 09 09 09 09 3a 3a 61 70 70  tch {......::app
3d50: 66 73 3a 3a 67 65 74 69 6e 64 65 78 20 24 70 61  fs::getindex $pa
3d60: 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29  thinfo(hostname)
3d70: 0a 09 09 09 09 09 3a 3a 61 70 70 66 73 3a 3a 67  ......::appfs::g
3d80: 65 74 70 6b 67 6d 61 6e 69 66 65 73 74 20 24 70  etpkgmanifest $p
3d90: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
3da0: 29 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b  ) $pathinfo(pack
3db0: 61 67 65 5f 73 68 61 31 29 0a 09 09 09 09 7d 0a  age_sha1).....}.
3dc0: 0a 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 20  .....set retval 
3dd0: 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65 76 61  [::appfs::db eva
3de0: 6c 20 7b 53 45 4c 45 43 54 20 44 49 53 54 49 4e  l {SELECT DISTIN
3df0: 43 54 20 66 69 6c 65 5f 6e 61 6d 65 20 46 52 4f  CT file_name FRO
3e00: 4d 20 66 69 6c 65 73 20 57 48 45 52 45 20 70 61  M files WHERE pa
3e10: 63 6b 61 67 65 5f 73 68 61 31 20 3d 20 24 70 61  ckage_sha1 = $pa
3e20: 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73  thinfo(package_s
3e30: 68 61 31 29 20 41 4e 44 20 66 69 6c 65 5f 64 69  ha1) AND file_di
3e40: 72 65 63 74 6f 72 79 20 3d 20 24 70 61 74 68 69  rectory = $pathi
3e50: 6e 66 6f 28 66 69 6c 65 29 3b 7d 5d 0a 0a 09 09  nfo(file);}]....
3e60: 09 09 69 66 20 7b 5b 69 6e 66 6f 20 65 78 69 73  ..if {[info exis
3e70: 74 73 20 70 61 74 68 69 6e 66 6f 28 70 61 63 6b  ts pathinfo(pack
3e80: 61 67 65 29 5d 20 26 26 20 5b 69 6e 66 6f 20 65  age)] && [info e
3e90: 78 69 73 74 73 20 70 61 74 68 69 6e 66 6f 28 68  xists pathinfo(h
3ea0: 6f 73 74 6e 61 6d 65 29 5d 20 26 26 20 5b 69 6e  ostname)] && [in
3eb0: 66 6f 20 65 78 69 73 74 73 20 70 61 74 68 69 6e  fo exists pathin
3ec0: 66 6f 28 66 69 6c 65 29 5d 7d 20 7b 0a 09 09 09  fo(file)]} {....
3ed0: 09 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09 09 09  .._as_user {....
3ee0: 09 09 09 73 65 74 20 64 69 72 20 5b 5f 6c 6f 63  ...set dir [_loc
3ef0: 61 6c 70 61 74 68 20 24 70 61 74 68 69 6e 66 6f  alpath $pathinfo
3f00: 28 70 61 63 6b 61 67 65 29 20 24 70 61 74 68 69  (package) $pathi
3f10: 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24 70  nfo(hostname) $p
3f20: 61 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d 0a 09  athinfo(file)]..
3f30: 09 09 09 09 09 73 65 74 20 77 68 69 74 65 6f 75  .....set whiteou
3f40: 74 64 69 72 20 5b 73 74 72 69 6e 67 20 72 61 6e  tdir [string ran
3f50: 67 65 20 5b 5f 77 68 69 74 65 6f 75 74 70 61 74  ge [_whiteoutpat
3f60: 68 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b  h $pathinfo(pack
3f70: 61 67 65 29 20 24 70 61 74 68 69 6e 66 6f 28 68  age) $pathinfo(h
3f80: 6f 73 74 6e 61 6d 65 29 20 24 70 61 74 68 69 6e  ostname) $pathin
3f90: 66 6f 28 66 69 6c 65 29 5d 20 30 20 65 6e 64 2d  fo(file)] 0 end-
3fa0: 31 35 5d 0a 0a 09 09 09 09 09 09 69 66 20 7b 24  15]........if {$
3fb0: 77 68 69 74 65 6f 75 74 64 69 72 20 21 3d 20 22  whiteoutdir != "
3fc0: 22 7d 20 7b 0a 09 09 09 09 09 09 09 66 6f 72 65  "} {........fore
3fd0: 61 63 68 20 66 69 6c 65 20 5b 67 6c 6f 62 20 2d  ach file [glob -
3fe0: 6e 6f 63 6f 6d 70 6c 61 69 6e 20 2d 74 61 69 6c  nocomplain -tail
3ff0: 73 20 2d 64 69 72 65 63 74 6f 72 79 20 24 77 68  s -directory $wh
4000: 69 74 65 6f 75 74 64 69 72 20 7b 7b 2e 2c 7d 2a  iteoutdir {{.,}*
4010: 2e 41 50 50 46 53 2e 57 48 49 54 45 4f 55 54 7d  .APPFS.WHITEOUT}
4020: 5d 20 7b 0a 09 09 09 09 09 09 09 09 73 65 74 20  ] {.........set 
4030: 72 65 6d 6f 76 65 20 5b 73 74 72 69 6e 67 20 72  remove [string r
4040: 61 6e 67 65 20 24 66 69 6c 65 20 30 20 65 6e 64  ange $file 0 end
4050: 2d 31 35 5d 0a 09 09 09 09 09 09 09 09 73 65 74  -15].........set
4060: 20 69 64 78 20 5b 6c 73 65 61 72 63 68 20 2d 65   idx [lsearch -e
4070: 78 61 63 74 20 24 72 65 74 76 61 6c 20 24 72 65  xact $retval $re
4080: 6d 6f 76 65 5d 0a 09 09 09 09 09 09 09 09 69 66  move].........if
4090: 20 7b 24 69 64 78 20 21 3d 20 2d 31 7d 20 7b 0a   {$idx != -1} {.
40a0: 09 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74  .........set ret
40b0: 76 61 6c 20 5b 6c 72 65 70 6c 61 63 65 20 24 72  val [lreplace $r
40c0: 65 74 76 61 6c 20 24 69 64 78 20 24 69 64 78 5d  etval $idx $idx]
40d0: 0a 09 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09  .........}......
40e0: 09 09 7d 0a 09 09 09 09 09 09 7d 0a 0a 09 09 09  ..}.......}.....
40f0: 09 09 09 69 66 20 7b 24 64 69 72 20 21 3d 20 22  ...if {$dir != "
4100: 22 7d 20 7b 0a 09 09 09 09 09 09 09 66 6f 72 65  "} {........fore
4110: 61 63 68 20 66 69 6c 65 20 5b 67 6c 6f 62 20 2d  ach file [glob -
4120: 6e 6f 63 6f 6d 70 6c 61 69 6e 20 2d 74 61 69 6c  nocomplain -tail
4130: 73 20 2d 64 69 72 65 63 74 6f 72 79 20 24 64 69  s -directory $di
4140: 72 20 7b 7b 2e 2c 7d 2a 7d 5d 20 7b 0a 09 09 09  r {{.,}*}] {....
4150: 09 09 09 09 09 69 66 20 7b 24 66 69 6c 65 20 3d  .....if {$file =
4160: 3d 20 22 2e 22 20 7c 7c 20 24 66 69 6c 65 20 3d  = "." || $file =
4170: 3d 20 22 2e 2e 22 7d 20 7b 0a 09 09 09 09 09 09  = ".."} {.......
4180: 09 09 09 63 6f 6e 74 69 6e 75 65 0a 09 09 09 09  ...continue.....
4190: 09 09 09 09 7d 0a 0a 09 09 09 09 09 09 09 09 69  ....}..........i
41a0: 66 20 7b 24 66 69 6c 65 20 3d 3d 20 22 2e 41 50  f {$file == ".AP
41b0: 50 46 53 2e 57 48 49 54 45 4f 55 54 22 7d 20 7b  PFS.WHITEOUT"} {
41c0: 0a 09 09 09 09 09 09 09 09 09 63 6f 6e 74 69 6e  ..........contin
41d0: 75 65 0a 09 09 09 09 09 09 09 09 7d 0a 0a 09 09  ue.........}....
41e0: 09 09 09 09 09 09 69 66 20 7b 5b 6c 73 65 61 72  ......if {[lsear
41f0: 63 68 20 2d 65 78 61 63 74 20 24 72 65 74 76 61  ch -exact $retva
4200: 6c 20 24 66 69 6c 65 5d 20 21 3d 20 2d 31 7d 20  l $file] != -1} 
4210: 7b 0a 09 09 09 09 09 09 09 09 09 63 6f 6e 74 69  {..........conti
4220: 6e 75 65 0a 09 09 09 09 09 09 09 09 7d 0a 0a 09  nue.........}...
4230: 09 09 09 09 09 09 09 6c 61 70 70 65 6e 64 20 72  .......lappend r
4240: 65 74 76 61 6c 20 24 66 69 6c 65 0a 09 09 09 09  etval $file.....
4250: 09 09 09 7d 0a 09 09 09 09 09 09 7d 0a 09 09 09  ...}.......}....
4260: 09 09 7d 0a 09 09 09 09 7d 0a 0a 09 09 09 09 72  ..}.....}......r
4270: 65 74 75 72 6e 20 24 72 65 74 76 61 6c 0a 09 09  eturn $retval...
4280: 09 7d 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e  .}...}....return
4290: 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 49 6e   -code error "In
42a0: 76 61 6c 69 64 20 6f 72 20 75 6e 61 63 63 65 70  valid or unaccep
42b0: 74 61 62 6c 65 20 70 61 74 68 3a 20 24 64 69 72  table path: $dir
42c0: 22 0a 09 7d 0a 0a 09 70 72 6f 63 20 67 65 74 61  "..}...proc geta
42d0: 74 74 72 20 7b 70 61 74 68 7d 20 7b 0a 09 09 61  ttr {path} {...a
42e0: 72 72 61 79 20 73 65 74 20 70 61 74 68 69 6e 66  rray set pathinf
42f0: 6f 20 5b 5f 70 61 72 73 65 70 61 74 68 20 24 70  o [_parsepath $p
4300: 61 74 68 5d 0a 09 09 61 72 72 61 79 20 73 65 74  ath]...array set
4310: 20 72 65 74 76 61 6c 20 5b 6c 69 73 74 5d 0a 0a   retval [list]..
4320: 09 09 63 61 74 63 68 20 7b 0a 09 09 09 3a 3a 61  ..catch {....::a
4330: 70 70 66 73 3a 3a 67 65 74 69 6e 64 65 78 20 24  ppfs::getindex $
4340: 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d  pathinfo(hostnam
4350: 65 29 0a 09 09 09 3a 3a 61 70 70 66 73 3a 3a 67  e)....::appfs::g
4360: 65 74 70 6b 67 6d 61 6e 69 66 65 73 74 20 24 70  etpkgmanifest $p
4370: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
4380: 29 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b  ) $pathinfo(pack
4390: 61 67 65 5f 73 68 61 31 29 0a 09 09 7d 0a 0a 09  age_sha1)...}...
43a0: 09 73 77 69 74 63 68 20 2d 2d 20 24 70 61 74 68  .switch -- $path
43b0: 69 6e 66 6f 28 5f 74 79 70 65 29 20 7b 0a 09 09  info(_type) {...
43c0: 09 22 74 6f 70 6c 65 76 65 6c 22 20 7b 0a 09 09  ."toplevel" {...
43d0: 09 09 73 65 74 20 72 65 74 76 61 6c 28 74 79 70  ..set retval(typ
43e0: 65 29 20 64 69 72 65 63 74 6f 72 79 0a 09 09 09  e) directory....
43f0: 09 73 65 74 20 72 65 74 76 61 6c 28 63 68 69 6c  .set retval(chil
4400: 64 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74 68  dcount) [llength
4410: 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70   [getchildren $p
4420: 61 74 68 5d 5d 0a 09 09 09 7d 0a 09 09 09 22 73  ath]]....}...."s
4430: 69 74 65 73 22 20 7b 0a 09 09 09 09 73 65 74 20  ites" {.....set 
4440: 63 68 65 63 6b 20 5b 3a 3a 61 70 70 66 73 3a 3a  check [::appfs::
4450: 64 62 20 6f 6e 65 63 6f 6c 75 6d 6e 20 7b 53 45  db onecolumn {SE
4460: 4c 45 43 54 20 31 20 46 52 4f 4d 20 70 61 63 6b  LECT 1 FROM pack
4470: 61 67 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e  ages WHERE hostn
4480: 61 6d 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28  ame = $pathinfo(
4490: 68 6f 73 74 6e 61 6d 65 29 3b 7d 5d 0a 09 09 09  hostname);}]....
44a0: 09 69 66 20 7b 24 63 68 65 63 6b 20 3d 3d 20 22  .if {$check == "
44b0: 31 22 7d 20 7b 0a 09 09 09 09 09 73 65 74 20 72  1"} {......set r
44c0: 65 74 76 61 6c 28 74 79 70 65 29 20 64 69 72 65  etval(type) dire
44d0: 63 74 6f 72 79 0a 09 09 09 09 09 73 65 74 20 72  ctory......set r
44e0: 65 74 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74  etval(childcount
44f0: 29 20 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74 63  ) [llength [getc
4500: 68 69 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d 0a  hildren $path]].
4510: 09 09 09 09 7d 0a 09 09 09 7d 0a 09 09 09 22 70  ....}....}...."p
4520: 61 63 6b 61 67 65 73 22 20 7b 0a 09 09 09 09 73  ackages" {.....s
4530: 65 74 20 63 68 65 63 6b 20 5b 3a 3a 61 70 70 66  et check [::appf
4540: 73 3a 3a 64 62 20 6f 6e 65 63 6f 6c 75 6d 6e 20  s::db onecolumn 
4550: 7b 53 45 4c 45 43 54 20 31 20 46 52 4f 4d 20 70  {SELECT 1 FROM p
4560: 61 63 6b 61 67 65 73 20 57 48 45 52 45 20 68 6f  ackages WHERE ho
4570: 73 74 6e 61 6d 65 20 3d 20 24 70 61 74 68 69 6e  stname = $pathin
4580: 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 41 4e 44  fo(hostname) AND
4590: 20 70 61 63 6b 61 67 65 20 3d 20 24 70 61 74 68   package = $path
45a0: 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 3b 7d 5d  info(package);}]
45b0: 0a 09 09 09 09 69 66 20 7b 24 63 68 65 63 6b 20  .....if {$check 
45c0: 3d 3d 20 22 31 22 7d 20 7b 0a 09 09 09 09 09 73  == "1"} {......s
45d0: 65 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20  et retval(type) 
45e0: 64 69 72 65 63 74 6f 72 79 0a 09 09 09 09 09 73  directory......s
45f0: 65 74 20 72 65 74 76 61 6c 28 63 68 69 6c 64 63  et retval(childc
4600: 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74 68 20 5b  ount) [llength [
4610: 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74  getchildren $pat
4620: 68 5d 5d 0a 09 09 09 09 7d 0a 09 09 09 7d 0a 09  h]].....}....}..
4630: 09 09 22 6f 73 2d 63 70 75 22 20 7b 0a 09 09 09  .."os-cpu" {....
4640: 09 69 66 20 7b 24 70 61 74 68 69 6e 66 6f 28 6f  .if {$pathinfo(o
4650: 73 29 20 3d 3d 20 22 70 6c 61 74 66 6f 72 6d 22  s) == "platform"
4660: 20 26 26 20 24 70 61 74 68 69 6e 66 6f 28 63 70   && $pathinfo(cp
4670: 75 29 20 3d 3d 20 22 22 7d 20 7b 0a 09 09 09 09  u) == ""} {.....
4680: 09 73 65 74 20 72 65 74 76 61 6c 28 74 79 70 65  .set retval(type
4690: 29 20 73 79 6d 6c 69 6e 6b 0a 09 09 09 09 09 73  ) symlink......s
46a0: 65 74 20 72 65 74 76 61 6c 28 73 6f 75 72 63 65  et retval(source
46b0: 29 20 5b 70 6c 61 74 66 6f 72 6d 3a 3a 67 65 6e  ) [platform::gen
46c0: 65 72 69 63 5d 0a 09 09 09 09 7d 20 65 6c 73 65  eric].....} else
46d0: 20 7b 0a 09 09 09 09 09 73 65 74 20 63 68 65 63   {......set chec
46e0: 6b 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62 20 6f  k [::appfs::db o
46f0: 6e 65 63 6f 6c 75 6d 6e 20 7b 0a 09 09 09 09 09  necolumn {......
4700: 09 53 45 4c 45 43 54 20 31 20 46 52 4f 4d 20 70  .SELECT 1 FROM p
4710: 61 63 6b 61 67 65 73 20 57 48 45 52 45 20 68 6f  ackages WHERE ho
4720: 73 74 6e 61 6d 65 20 3d 20 24 70 61 74 68 69 6e  stname = $pathin
4730: 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 41 4e 44  fo(hostname) AND
4740: 20 70 61 63 6b 61 67 65 20 3d 20 24 70 61 74 68   package = $path
4750: 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 41 4e  info(package) AN
4760: 44 20 6f 73 20 3d 20 24 70 61 74 68 69 6e 66 6f  D os = $pathinfo
4770: 28 6f 73 29 20 41 4e 44 20 63 70 75 41 72 63 68  (os) AND cpuArch
4780: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 63 70 75   = $pathinfo(cpu
4790: 29 3b 0a 09 09 09 09 09 7d 5d 0a 09 09 09 09 09  );......}]......
47a0: 69 66 20 7b 24 63 68 65 63 6b 20 3d 3d 20 22 31  if {$check == "1
47b0: 22 7d 20 7b 0a 09 09 09 09 09 09 73 65 74 20 72  "} {.......set r
47c0: 65 74 76 61 6c 28 74 79 70 65 29 20 64 69 72 65  etval(type) dire
47d0: 63 74 6f 72 79 0a 09 09 09 09 09 09 73 65 74 20  ctory.......set 
47e0: 72 65 74 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e  retval(childcoun
47f0: 74 29 20 5b 6c 6c 65 6e 67 74 68 20 5b 67 65 74  t) [llength [get
4800: 63 68 69 6c 64 72 65 6e 20 24 70 61 74 68 5d 5d  children $path]]
4810: 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 0a 09 09  ......}.....}...
4820: 09 7d 0a 09 09 09 22 76 65 72 73 69 6f 6e 73 22  .}...."versions"
4830: 20 7b 0a 09 09 09 09 69 66 20 7b 24 70 61 74 68   {.....if {$path
4840: 69 6e 66 6f 28 76 65 72 73 69 6f 6e 29 20 3d 3d  info(version) ==
4850: 20 22 6c 61 74 65 73 74 22 7d 20 7b 0a 09 09 09   "latest"} {....
4860: 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65 76  ..::appfs::db ev
4870: 61 6c 20 7b 53 45 4c 45 43 54 20 76 65 72 73 69  al {SELECT versi
4880: 6f 6e 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73  on FROM packages
4890: 20 57 48 45 52 45 20 69 73 4c 61 74 65 73 74 20   WHERE isLatest 
48a0: 3d 20 31 20 41 4e 44 20 68 6f 73 74 6e 61 6d 65  = 1 AND hostname
48b0: 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73   = $pathinfo(hos
48c0: 74 6e 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61  tname) AND packa
48d0: 67 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70  ge = $pathinfo(p
48e0: 61 63 6b 61 67 65 29 20 41 4e 44 20 6f 73 20 3d  ackage) AND os =
48f0: 20 24 70 61 74 68 69 6e 66 6f 28 6f 73 29 20 41   $pathinfo(os) A
4900: 4e 44 20 63 70 75 41 72 63 68 20 3d 20 24 70 61  ND cpuArch = $pa
4910: 74 68 69 6e 66 6f 28 63 70 75 29 20 4c 49 4d 49  thinfo(cpu) LIMI
4920: 54 20 31 3b 7d 20 6c 61 74 65 73 74 5f 69 6e 66  T 1;} latest_inf
4930: 6f 20 7b 7d 0a 0a 09 09 09 09 09 69 66 20 7b 5b  o {}.......if {[
4940: 69 6e 66 6f 20 65 78 69 73 74 73 20 6c 61 74 65  info exists late
4950: 73 74 5f 69 6e 66 6f 28 76 65 72 73 69 6f 6e 29  st_info(version)
4960: 5d 7d 20 7b 0a 09 09 09 09 09 09 73 65 74 20 72  ]} {.......set r
4970: 65 74 76 61 6c 28 74 79 70 65 29 20 73 79 6d 6c  etval(type) syml
4980: 69 6e 6b 0a 09 09 09 09 09 09 73 65 74 20 72 65  ink.......set re
4990: 74 76 61 6c 28 73 6f 75 72 63 65 29 20 24 6c 61  tval(source) $la
49a0: 74 65 73 74 5f 69 6e 66 6f 28 76 65 72 73 69 6f  test_info(versio
49b0: 6e 29 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 20  n)......}.....} 
49c0: 65 6c 73 65 20 7b 0a 09 09 09 09 09 69 66 20 7b  else {......if {
49d0: 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 70 61 74  [info exists pat
49e0: 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73 68  hinfo(package_sh
49f0: 61 31 29 5d 20 26 26 20 24 70 61 74 68 69 6e 66  a1)] && $pathinf
4a00: 6f 28 70 61 63 6b 61 67 65 5f 73 68 61 31 29 20  o(package_sha1) 
4a10: 21 3d 20 22 22 7d 20 7b 0a 09 09 09 09 09 09 73  != ""} {.......s
4a20: 65 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20  et retval(type) 
4a30: 64 69 72 65 63 74 6f 72 79 0a 09 09 09 09 09 09  directory.......
4a40: 73 65 74 20 72 65 74 76 61 6c 28 63 68 69 6c 64  set retval(child
4a50: 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74 68 20  count) [llength 
4a60: 5b 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61  [getchildren $pa
4a70: 74 68 5d 5d 0a 09 09 09 09 09 7d 0a 09 09 09 09  th]]......}.....
4a80: 7d 0a 09 09 09 7d 0a 09 09 09 22 66 69 6c 65 73  }....}...."files
4a90: 22 20 7b 0a 09 09 09 09 73 65 74 20 72 65 74 76  " {.....set retv
4aa0: 61 6c 28 70 61 63 6b 61 67 65 64 29 20 31 0a 0a  al(packaged) 1..
4ab0: 09 09 09 09 73 65 74 20 6c 6f 63 61 6c 70 61 74  ....set localpat
4ac0: 68 20 5b 5f 6c 6f 63 61 6c 70 61 74 68 20 24 70  h [_localpath $p
4ad0: 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29  athinfo(package)
4ae0: 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e   $pathinfo(hostn
4af0: 61 6d 65 29 20 24 70 61 74 68 69 6e 66 6f 28 66  ame) $pathinfo(f
4b00: 69 6c 65 29 5d 0a 09 09 09 09 73 65 74 20 77 68  ile)].....set wh
4b10: 69 74 65 6f 75 74 70 61 74 68 20 20 5b 5f 77 68  iteoutpath  [_wh
4b20: 69 74 65 6f 75 74 70 61 74 68 20 24 70 61 74 68  iteoutpath $path
4b30: 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 24 70  info(package) $p
4b40: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
4b50: 29 20 24 70 61 74 68 69 6e 66 6f 28 66 69 6c 65  ) $pathinfo(file
4b60: 29 5d 0a 0a 09 09 09 09 73 65 74 20 72 65 74 76  )]......set retv
4b70: 61 6c 28 6c 6f 63 61 6c 70 61 74 68 29 20 24 6c  al(localpath) $l
4b80: 6f 63 61 6c 70 61 74 68 0a 09 09 09 09 73 65 74  ocalpath.....set
4b90: 20 72 65 74 76 61 6c 28 77 68 69 74 65 6f 75 74   retval(whiteout
4ba0: 70 61 74 68 29 20 24 77 68 69 74 65 6f 75 74 70  path) $whiteoutp
4bb0: 61 74 68 0a 0a 09 09 09 09 75 6e 73 65 74 20 2d  ath......unset -
4bc0: 6e 6f 63 6f 6d 70 6c 61 69 6e 20 6c 6f 63 61 6c  nocomplain local
4bd0: 70 61 74 68 69 6e 66 6f 0a 09 09 09 09 69 66 20  pathinfo.....if 
4be0: 7b 24 6c 6f 63 61 6c 70 61 74 68 20 21 3d 20 22  {$localpath != "
4bf0: 22 7d 20 7b 0a 09 09 09 09 09 5f 61 73 5f 75 73  "} {......_as_us
4c00: 65 72 20 7b 0a 09 09 09 09 09 09 63 61 74 63 68  er {.......catch
4c10: 20 7b 0a 09 09 09 09 09 09 09 66 69 6c 65 20 6c   {........file l
4c20: 73 74 61 74 20 24 6c 6f 63 61 6c 70 61 74 68 20  stat $localpath 
4c30: 6c 6f 63 61 6c 70 61 74 68 69 6e 66 6f 0a 09 09  localpathinfo...
4c40: 09 09 09 09 7d 0a 09 09 09 09 09 7d 0a 09 09 09  ....}......}....
4c50: 09 7d 0a 0a 09 09 09 09 69 66 20 7b 24 6c 6f 63  .}......if {$loc
4c60: 61 6c 70 61 74 68 20 21 3d 20 22 22 20 26 26 20  alpath != "" && 
4c70: 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 6c 6f 63  [info exists loc
4c80: 61 6c 70 61 74 68 69 6e 66 6f 5d 7d 20 7b 0a 09  alpathinfo]} {..
4c90: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 69  ....set retval(i
4ca0: 73 5f 6c 6f 63 61 6c 66 69 6c 65 29 20 31 0a 09  s_localfile) 1..
4cb0: 09 09 09 09 63 61 74 63 68 20 7b 0a 09 09 09 09  ....catch {.....
4cc0: 09 09 73 65 74 20 72 65 74 76 61 6c 28 74 69 6d  ..set retval(tim
4cd0: 65 29 20 24 6c 6f 63 61 6c 70 61 74 68 69 6e 66  e) $localpathinf
4ce0: 6f 28 6d 74 69 6d 65 29 0a 0a 09 09 09 09 09 09  o(mtime)........
4cf0: 73 77 69 74 63 68 20 2d 2d 20 24 6c 6f 63 61 6c  switch -- $local
4d00: 70 61 74 68 69 6e 66 6f 28 74 79 70 65 29 20 7b  pathinfo(type) {
4d10: 0a 09 09 09 09 09 09 09 22 64 69 72 65 63 74 6f  ........"directo
4d20: 72 79 22 20 7b 0a 09 09 09 09 09 09 09 09 73 65  ry" {.........se
4d30: 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 22  t retval(type) "
4d40: 64 69 72 65 63 74 6f 72 79 22 0a 09 09 09 09 09  directory"......
4d50: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 63 68  ...set retval(ch
4d60: 69 6c 64 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67  ildcount) [lleng
4d70: 74 68 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20  th [getchildren 
4d80: 24 70 61 74 68 5d 5d 0a 09 09 09 09 09 09 09 7d  $path]]........}
4d90: 0a 09 09 09 09 09 09 09 22 66 69 6c 65 22 20 7b  ........"file" {
4da0: 0a 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74  .........set ret
4db0: 76 61 6c 28 74 79 70 65 29 20 22 66 69 6c 65 22  val(type) "file"
4dc0: 0a 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74  .........set ret
4dd0: 76 61 6c 28 73 69 7a 65 29 20 24 6c 6f 63 61 6c  val(size) $local
4de0: 70 61 74 68 69 6e 66 6f 28 73 69 7a 65 29 0a 09  pathinfo(size)..
4df0: 09 09 09 09 09 09 09 5f 61 73 5f 75 73 65 72 20  ......._as_user 
4e00: 7b 0a 09 09 09 09 09 09 09 09 09 69 66 20 7b 5b  {..........if {[
4e10: 66 69 6c 65 20 65 78 65 63 75 74 61 62 6c 65 20  file executable 
4e20: 24 6c 6f 63 61 6c 70 61 74 68 5d 7d 20 7b 0a 09  $localpath]} {..
4e30: 09 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74  .........set ret
4e40: 76 61 6c 28 70 65 72 6d 73 29 20 22 78 22 0a 09  val(perms) "x"..
4e50: 09 09 09 09 09 09 09 09 7d 20 65 6c 73 65 20 7b  ........} else {
4e60: 0a 09 09 09 09 09 09 09 09 09 09 73 65 74 20 72  ...........set r
4e70: 65 74 76 61 6c 28 70 65 72 6d 73 29 20 22 22 0a  etval(perms) "".
4e80: 09 09 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09  .........}......
4e90: 09 09 09 7d 0a 09 09 09 09 09 09 09 7d 0a 09 09  ...}........}...
4ea0: 09 09 09 09 09 22 6c 69 6e 6b 22 20 7b 0a 09 09  ....."link" {...
4eb0: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
4ec0: 28 74 79 70 65 29 20 22 73 79 6d 6c 69 6e 6b 22  (type) "symlink"
4ed0: 0a 0a 09 09 09 09 09 09 09 09 5f 61 73 5f 75 73  .........._as_us
4ee0: 65 72 20 7b 0a 09 09 09 09 09 09 09 09 09 73 65  er {..........se
4ef0: 74 20 72 65 74 76 61 6c 28 73 6f 75 72 63 65 29  t retval(source)
4f00: 20 5b 66 69 6c 65 20 72 65 61 64 6c 69 6e 6b 20   [file readlink 
4f10: 24 6c 6f 63 61 6c 70 61 74 68 5d 0a 09 09 09 09  $localpath].....
4f20: 09 09 09 09 7d 0a 09 09 09 09 09 09 09 7d 0a 09  ....}........}..
4f30: 09 09 09 09 09 09 22 66 69 66 6f 22 20 7b 0a 09  ......"fifo" {..
4f40: 09 09 09 09 09 09 09 23 20 43 61 70 69 74 61 6c  .......# Capital
4f50: 69 7a 65 64 20 73 6f 20 74 68 61 74 20 74 68 65  ized so that the
4f60: 20 66 69 72 73 74 20 63 68 61 72 20 69 73 20 75   first char is u
4f70: 6e 69 71 75 65 0a 09 09 09 09 09 09 09 09 73 65  nique.........se
4f80: 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 22  t retval(type) "
4f90: 46 69 66 6f 22 0a 09 09 09 09 09 09 09 7d 0a 09  Fifo"........}..
4fa0: 09 09 09 09 09 09 22 73 6f 63 6b 65 74 22 20 7b  ......"socket" {
4fb0: 0a 09 09 09 09 09 09 09 09 23 20 43 61 70 69 74  .........# Capit
4fc0: 61 6c 69 7a 65 64 20 73 6f 20 74 68 61 74 20 74  alized so that t
4fd0: 68 65 20 66 69 72 73 74 20 63 68 61 72 20 69 73  he first char is
4fe0: 20 75 6e 69 71 75 65 0a 09 09 09 09 09 09 09 09   unique.........
4ff0: 73 65 74 20 72 65 74 76 61 6c 28 74 79 70 65 29  set retval(type)
5000: 20 22 53 6f 63 6b 65 74 22 0a 09 09 09 09 09 09   "Socket".......
5010: 09 7d 0a 09 09 09 09 09 09 7d 0a 09 09 09 09 09  .}.......}......
5020: 7d 20 65 72 72 0a 09 09 09 09 7d 20 65 6c 73 65  } err.....} else
5030: 20 7b 0a 09 09 09 09 09 69 66 20 7b 24 77 68 69   {......if {$whi
5040: 74 65 6f 75 74 70 61 74 68 20 3d 3d 20 22 22 20  teoutpath == "" 
5050: 7c 7c 20 21 5b 66 69 6c 65 20 65 78 69 73 74 73  || ![file exists
5060: 20 24 77 68 69 74 65 6f 75 74 70 61 74 68 5d 7d   $whiteoutpath]}
5070: 20 7b 0a 09 09 09 09 09 09 73 65 74 20 72 65 74   {.......set ret
5080: 76 61 6c 28 69 73 5f 72 65 6d 6f 74 65 66 69 6c  val(is_remotefil
5090: 65 29 20 31 0a 0a 09 09 09 09 09 09 73 65 74 20  e) 1........set 
50a0: 77 6f 72 6b 20 5b 73 70 6c 69 74 20 24 70 61 74  work [split $pat
50b0: 68 69 6e 66 6f 28 66 69 6c 65 29 20 22 2f 22 5d  hinfo(file) "/"]
50c0: 0a 09 09 09 09 09 09 73 65 74 20 64 69 72 65 63  .......set direc
50d0: 74 6f 72 79 20 5b 6a 6f 69 6e 20 5b 6c 72 61 6e  tory [join [lran
50e0: 67 65 20 24 77 6f 72 6b 20 30 20 65 6e 64 2d 31  ge $work 0 end-1
50f0: 5d 20 22 2f 22 5d 0a 09 09 09 09 09 09 73 65 74  ] "/"].......set
5100: 20 66 69 6c 65 20 5b 6c 69 6e 64 65 78 20 24 77   file [lindex $w
5110: 6f 72 6b 20 65 6e 64 5d 0a 0a 09 09 09 09 09 09  ork end]........
5120: 69 66 20 7b 24 64 69 72 65 63 74 6f 72 79 20 3d  if {$directory =
5130: 3d 20 22 22 20 26 26 20 24 66 69 6c 65 20 3d 3d  = "" && $file ==
5140: 20 22 22 7d 20 7b 0a 09 09 09 09 09 09 09 61 72   ""} {........ar
5150: 72 61 79 20 73 65 74 20 72 65 74 76 61 6c 20 5b  ray set retval [
5160: 6c 69 73 74 20 74 79 70 65 20 64 69 72 65 63 74  list type direct
5170: 6f 72 79 5d 0a 09 09 09 09 09 09 7d 0a 0a 09 09  ory].......}....
5180: 09 09 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20  ....::appfs::db 
5190: 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 74 79 70  eval {SELECT typ
51a0: 65 2c 20 74 69 6d 65 2c 20 73 6f 75 72 63 65 2c  e, time, source,
51b0: 20 73 69 7a 65 2c 20 70 65 72 6d 73 20 46 52 4f   size, perms FRO
51c0: 4d 20 66 69 6c 65 73 20 57 48 45 52 45 20 70 61  M files WHERE pa
51d0: 63 6b 61 67 65 5f 73 68 61 31 20 3d 20 24 70 61  ckage_sha1 = $pa
51e0: 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73  thinfo(package_s
51f0: 68 61 31 29 20 41 4e 44 20 66 69 6c 65 5f 64 69  ha1) AND file_di
5200: 72 65 63 74 6f 72 79 20 3d 20 24 64 69 72 65 63  rectory = $direc
5210: 74 6f 72 79 20 41 4e 44 20 66 69 6c 65 5f 6e 61  tory AND file_na
5220: 6d 65 20 3d 20 24 66 69 6c 65 3b 7d 20 72 65 74  me = $file;} ret
5230: 76 61 6c 20 7b 7d 0a 0a 09 09 09 09 09 09 69 66  val {}........if
5240: 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 72   {[info exists r
5250: 65 74 76 61 6c 28 74 79 70 65 29 5d 20 26 26 20  etval(type)] && 
5260: 24 72 65 74 76 61 6c 28 74 79 70 65 29 20 3d 3d  $retval(type) ==
5270: 20 22 64 69 72 65 63 74 6f 72 79 22 7d 20 7b 0a   "directory"} {.
5280: 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61  .......set retva
5290: 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20 5b 6c  l(childcount) [l
52a0: 6c 65 6e 67 74 68 20 5b 67 65 74 63 68 69 6c 64  length [getchild
52b0: 72 65 6e 20 24 70 61 74 68 5d 5d 0a 09 09 09 09  ren $path]].....
52c0: 09 09 7d 0a 0a 09 09 09 09 09 09 75 6e 73 65 74  ..}........unset
52d0: 20 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 72 65 74   -nocomplain ret
52e0: 76 61 6c 28 2a 29 0a 09 09 09 09 09 7d 0a 09 09  val(*)......}...
52f0: 09 09 7d 0a 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09  ..}.....}...}...
5300: 09 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69 73  .if {![info exis
5310: 74 73 20 72 65 74 76 61 6c 28 74 79 70 65 29 5d  ts retval(type)]
5320: 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d 63  } {....return -c
5330: 6f 64 65 20 65 72 72 6f 72 20 22 4e 6f 20 73 75  ode error "No su
5340: 63 68 20 66 69 6c 65 20 6f 72 20 64 69 72 65 63  ch file or direc
5350: 74 6f 72 79 22 0a 09 09 7d 0a 0a 09 09 72 65 74  tory"...}....ret
5360: 75 72 6e 20 5b 61 72 72 61 79 20 67 65 74 20 72  urn [array get r
5370: 65 74 76 61 6c 5d 0a 09 7d 0a 0a 09 70 72 6f 63  etval]..}...proc
5380: 20 6f 70 65 6e 70 61 74 68 20 7b 70 61 74 68 20   openpath {path 
5390: 6d 6f 64 65 7d 20 7b 0a 09 09 61 72 72 61 79 20  mode} {...array 
53a0: 73 65 74 20 70 61 74 68 69 6e 66 6f 20 5b 5f 70  set pathinfo [_p
53b0: 61 72 73 65 70 61 74 68 20 24 70 61 74 68 5d 0a  arsepath $path].
53c0: 0a 09 09 69 66 20 7b 24 70 61 74 68 69 6e 66 6f  ...if {$pathinfo
53d0: 28 5f 74 79 70 65 29 20 21 3d 20 22 66 69 6c 65  (_type) != "file
53e0: 73 22 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  s"} {....return 
53f0: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 69 6e 76  -code error "inv
5400: 61 6c 69 64 20 74 79 70 65 22 0a 09 09 7d 0a 0a  alid type"...}..
5410: 09 09 73 65 74 20 6c 6f 63 61 6c 70 61 74 68 20  ..set localpath 
5420: 5b 5f 6c 6f 63 61 6c 70 61 74 68 20 24 70 61 74  [_localpath $pat
5430: 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 24  hinfo(package) $
5440: 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d  pathinfo(hostnam
5450: 65 29 20 24 70 61 74 68 69 6e 66 6f 28 66 69 6c  e) $pathinfo(fil
5460: 65 29 5d 0a 0a 09 09 69 66 20 7b 24 6d 6f 64 65  e)]....if {$mode
5470: 20 3d 3d 20 22 63 72 65 61 74 65 22 7d 20 7b 0a   == "create"} {.
5480: 09 09 09 69 66 20 7b 24 6c 6f 63 61 6c 70 61 74  ...if {$localpat
5490: 68 20 3d 3d 20 22 22 7d 20 7b 0a 09 09 09 09 72  h == ""} {.....r
54a0: 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f  eturn -code erro
54b0: 72 20 22 41 73 6b 65 64 20 74 6f 20 63 72 65 61  r "Asked to crea
54c0: 74 65 2c 20 62 75 74 20 6e 6f 20 68 6f 6d 65 20  te, but no home 
54d0: 64 69 72 65 63 74 6f 72 79 2e 22 0a 09 09 09 7d  directory."....}
54e0: 0a 0a 09 09 09 72 65 74 75 72 6e 20 24 6c 6f 63  .....return $loc
54f0: 61 6c 70 61 74 68 0a 09 09 7d 0a 0a 09 09 69 66  alpath...}....if
5500: 20 7b 24 6c 6f 63 61 6c 70 61 74 68 20 21 3d 20   {$localpath != 
5510: 22 22 20 26 26 20 5b 66 69 6c 65 20 65 78 69 73  "" && [file exis
5520: 74 73 20 24 6c 6f 63 61 6c 70 61 74 68 5d 7d 20  ts $localpath]} 
5530: 7b 0a 09 09 09 72 65 74 75 72 6e 20 24 6c 6f 63  {....return $loc
5540: 61 6c 70 61 74 68 0a 09 09 7d 0a 0a 09 09 73 65  alpath...}....se
5550: 74 20 77 6f 72 6b 20 5b 73 70 6c 69 74 20 24 70  t work [split $p
5560: 61 74 68 69 6e 66 6f 28 66 69 6c 65 29 20 22 2f  athinfo(file) "/
5570: 22 5d 0a 09 09 73 65 74 20 64 69 72 65 63 74 6f  "]...set directo
5580: 72 79 20 5b 6a 6f 69 6e 20 5b 6c 72 61 6e 67 65  ry [join [lrange
5590: 20 24 77 6f 72 6b 20 30 20 65 6e 64 2d 31 5d 20   $work 0 end-1] 
55a0: 22 2f 22 5d 0a 09 09 73 65 74 20 66 69 6c 65 20  "/"]...set file 
55b0: 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 65 6e  [lindex $work en
55c0: 64 5d 0a 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62  d]...::appfs::db
55d0: 20 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 66 69   eval {SELECT fi
55e0: 6c 65 5f 73 68 61 31 2c 20 70 65 72 6d 73 20 46  le_sha1, perms F
55f0: 52 4f 4d 20 66 69 6c 65 73 20 57 48 45 52 45 20  ROM files WHERE 
5600: 70 61 63 6b 61 67 65 5f 73 68 61 31 20 3d 20 24  package_sha1 = $
5610: 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65  pathinfo(package
5620: 5f 73 68 61 31 29 20 41 4e 44 20 66 69 6c 65 5f  _sha1) AND file_
5630: 6e 61 6d 65 20 3d 20 24 66 69 6c 65 20 41 4e 44  name = $file AND
5640: 20 66 69 6c 65 5f 64 69 72 65 63 74 6f 72 79 20   file_directory 
5650: 3d 20 24 64 69 72 65 63 74 6f 72 79 3b 7d 20 70  = $directory;} p
5660: 6b 67 70 61 74 68 69 6e 66 6f 20 7b 7d 0a 0a 09  kgpathinfo {}...
5670: 09 69 66 20 7b 24 70 6b 67 70 61 74 68 69 6e 66  .if {$pkgpathinf
5680: 6f 28 66 69 6c 65 5f 73 68 61 31 29 20 3d 3d 20  o(file_sha1) == 
5690: 22 22 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  ""} {....return 
56a0: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 4e 6f 20  -code error "No 
56b0: 73 75 63 68 20 66 69 6c 65 20 6f 72 20 64 69 72  such file or dir
56c0: 65 63 74 6f 72 79 22 0a 09 09 7d 0a 0a 09 09 73  ectory"...}....s
56d0: 65 74 20 6c 6f 63 61 6c 63 61 63 68 65 66 69 6c  et localcachefil
56e0: 65 20 5b 64 6f 77 6e 6c 6f 61 64 20 24 70 61 74  e [download $pat
56f0: 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20  hinfo(hostname) 
5700: 24 70 6b 67 70 61 74 68 69 6e 66 6f 28 66 69 6c  $pkgpathinfo(fil
5710: 65 5f 73 68 61 31 29 5d 0a 0a 09 09 69 66 20 7b  e_sha1)]....if {
5720: 24 6d 6f 64 65 20 3d 3d 20 22 77 72 69 74 65 22  $mode == "write"
5730: 7d 20 7b 0a 09 09 09 5f 61 73 5f 75 73 65 72 20  } {...._as_user 
5740: 7b 0a 09 09 09 09 73 65 74 20 74 6d 70 6c 6f 63  {.....set tmploc
5750: 61 6c 70 61 74 68 20 22 24 7b 6c 6f 63 61 6c 70  alpath "${localp
5760: 61 74 68 7d 2e 5b 65 78 70 72 20 72 61 6e 64 28  ath}.[expr rand(
5770: 29 5d 5b 63 6c 6f 63 6b 20 63 6c 69 63 6b 73 5d  )][clock clicks]
5780: 22 0a 0a 09 09 09 09 73 65 74 20 66 61 69 6c 65  "......set faile
5790: 64 20 30 0a 09 09 09 09 69 66 20 7b 5b 63 61 74  d 0.....if {[cat
57a0: 63 68 20 7b 0a 09 09 09 09 09 66 69 6c 65 20 6d  ch {......file m
57b0: 6b 64 69 72 20 5b 66 69 6c 65 20 64 69 72 6e 61  kdir [file dirna
57c0: 6d 65 20 24 6c 6f 63 61 6c 70 61 74 68 5d 0a 09  me $localpath]..
57d0: 09 09 09 09 66 69 6c 65 20 63 6f 70 79 20 2d 66  ....file copy -f
57e0: 6f 72 63 65 20 2d 2d 20 24 6c 6f 63 61 6c 63 61  orce -- $localca
57f0: 63 68 65 66 69 6c 65 20 24 74 6d 70 6c 6f 63 61  chefile $tmploca
5800: 6c 70 61 74 68 0a 0a 09 09 09 09 09 69 66 20 7b  lpath.......if {
5810: 24 70 6b 67 70 61 74 68 69 6e 66 6f 28 70 65 72  $pkgpathinfo(per
5820: 6d 73 29 20 3d 3d 20 22 78 22 7d 20 7b 0a 09 09  ms) == "x"} {...
5830: 09 09 09 09 66 69 6c 65 20 61 74 74 72 69 62 75  ....file attribu
5840: 74 65 73 20 24 74 6d 70 6c 6f 63 61 6c 70 61 74  tes $tmplocalpat
5850: 68 20 2d 70 65 72 6d 69 73 73 69 6f 6e 73 20 2b  h -permissions +
5860: 78 0a 09 09 09 09 09 7d 0a 0a 09 09 09 09 09 66  x......}.......f
5870: 69 6c 65 20 72 65 6e 61 6d 65 20 2d 66 6f 72 63  ile rename -forc
5880: 65 20 2d 2d 20 24 74 6d 70 6c 6f 63 61 6c 70 61  e -- $tmplocalpa
5890: 74 68 20 24 6c 6f 63 61 6c 70 61 74 68 0a 09 09  th $localpath...
58a0: 09 09 7d 20 65 72 72 5d 7d 20 7b 0a 09 09 09 09  ..} err]} {.....
58b0: 09 73 65 74 20 66 61 69 6c 65 64 20 31 0a 09 09  .set failed 1...
58c0: 09 09 7d 0a 09 09 09 09 63 61 74 63 68 20 7b 0a  ..}.....catch {.
58d0: 09 09 09 09 09 66 69 6c 65 20 64 65 6c 65 74 65  .....file delete
58e0: 20 2d 66 6f 72 63 65 20 2d 2d 20 24 74 6d 70 6c   -force -- $tmpl
58f0: 6f 63 61 6c 70 61 74 68 0a 09 09 09 09 7d 0a 09  ocalpath.....}..
5900: 09 09 7d 0a 0a 09 09 09 69 66 20 7b 24 66 61 69  ..}.....if {$fai
5910: 6c 65 64 7d 20 7b 0a 09 09 09 09 72 65 74 75 72  led} {.....retur
5920: 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 24 65  n -code error $e
5930: 72 72 0a 09 09 09 7d 0a 0a 09 09 09 72 65 74 75  rr....}.....retu
5940: 72 6e 20 24 6c 6f 63 61 6c 70 61 74 68 0a 09 09  rn $localpath...
5950: 7d 0a 0a 09 09 72 65 74 75 72 6e 20 24 6c 6f 63  }....return $loc
5960: 61 6c 63 61 63 68 65 66 69 6c 65 0a 09 7d 0a 0a  alcachefile..}..
5970: 09 70 72 6f 63 20 6c 6f 63 61 6c 70 61 74 68 20  .proc localpath 
5980: 7b 70 61 74 68 7d 20 7b 0a 09 09 61 72 72 61 79  {path} {...array
5990: 20 73 65 74 20 70 61 74 68 69 6e 66 6f 20 5b 5f   set pathinfo [_
59a0: 70 61 72 73 65 70 61 74 68 20 24 70 61 74 68 5d  parsepath $path]
59b0: 0a 0a 09 09 69 66 20 7b 24 70 61 74 68 69 6e 66  ....if {$pathinf
59c0: 6f 28 5f 74 79 70 65 29 20 21 3d 20 22 66 69 6c  o(_type) != "fil
59d0: 65 73 22 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e  es"} {....return
59e0: 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 69 6e   -code error "in
59f0: 76 61 6c 69 64 20 74 79 70 65 22 0a 09 09 7d 0a  valid type"...}.
5a00: 0a 09 09 73 65 74 20 6c 6f 63 61 6c 70 61 74 68  ...set localpath
5a10: 20 5b 5f 6c 6f 63 61 6c 70 61 74 68 20 24 70 61   [_localpath $pa
5a20: 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20  thinfo(package) 
5a30: 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61  $pathinfo(hostna
5a40: 6d 65 29 20 24 70 61 74 68 69 6e 66 6f 28 66 69  me) $pathinfo(fi
5a50: 6c 65 29 5d 0a 0a 09 09 72 65 74 75 72 6e 20 24  le)]....return $
5a60: 6c 6f 63 61 6c 70 61 74 68 0a 09 7d 0a 0a 09 70  localpath..}...p
5a70: 72 6f 63 20 65 78 69 73 74 73 20 7b 70 61 74 68  roc exists {path
5a80: 7d 20 7b 0a 09 09 63 61 74 63 68 20 7b 0a 09 09  } {...catch {...
5a90: 09 73 65 74 20 69 6e 66 6f 20 5b 67 65 74 61 74  .set info [getat
5aa0: 74 72 20 24 70 61 74 68 5d 0a 09 09 7d 20 65 72  tr $path]...} er
5ab0: 72 0a 0a 09 09 69 66 20 7b 21 5b 69 6e 66 6f 20  r....if {![info 
5ac0: 65 78 69 73 74 73 20 69 6e 66 6f 5d 7d 20 7b 0a  exists info]} {.
5ad0: 09 09 09 69 66 20 7b 24 65 72 72 20 3d 3d 20 22  ...if {$err == "
5ae0: 4e 6f 20 73 75 63 68 20 66 69 6c 65 20 6f 72 20  No such file or 
5af0: 64 69 72 65 63 74 6f 72 79 22 7d 20 7b 0a 09 09  directory"} {...
5b00: 09 09 72 65 74 75 72 6e 20 5b 6c 69 73 74 5d 0a  ..return [list].
5b10: 09 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09  ...} else {.....
5b20: 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72  return -code err
5b30: 6f 72 20 24 65 72 72 0a 09 09 09 7d 0a 09 09 7d  or $err....}...}
5b40: 0a 0a 09 09 72 65 74 75 72 6e 20 24 69 6e 66 6f  ....return $info
5b50: 0a 09 7d 0a 0a 09 70 72 6f 63 20 70 72 65 70 61  ..}...proc prepa
5b60: 72 65 5f 74 6f 5f 63 72 65 61 74 65 20 7b 70 61  re_to_create {pa
5b70: 74 68 20 7b 6d 75 73 74 5f 6e 6f 74 5f 65 78 69  th {must_not_exi
5b80: 73 74 20 31 7d 7d 20 7b 0a 09 09 69 66 20 7b 24  st 1}} {...if {$
5b90: 6d 75 73 74 5f 6e 6f 74 5f 65 78 69 73 74 7d 20  must_not_exist} 
5ba0: 7b 0a 09 09 09 69 66 20 7b 5b 65 78 69 73 74 73  {....if {[exists
5bb0: 20 24 70 61 74 68 5d 20 21 3d 20 22 22 7d 20 7b   $path] != ""} {
5bc0: 0a 09 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64  .....return -cod
5bd0: 65 20 65 72 72 6f 72 20 22 46 69 6c 65 20 61 6c  e error "File al
5be0: 72 65 61 64 79 20 65 78 69 73 74 73 22 0a 09 09  ready exists"...
5bf0: 09 7d 0a 09 09 7d 0a 0a 09 09 73 65 74 20 66 69  .}...}....set fi
5c00: 6c 65 6e 61 6d 65 20 5b 6c 6f 63 61 6c 70 61 74  lename [localpat
5c10: 68 20 24 70 61 74 68 5d 0a 0a 09 09 69 66 20 7b  h $path]....if {
5c20: 24 66 69 6c 65 6e 61 6d 65 20 3d 3d 20 22 22 7d  $filename == ""}
5c30: 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 2d 63 6f   {....return -co
5c40: 64 65 20 65 72 72 6f 72 20 22 41 73 6b 65 64 20  de error "Asked 
5c50: 74 6f 20 63 72 65 61 74 65 2c 20 62 75 74 20 6e  to create, but n
5c60: 6f 20 68 6f 6d 65 20 64 69 72 65 63 74 6f 72 79  o home directory
5c70: 2e 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20 64 69  ."...}....set di
5c80: 72 6e 61 6d 65 20 5b 66 69 6c 65 20 64 69 72 6e  rname [file dirn
5c90: 61 6d 65 20 24 66 69 6c 65 6e 61 6d 65 5d 0a 0a  ame $filename]..
5ca0: 09 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09 09 09  .._as_user {....
5cb0: 66 69 6c 65 20 6d 6b 64 69 72 20 24 64 69 72 6e  file mkdir $dirn
5cc0: 61 6d 65 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72  ame...}....retur
5cd0: 6e 20 24 66 69 6c 65 6e 61 6d 65 0a 09 7d 0a 0a  n $filename..}..
5ce0: 09 70 72 6f 63 20 75 6e 6c 69 6e 6b 70 61 74 68  .proc unlinkpath
5cf0: 20 7b 70 61 74 68 7d 20 7b 0a 09 09 61 72 72 61   {path} {...arra
5d00: 79 20 73 65 74 20 70 61 74 68 61 74 74 72 73 20  y set pathattrs 
5d10: 5b 65 78 69 73 74 73 20 24 70 61 74 68 5d 0a 0a  [exists $path]..
5d20: 09 09 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69  ..if {![info exi
5d30: 73 74 73 20 70 61 74 68 61 74 74 72 73 28 70 61  sts pathattrs(pa
5d40: 63 6b 61 67 65 64 29 5d 7d 20 7b 0a 09 09 09 72  ckaged)]} {....r
5d50: 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f  eturn -code erro
5d60: 72 20 22 69 6e 76 61 6c 69 64 20 74 79 70 65 22  r "invalid type"
5d70: 0a 09 09 7d 0a 0a 09 09 73 65 74 20 6c 6f 63 61  ...}....set loca
5d80: 6c 70 61 74 68 20 24 70 61 74 68 61 74 74 72 73  lpath $pathattrs
5d90: 28 6c 6f 63 61 6c 70 61 74 68 29 0a 0a 09 09 69  (localpath)....i
5da0: 66 20 7b 24 6c 6f 63 61 6c 70 61 74 68 20 3d 3d  f {$localpath ==
5db0: 20 22 22 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e   ""} {....return
5dc0: 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 41 73   -code error "As
5dd0: 6b 65 64 20 74 6f 20 64 65 6c 65 74 65 2c 20 62  ked to delete, b
5de0: 75 74 20 6e 6f 20 68 6f 6d 65 20 64 69 72 65 63  ut no home direc
5df0: 74 6f 72 79 2e 22 0a 09 09 7d 0a 0a 09 09 69 66  tory."...}....if
5e00: 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 70   {[info exists p
5e10: 61 74 68 61 74 74 72 73 28 69 73 5f 6c 6f 63 61  athattrs(is_loca
5e20: 6c 66 69 6c 65 29 5d 7d 20 7b 0a 09 09 09 69 66  lfile)]} {....if
5e30: 20 7b 5b 66 69 6c 65 20 69 73 64 69 72 65 63 74   {[file isdirect
5e40: 6f 72 79 20 24 6c 6f 63 61 6c 70 61 74 68 5d 7d  ory $localpath]}
5e50: 20 7b 0a 09 09 09 09 73 65 74 20 63 68 69 6c 64   {.....set child
5e60: 72 65 6e 20 5b 67 65 74 63 68 69 6c 64 72 65 6e  ren [getchildren
5e70: 20 24 70 61 74 68 5d 0a 0a 09 09 09 09 69 66 20   $path]......if 
5e80: 7b 5b 6c 6c 65 6e 67 74 68 20 24 63 68 69 6c 64  {[llength $child
5e90: 72 65 6e 5d 20 21 3d 20 30 7d 20 7b 0a 09 09 09  ren] != 0} {....
5ea0: 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65  ..return -code e
5eb0: 72 72 6f 72 20 22 41 73 6b 65 64 20 74 6f 20 64  rror "Asked to d
5ec0: 65 6c 65 74 65 20 6e 6f 6e 2d 65 6d 70 74 79 20  elete non-empty 
5ed0: 64 69 72 65 63 74 6f 72 79 22 0a 09 09 09 09 7d  directory".....}
5ee0: 0a 09 09 09 7d 0a 0a 09 09 09 5f 61 73 5f 75 73  ....}....._as_us
5ef0: 65 72 20 7b 0a 09 09 09 09 66 69 6c 65 20 64 65  er {.....file de
5f00: 6c 65 74 65 20 2d 66 6f 72 63 65 20 2d 2d 20 24  lete -force -- $
5f10: 6c 6f 63 61 6c 70 61 74 68 0a 09 09 09 7d 0a 09  localpath....}..
5f20: 09 7d 20 65 6c 73 65 69 66 20 7b 5b 69 6e 66 6f  .} elseif {[info
5f30: 20 65 78 69 73 74 73 20 70 61 74 68 61 74 74 72   exists pathattr
5f40: 73 28 69 73 5f 72 65 6d 6f 74 65 66 69 6c 65 29  s(is_remotefile)
5f50: 5d 7d 20 7b 0a 09 09 09 69 66 20 7b 24 70 61 74  ]} {....if {$pat
5f60: 68 61 74 74 72 73 28 74 79 70 65 29 20 3d 3d 20  hattrs(type) == 
5f70: 22 64 69 72 65 63 74 6f 72 79 22 7d 20 7b 0a 09  "directory"} {..
5f80: 09 09 09 73 65 74 20 63 68 69 6c 64 72 65 6e 20  ...set children 
5f90: 5b 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61  [getchildren $pa
5fa0: 74 68 5d 0a 0a 09 09 09 09 69 66 20 7b 5b 6c 6c  th]......if {[ll
5fb0: 65 6e 67 74 68 20 24 63 68 69 6c 64 72 65 6e 5d  ength $children]
5fc0: 20 21 3d 20 30 7d 20 7b 0a 09 09 09 09 09 72 65   != 0} {......re
5fd0: 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72  turn -code error
5fe0: 20 22 41 73 6b 65 64 20 74 6f 20 64 65 6c 65 74   "Asked to delet
5ff0: 65 20 6e 6f 6e 2d 65 6d 70 74 79 20 64 69 72 65  e non-empty dire
6000: 63 74 6f 72 79 22 0a 09 09 09 09 7d 0a 09 09 09  ctory".....}....
6010: 7d 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09  }...} else {....
6020: 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72  return -code err
6030: 6f 72 20 22 55 6e 6b 6e 6f 77 6e 20 69 66 20 66  or "Unknown if f
6040: 69 6c 65 20 69 73 20 72 65 6d 6f 74 65 20 6f 72  ile is remote or
6050: 20 6c 6f 63 61 6c 20 21 3f 22 0a 09 09 7d 0a 0a   local !?"...}..
6060: 09 09 73 65 74 20 77 68 69 74 65 6f 75 74 66 69  ..set whiteoutfi
6070: 6c 65 20 24 70 61 74 68 61 74 74 72 73 28 77 68  le $pathattrs(wh
6080: 69 74 65 6f 75 74 70 61 74 68 29 0a 09 09 73 65  iteoutpath)...se
6090: 74 20 77 68 69 74 65 6f 75 74 64 69 72 20 5b 66  t whiteoutdir [f
60a0: 69 6c 65 20 64 69 72 6e 61 6d 65 20 24 77 68 69  ile dirname $whi
60b0: 74 65 6f 75 74 66 69 6c 65 5d 0a 0a 09 09 5f 61  teoutfile]...._a
60c0: 73 5f 75 73 65 72 20 7b 0a 09 09 09 66 69 6c 65  s_user {....file
60d0: 20 6d 6b 64 69 72 20 24 77 68 69 74 65 6f 75 74   mkdir $whiteout
60e0: 64 69 72 0a 09 09 09 63 6c 6f 73 65 20 5b 6f 70  dir....close [op
60f0: 65 6e 20 24 77 68 69 74 65 6f 75 74 66 69 6c 65  en $whiteoutfile
6100: 20 77 5d 0a 09 09 7d 0a 09 7d 0a 7d 0a            w]...}..}.}.