Hex Artifact Content

Artifact 985d2df621ad5de07cf129f00e1e2913217df049:


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 23 0a 23 20 43 6f 70 79 72  tclsh..#.# Copyr
0020: 69 67 68 74 20 28 63 29 20 32 30 31 34 20 20 52  ight (c) 2014  R
0030: 6f 79 20 4b 65 65 6e 65 0a 23 0a 23 20 50 65 72  oy Keene.#.# Per
0040: 6d 69 73 73 69 6f 6e 20 69 73 20 68 65 72 65 62  mission is hereb
0050: 79 20 67 72 61 6e 74 65 64 2c 20 66 72 65 65 20  y granted, free 
0060: 6f 66 20 63 68 61 72 67 65 2c 20 74 6f 20 61 6e  of charge, to an
0070: 79 20 70 65 72 73 6f 6e 20 6f 62 74 61 69 6e 69  y person obtaini
0080: 6e 67 20 61 20 63 6f 70 79 0a 23 20 6f 66 20 74  ng a copy.# of t
0090: 68 69 73 20 73 6f 66 74 77 61 72 65 20 61 6e 64  his software and
00a0: 20 61 73 73 6f 63 69 61 74 65 64 20 64 6f 63 75   associated docu
00b0: 6d 65 6e 74 61 74 69 6f 6e 20 66 69 6c 65 73 20  mentation files 
00c0: 28 74 68 65 20 22 53 6f 66 74 77 61 72 65 22 29  (the "Software")
00d0: 2c 20 74 6f 20 64 65 61 6c 0a 23 20 69 6e 20 74  , to deal.# in t
00e0: 68 65 20 53 6f 66 74 77 61 72 65 20 77 69 74 68  he Software with
00f0: 6f 75 74 20 72 65 73 74 72 69 63 74 69 6f 6e 2c  out restriction,
0100: 20 69 6e 63 6c 75 64 69 6e 67 20 77 69 74 68 6f   including witho
0110: 75 74 20 6c 69 6d 69 74 61 74 69 6f 6e 20 74 68  ut limitation th
0120: 65 20 72 69 67 68 74 73 0a 23 20 74 6f 20 75 73  e rights.# to us
0130: 65 2c 20 63 6f 70 79 2c 20 6d 6f 64 69 66 79 2c  e, copy, modify,
0140: 20 6d 65 72 67 65 2c 20 70 75 62 6c 69 73 68 2c   merge, publish,
0150: 20 64 69 73 74 72 69 62 75 74 65 2c 20 73 75 62   distribute, sub
0160: 6c 69 63 65 6e 73 65 2c 20 61 6e 64 2f 6f 72 20  license, and/or 
0170: 73 65 6c 6c 0a 23 20 63 6f 70 69 65 73 20 6f 66  sell.# copies of
0180: 20 74 68 65 20 53 6f 66 74 77 61 72 65 2c 20 61   the Software, a
0190: 6e 64 20 74 6f 20 70 65 72 6d 69 74 20 70 65 72  nd to permit per
01a0: 73 6f 6e 73 20 74 6f 20 77 68 6f 6d 20 74 68 65  sons to whom the
01b0: 20 53 6f 66 74 77 61 72 65 20 69 73 0a 23 20 66   Software is.# f
01c0: 75 72 6e 69 73 68 65 64 20 74 6f 20 64 6f 20 73  urnished to do s
01d0: 6f 2c 20 73 75 62 6a 65 63 74 20 74 6f 20 74 68  o, subject to th
01e0: 65 20 66 6f 6c 6c 6f 77 69 6e 67 20 63 6f 6e 64  e following cond
01f0: 69 74 69 6f 6e 73 3a 0a 23 0a 23 20 54 68 65 20  itions:.#.# The 
0200: 61 62 6f 76 65 20 63 6f 70 79 72 69 67 68 74 20  above copyright 
0210: 6e 6f 74 69 63 65 20 61 6e 64 20 74 68 69 73 20  notice and this 
0220: 70 65 72 6d 69 73 73 69 6f 6e 20 6e 6f 74 69 63  permission notic
0230: 65 20 73 68 61 6c 6c 20 62 65 20 69 6e 63 6c 75  e shall be inclu
0240: 64 65 64 20 69 6e 0a 23 20 61 6c 6c 20 63 6f 70  ded in.# all cop
0250: 69 65 73 20 6f 72 20 73 75 62 73 74 61 6e 74 69  ies or substanti
0260: 61 6c 20 70 6f 72 74 69 6f 6e 73 20 6f 66 20 74  al portions of t
0270: 68 65 20 53 6f 66 74 77 61 72 65 2e 0a 23 0a 23  he Software..#.#
0280: 20 54 48 45 20 53 4f 46 54 57 41 52 45 20 49 53   THE SOFTWARE IS
0290: 20 50 52 4f 56 49 44 45 44 20 22 41 53 20 49 53   PROVIDED "AS IS
02a0: 22 2c 20 57 49 54 48 4f 55 54 20 57 41 52 52 41  ", WITHOUT WARRA
02b0: 4e 54 59 20 4f 46 20 41 4e 59 20 4b 49 4e 44 2c  NTY OF ANY KIND,
02c0: 20 45 58 50 52 45 53 53 20 4f 52 0a 23 20 49 4d   EXPRESS OR.# IM
02d0: 50 4c 49 45 44 2c 20 49 4e 43 4c 55 44 49 4e 47  PLIED, INCLUDING
02e0: 20 42 55 54 20 4e 4f 54 20 4c 49 4d 49 54 45 44   BUT NOT LIMITED
02f0: 20 54 4f 20 54 48 45 20 57 41 52 52 41 4e 54 49   TO THE WARRANTI
0300: 45 53 20 4f 46 20 4d 45 52 43 48 41 4e 54 41 42  ES OF MERCHANTAB
0310: 49 4c 49 54 59 2c 0a 23 20 46 49 54 4e 45 53 53  ILITY,.# FITNESS
0320: 20 46 4f 52 20 41 20 50 41 52 54 49 43 55 4c 41   FOR A PARTICULA
0330: 52 20 50 55 52 50 4f 53 45 20 41 4e 44 20 4e 4f  R PURPOSE AND NO
0340: 4e 49 4e 46 52 49 4e 47 45 4d 45 4e 54 2e 20 49  NINFRINGEMENT. I
0350: 4e 20 4e 4f 20 45 56 45 4e 54 20 53 48 41 4c 4c  N NO EVENT SHALL
0360: 20 54 48 45 0a 23 20 41 55 54 48 4f 52 53 20 4f   THE.# AUTHORS O
0370: 52 20 43 4f 50 59 52 49 47 48 54 20 48 4f 4c 44  R COPYRIGHT HOLD
0380: 45 52 53 20 42 45 20 4c 49 41 42 4c 45 20 46 4f  ERS BE LIABLE FO
0390: 52 20 41 4e 59 20 43 4c 41 49 4d 2c 20 44 41 4d  R ANY CLAIM, DAM
03a0: 41 47 45 53 20 4f 52 20 4f 54 48 45 52 0a 23 20  AGES OR OTHER.# 
03b0: 4c 49 41 42 49 4c 49 54 59 2c 20 57 48 45 54 48  LIABILITY, WHETH
03c0: 45 52 20 49 4e 20 41 4e 20 41 43 54 49 4f 4e 20  ER IN AN ACTION 
03d0: 4f 46 20 43 4f 4e 54 52 41 43 54 2c 20 54 4f 52  OF CONTRACT, TOR
03e0: 54 20 4f 52 20 4f 54 48 45 52 57 49 53 45 2c 20  T OR OTHERWISE, 
03f0: 41 52 49 53 49 4e 47 20 46 52 4f 4d 2c 0a 23 20  ARISING FROM,.# 
0400: 4f 55 54 20 4f 46 20 4f 52 20 49 4e 20 43 4f 4e  OUT OF OR IN CON
0410: 4e 45 43 54 49 4f 4e 20 57 49 54 48 20 54 48 45  NECTION WITH THE
0420: 20 53 4f 46 54 57 41 52 45 20 4f 52 20 54 48 45   SOFTWARE OR THE
0430: 20 55 53 45 20 4f 52 20 4f 54 48 45 52 20 44 45   USE OR OTHER DE
0440: 41 4c 49 4e 47 53 20 49 4e 0a 23 20 54 48 45 20  ALINGS IN.# THE 
0450: 53 4f 46 54 57 41 52 45 2e 0a 23 0a 0a 70 61 63  SOFTWARE..#..pac
0460: 6b 61 67 65 20 72 65 71 75 69 72 65 20 68 74 74  kage require htt
0470: 70 20 32 2e 37 0a 70 61 63 6b 61 67 65 20 72 65  p 2.7.package re
0480: 71 75 69 72 65 20 73 71 6c 69 74 65 33 0a 70 61  quire sqlite3.pa
0490: 63 6b 61 67 65 20 72 65 71 75 69 72 65 20 73 68  ckage require sh
04a0: 61 31 0a 70 61 63 6b 61 67 65 20 72 65 71 75 69  a1.package requi
04b0: 72 65 20 61 70 70 66 73 64 0a 70 61 63 6b 61 67  re appfsd.packag
04c0: 65 20 72 65 71 75 69 72 65 20 70 6c 61 74 66 6f  e require platfo
04d0: 72 6d 0a 70 61 63 6b 61 67 65 20 72 65 71 75 69  rm.package requi
04e0: 72 65 20 70 6b 69 0a 0a 23 20 46 75 6e 63 74 69  re pki..# Functi
04f0: 6f 6e 73 20 73 70 65 63 69 66 69 63 61 6c 6c 79  ons specifically
0500: 20 6d 65 61 6e 74 20 66 6f 72 20 75 73 65 72 73   meant for users
0510: 20 74 6f 20 72 65 70 6c 61 63 65 20 61 73 20 61   to replace as a
0520: 20 70 61 72 74 20 6f 66 20 63 6f 6e 66 69 67 75   part of configu
0530: 72 61 74 69 6f 6e 0a 6e 61 6d 65 73 70 61 63 65  ration.namespace
0540: 20 65 76 61 6c 20 3a 3a 61 70 70 66 73 3a 3a 75   eval ::appfs::u
0550: 73 65 72 20 7b 0a 09 76 61 72 69 61 62 6c 65 20  ser {..variable 
0560: 64 6f 77 6e 6c 6f 61 64 5f 6d 65 74 68 6f 64 20  download_method 
0570: 22 74 63 6c 22 0a 0a 09 23 20 55 73 65 72 2d 72  "tcl"...# User-r
0580: 65 70 6c 61 63 61 62 6c 65 20 66 75 6e 63 74 69  eplacable functi
0590: 6f 6e 20 74 6f 20 63 6f 6e 76 65 72 74 20 61 20  on to convert a 
05a0: 68 6f 73 74 6e 61 6d 65 2f 68 61 73 68 2f 6d 65  hostname/hash/me
05b0: 74 68 6f 64 20 74 6f 20 61 6e 20 55 52 4c 0a 09  thod to an URL..
05c0: 70 72 6f 63 20 63 6f 6e 73 74 72 75 63 74 5f 75  proc construct_u
05d0: 72 6c 20 7b 68 6f 73 74 6e 61 6d 65 20 68 61 73  rl {hostname has
05e0: 68 20 6d 65 74 68 6f 64 7d 20 7b 0a 09 09 72 65  h method} {...re
05f0: 74 75 72 6e 20 22 68 74 74 70 3a 2f 2f 24 68 6f  turn "http://$ho
0600: 73 74 6e 61 6d 65 2f 61 70 70 66 73 2f 24 6d 65  stname/appfs/$me
0610: 74 68 6f 64 2f 24 68 61 73 68 22 0a 09 7d 0a 0a  thod/$hash"..}..
0620: 09 23 20 55 73 65 72 2d 72 65 70 6c 61 63 65 61  .# User-replacea
0630: 62 6c 65 20 66 75 6e 63 74 69 6f 6e 20 67 65 74  ble function get
0640: 20 74 68 65 20 68 6f 6d 65 20 64 69 72 65 63 74   the home direct
0650: 6f 72 79 20 6f 66 20 74 68 65 20 63 75 72 72 65  ory of the curre
0660: 6e 74 20 75 73 65 72 0a 09 70 72 6f 63 20 67 65  nt user..proc ge
0670: 74 5f 68 6f 6d 65 64 69 72 20 7b 7d 20 7b 0a 09  t_homedir {} {..
0680: 09 72 65 74 75 72 6e 20 5b 3a 3a 61 70 70 66 73  .return [::appfs
0690: 64 3a 3a 67 65 74 5f 68 6f 6d 65 64 69 72 5d 0a  d::get_homedir].
06a0: 09 7d 0a 0a 09 23 20 55 73 65 72 2d 72 65 70 6c  .}...# User-repl
06b0: 61 63 61 62 6c 65 20 66 75 6e 63 74 69 6f 6e 20  acable function 
06c0: 74 6f 20 75 70 64 61 74 65 20 70 65 72 6d 69 73  to update permis
06d0: 73 69 6f 6e 73 0a 09 70 72 6f 63 20 63 68 61 6e  sions..proc chan
06e0: 67 65 5f 70 65 72 6d 73 20 7b 66 69 6c 65 20 73  ge_perms {file s
06f0: 68 61 31 20 70 65 72 6d 73 7d 20 7b 0a 09 09 69  ha1 perms} {...i
0700: 66 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20  f {[info exists 
0710: 3a 3a 61 70 70 66 73 3a 3a 75 73 65 72 3a 3a 61  ::appfs::user::a
0720: 64 64 5f 70 65 72 6d 73 28 24 66 69 6c 65 29 5d  dd_perms($file)]
0730: 7d 20 7b 0a 09 09 09 61 70 70 65 6e 64 20 70 65  } {....append pe
0740: 72 6d 73 20 24 3a 3a 61 70 70 66 73 3a 3a 75 73  rms $::appfs::us
0750: 65 72 3a 3a 61 64 64 5f 70 65 72 6d 73 28 24 66  er::add_perms($f
0760: 69 6c 65 29 0a 09 09 7d 0a 0a 09 09 69 66 20 7b  ile)...}....if {
0770: 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 3a 3a 61  [info exists ::a
0780: 70 70 66 73 3a 3a 75 73 65 72 3a 3a 61 64 64 5f  ppfs::user::add_
0790: 70 65 72 6d 73 28 24 73 68 61 31 29 5d 7d 20 7b  perms($sha1)]} {
07a0: 0a 09 09 09 61 70 70 65 6e 64 20 70 65 72 6d 73  ....append perms
07b0: 20 24 3a 3a 61 70 70 66 73 3a 3a 75 73 65 72 3a   $::appfs::user:
07c0: 3a 61 64 64 5f 70 65 72 6d 73 28 24 73 68 61 31  :add_perms($sha1
07d0: 29 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e 20  )...}....return 
07e0: 24 70 65 72 6d 73 0a 09 7d 0a 0a 09 23 20 55 73  $perms..}...# Us
07f0: 65 72 2d 72 65 70 6c 61 63 61 62 6c 65 20 66 75  er-replacable fu
0800: 6e 63 74 69 6f 6e 20 74 6f 20 66 65 74 63 68 20  nction to fetch 
0810: 61 20 72 65 6d 6f 74 65 20 66 69 6c 65 0a 09 70  a remote file..p
0820: 72 6f 63 20 64 6f 77 6e 6c 6f 61 64 5f 66 69 6c  roc download_fil
0830: 65 20 7b 75 72 6c 20 7b 6f 75 74 70 75 74 43 68  e {url {outputCh
0840: 61 6e 6e 65 6c 20 22 22 7d 7d 20 7b 0a 09 09 73  annel ""}} {...s
0850: 77 69 74 63 68 20 2d 2d 20 24 3a 3a 61 70 70 66  witch -- $::appf
0860: 73 3a 3a 75 73 65 72 3a 3a 64 6f 77 6e 6c 6f 61  s::user::downloa
0870: 64 5f 6d 65 74 68 6f 64 20 7b 0a 09 09 09 22 63  d_method {...."c
0880: 75 72 6c 22 20 7b 0a 09 09 09 09 69 66 20 7b 24  url" {.....if {$
0890: 6f 75 74 70 75 74 43 68 61 6e 6e 65 6c 20 65 71  outputChannel eq
08a0: 20 22 22 7d 20 7b 0a 09 09 09 09 09 72 65 74 75   ""} {......retu
08b0: 72 6e 20 5b 65 78 65 63 20 63 75 72 6c 20 2d 73  rn [exec curl -s
08c0: 53 20 2d 4c 20 2d 2d 20 24 75 72 6c 5d 0a 09 09  S -L -- $url]...
08d0: 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 09  ..} else {......
08e0: 65 78 65 63 20 63 75 72 6c 20 2d 73 53 20 2d 4c  exec curl -sS -L
08f0: 20 2d 2d 20 24 75 72 6c 20 3e 40 20 24 6f 75 74   -- $url >@ $out
0900: 70 75 74 43 68 61 6e 6e 65 6c 0a 0a 09 09 09 09  putChannel......
0910: 09 72 65 74 75 72 6e 20 22 22 0a 09 09 09 09 7d  .return "".....}
0920: 0a 09 09 09 7d 0a 09 09 09 22 74 63 6c 22 20 7b  ....}...."tcl" {
0930: 0a 09 09 09 09 63 61 74 63 68 20 7b 0a 09 09 09  .....catch {....
0940: 09 09 69 66 20 7b 24 6f 75 74 70 75 74 43 68 61  ..if {$outputCha
0950: 6e 6e 65 6c 20 65 71 20 22 22 7d 20 7b 0a 09 09  nnel eq ""} {...
0960: 09 09 09 09 73 65 74 20 74 6f 6b 65 6e 20 5b 68  ....set token [h
0970: 74 74 70 3a 3a 67 65 74 75 72 6c 20 24 75 72 6c  ttp::geturl $url
0980: 5d 0a 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ].......set retv
0990: 61 6c 20 5b 68 74 74 70 3a 3a 64 61 74 61 20 24  al [http::data $
09a0: 74 6f 6b 65 6e 5d 0a 09 09 09 09 09 7d 20 65 6c  token]......} el
09b0: 73 65 20 7b 0a 09 09 09 09 09 09 73 65 74 20 74  se {.......set t
09c0: 6f 6b 65 6e 20 5b 68 74 74 70 3a 3a 67 65 74 75  oken [http::getu
09d0: 72 6c 20 24 75 72 6c 20 2d 62 69 6e 61 72 79 20  rl $url -binary 
09e0: 74 72 75 65 20 2d 63 68 61 6e 6e 65 6c 20 24 6f  true -channel $o
09f0: 75 74 70 75 74 43 68 61 6e 6e 65 6c 5d 0a 09 09  utputChannel]...
0a00: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 20 22  ....set retval "
0a10: 22 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 20 65  "......}.....} e
0a20: 72 72 0a 0a 09 09 09 09 69 66 20 7b 21 5b 69 6e  rr......if {![in
0a30: 66 6f 20 65 78 69 73 74 73 20 74 6f 6b 65 6e 5d  fo exists token]
0a40: 7d 20 7b 0a 09 09 09 09 09 72 65 74 75 72 6e 20  } {......return 
0a50: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55 6e 61  -code error "Una
0a60: 62 6c 65 20 74 6f 20 64 6f 77 6e 6c 6f 61 64 20  ble to download 
0a70: 5c 22 24 75 72 6c 5c 22 3a 20 24 65 72 72 22 0a  \"$url\": $err".
0a80: 09 09 09 09 7d 0a 0a 09 09 09 09 73 65 74 20 74  ....}......set t
0a90: 6f 6b 65 6e 43 6f 64 65 20 5b 68 74 74 70 3a 3a  okenCode [http::
0aa0: 6e 63 6f 64 65 20 24 74 6f 6b 65 6e 5d 0a 0a 09  ncode $token]...
0ab0: 09 09 09 68 74 74 70 3a 3a 63 6c 65 61 6e 75 70  ...http::cleanup
0ac0: 20 24 74 6f 6b 65 6e 0a 0a 09 09 09 09 69 66 20   $token......if 
0ad0: 7b 24 74 6f 6b 65 6e 43 6f 64 65 20 21 3d 20 22  {$tokenCode != "
0ae0: 32 30 30 22 7d 20 7b 0a 09 09 09 09 09 72 65 74  200"} {......ret
0af0: 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20  urn -code error 
0b00: 22 55 6e 61 62 6c 65 20 74 6f 20 64 6f 77 6e 6c  "Unable to downl
0b10: 6f 61 64 20 5c 22 24 75 72 6c 5c 22 3a 20 53 69  oad \"$url\": Si
0b20: 74 65 20 64 69 64 20 6e 6f 74 20 72 65 74 75 72  te did not retur
0b30: 6e 20 61 20 32 30 30 20 28 72 65 74 75 72 6e 65  n a 200 (returne
0b40: 64 20 24 74 6f 6b 65 6e 43 6f 64 65 29 22 0a 09  d $tokenCode)"..
0b50: 09 09 09 7d 0a 0a 09 09 09 09 69 66 20 7b 21 5b  ...}......if {![
0b60: 69 6e 66 6f 20 65 78 69 73 74 73 20 72 65 74 76  info exists retv
0b70: 61 6c 5d 7d 20 7b 0a 09 09 09 09 09 72 65 74 75  al]} {......retu
0b80: 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22  rn -code error "
0b90: 55 6e 61 62 6c 65 20 74 6f 20 64 6f 77 6e 6c 6f  Unable to downlo
0ba0: 61 64 20 5c 22 24 75 72 6c 5c 22 3a 20 53 69 74  ad \"$url\": Sit
0bb0: 65 20 64 69 64 20 6e 6f 74 20 72 65 74 75 72 6e  e did not return
0bc0: 20 70 72 6f 70 65 72 20 64 61 74 61 3a 20 24 65   proper data: $e
0bd0: 72 72 22 0a 09 09 09 09 7d 0a 0a 09 09 09 09 72  rr".....}......r
0be0: 65 74 75 72 6e 20 24 72 65 74 76 61 6c 0a 09 09  eturn $retval...
0bf0: 09 7d 0a 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72  .}....}....retur
0c00: 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55  n -code error "U
0c10: 6e 61 62 6c 65 20 74 6f 20 64 6f 77 6e 6c 6f 61  nable to downloa
0c20: 64 22 0a 09 7d 0a 7d 0a 0a 6e 61 6d 65 73 70 61  d"..}.}..namespa
0c30: 63 65 20 65 76 61 6c 20 3a 3a 61 70 70 66 73 20  ce eval ::appfs 
0c40: 7b 0a 09 76 61 72 69 61 62 6c 65 20 63 61 63 68  {..variable cach
0c50: 65 64 69 72 20 22 2f 74 6d 70 2f 61 70 70 66 73  edir "/tmp/appfs
0c60: 2d 63 61 63 68 65 22 0a 09 76 61 72 69 61 62 6c  -cache"..variabl
0c70: 65 20 74 74 6c 20 33 36 30 30 0a 09 76 61 72 69  e ttl 3600..vari
0c80: 61 62 6c 65 20 6e 74 74 6c 20 36 30 0a 09 76 61  able nttl 60..va
0c90: 72 69 61 62 6c 65 20 74 72 75 73 74 65 64 5f 63  riable trusted_c
0ca0: 61 73 20 5b 6c 69 73 74 5d 0a 09 76 61 72 69 61  as [list]..varia
0cb0: 62 6c 65 20 70 6c 61 74 66 6f 72 6d 20 5b 3a 3a  ble platform [::
0cc0: 70 6c 61 74 66 6f 72 6d 3a 3a 67 65 6e 65 72 69  platform::generi
0cd0: 63 5d 0a 0a 09 70 72 6f 63 20 5f 68 61 73 68 5f  c]...proc _hash_
0ce0: 73 65 70 20 7b 68 61 73 68 20 7b 73 65 70 73 20  sep {hash {seps 
0cf0: 34 7d 7d 20 7b 0a 09 09 66 6f 72 20 7b 73 65 74  4}} {...for {set
0d00: 20 69 64 78 20 30 7d 20 7b 24 69 64 78 20 3c 20   idx 0} {$idx < 
0d10: 24 73 65 70 73 7d 20 7b 69 6e 63 72 20 69 64 78  $seps} {incr idx
0d20: 7d 20 7b 0a 09 09 09 61 70 70 65 6e 64 20 72 65  } {....append re
0d30: 74 76 61 6c 20 22 5b 73 74 72 69 6e 67 20 72 61  tval "[string ra
0d40: 6e 67 65 20 24 68 61 73 68 20 5b 65 78 70 72 20  nge $hash [expr 
0d50: 7b 24 69 64 78 20 2a 20 32 7d 5d 20 5b 65 78 70  {$idx * 2}] [exp
0d60: 72 20 7b 28 24 69 64 78 20 2a 20 32 29 20 2b 20  r {($idx * 2) + 
0d70: 31 7d 5d 5d 2f 22 0a 09 09 7d 0a 09 09 61 70 70  1}]]/"...}...app
0d80: 65 6e 64 20 72 65 74 76 61 6c 20 22 5b 73 74 72  end retval "[str
0d90: 69 6e 67 20 72 61 6e 67 65 20 24 68 61 73 68 20  ing range $hash 
0da0: 5b 65 78 70 72 20 7b 24 69 64 78 20 2a 20 32 7d  [expr {$idx * 2}
0db0: 5d 20 65 6e 64 5d 22 0a 0a 09 09 72 65 74 75 72  ] end]"....retur
0dc0: 6e 20 24 72 65 74 76 61 6c 0a 09 7d 0a 0a 09 70  n $retval..}...p
0dd0: 72 6f 63 20 5f 63 61 63 68 65 66 69 6c 65 20 7b  roc _cachefile {
0de0: 75 72 6c 20 6b 65 79 20 6d 65 74 68 6f 64 20 7b  url key method {
0df0: 6b 65 79 49 73 48 61 73 68 20 31 7d 7d 20 7b 0a  keyIsHash 1}} {.
0e00: 09 09 69 66 20 7b 24 6b 65 79 49 73 48 61 73 68  ..if {$keyIsHash
0e10: 20 26 26 20 24 6d 65 74 68 6f 64 20 21 3d 20 22   && $method != "
0e20: 73 68 61 31 22 7d 20 7b 0a 09 09 09 72 65 74 75  sha1"} {....retu
0e30: 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22  rn -code error "
0e40: 4f 6e 6c 79 20 53 48 41 31 20 68 61 73 68 69 6e  Only SHA1 hashin
0e50: 67 20 6d 65 74 68 6f 64 20 69 73 20 73 75 70 70  g method is supp
0e60: 6f 72 74 65 64 22 0a 09 09 7d 0a 0a 09 09 73 65  orted"...}....se
0e70: 74 20 66 69 6c 65 6b 65 79 20 24 6b 65 79 0a 09  t filekey $key..
0e80: 09 69 66 20 7b 24 6b 65 79 49 73 48 61 73 68 7d  .if {$keyIsHash}
0e90: 20 7b 0a 09 09 09 73 65 74 20 66 69 6c 65 6b 65   {....set fileke
0ea0: 79 20 5b 5f 68 61 73 68 5f 73 65 70 20 24 66 69  y [_hash_sep $fi
0eb0: 6c 65 6b 65 79 5d 0a 09 09 7d 0a 0a 09 09 73 65  lekey]...}....se
0ec0: 74 20 66 69 6c 65 20 5b 66 69 6c 65 20 6a 6f 69  t file [file joi
0ed0: 6e 20 24 3a 3a 61 70 70 66 73 3a 3a 63 61 63 68  n $::appfs::cach
0ee0: 65 64 69 72 20 24 66 69 6c 65 6b 65 79 5d 0a 0a  edir $filekey]..
0ef0: 09 09 66 69 6c 65 20 6d 6b 64 69 72 20 5b 66 69  ..file mkdir [fi
0f00: 6c 65 20 64 69 72 6e 61 6d 65 20 24 66 69 6c 65  le dirname $file
0f10: 5d 0a 0a 09 09 69 66 20 7b 5b 66 69 6c 65 20 65  ]....if {[file e
0f20: 78 69 73 74 73 20 24 66 69 6c 65 5d 7d 20 7b 0a  xists $file]} {.
0f30: 09 09 09 72 65 74 75 72 6e 20 24 66 69 6c 65 0a  ...return $file.
0f40: 09 09 7d 0a 0a 09 09 73 65 74 20 74 6d 70 66 69  ..}....set tmpfi
0f50: 6c 65 20 22 24 7b 66 69 6c 65 7d 2e 5b 65 78 70  le "${file}.[exp
0f60: 72 20 7b 72 61 6e 64 28 29 7d 5d 5b 63 6c 6f 63  r {rand()}][cloc
0f70: 6b 20 63 6c 69 63 6b 73 5d 22 0a 0a 09 09 73 65  k clicks]"....se
0f80: 74 20 66 64 20 5b 6f 70 65 6e 20 24 74 6d 70 66  t fd [open $tmpf
0f90: 69 6c 65 20 22 77 22 5d 0a 09 09 66 63 6f 6e 66  ile "w"]...fconf
0fa0: 69 67 75 72 65 20 24 66 64 20 2d 74 72 61 6e 73  igure $fd -trans
0fb0: 6c 61 74 69 6f 6e 20 62 69 6e 61 72 79 0a 0a 09  lation binary...
0fc0: 09 63 61 74 63 68 20 7b 0a 09 09 09 3a 3a 61 70  .catch {....::ap
0fd0: 70 66 73 3a 3a 75 73 65 72 3a 3a 64 6f 77 6e 6c  pfs::user::downl
0fe0: 6f 61 64 5f 66 69 6c 65 20 24 75 72 6c 20 24 66  oad_file $url $f
0ff0: 64 0a 09 09 7d 0a 0a 09 09 63 6c 6f 73 65 20 24  d...}....close $
1000: 66 64 0a 0a 09 09 69 66 20 7b 24 6b 65 79 49 73  fd....if {$keyIs
1010: 48 61 73 68 7d 20 7b 0a 09 09 09 73 65 74 20 68  Hash} {....set h
1020: 61 73 68 20 5b 73 74 72 69 6e 67 20 74 6f 6c 6f  ash [string tolo
1030: 77 65 72 20 5b 73 68 61 31 3a 3a 73 68 61 31 20  wer [sha1::sha1 
1040: 2d 68 65 78 20 2d 66 69 6c 65 20 24 74 6d 70 66  -hex -file $tmpf
1050: 69 6c 65 5d 5d 0a 09 09 7d 20 65 6c 73 65 20 7b  ile]]...} else {
1060: 0a 09 09 09 73 65 74 20 68 61 73 68 20 24 6b 65  ....set hash $ke
1070: 79 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 24 68 61  y...}....if {$ha
1080: 73 68 20 3d 3d 20 24 6b 65 79 7d 20 7b 0a 09 09  sh == $key} {...
1090: 09 66 69 6c 65 20 72 65 6e 61 6d 65 20 2d 66 6f  .file rename -fo
10a0: 72 63 65 20 2d 2d 20 24 74 6d 70 66 69 6c 65 20  rce -- $tmpfile 
10b0: 24 66 69 6c 65 0a 09 09 7d 20 65 6c 73 65 20 7b  $file...} else {
10c0: 0a 09 09 09 66 69 6c 65 20 64 65 6c 65 74 65 20  ....file delete 
10d0: 2d 66 6f 72 63 65 20 2d 2d 20 24 74 6d 70 66 69  -force -- $tmpfi
10e0: 6c 65 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e  le...}....return
10f0: 20 24 66 69 6c 65 0a 09 7d 0a 0a 0a 09 70 72 6f   $file..}....pro
1100: 63 20 5f 69 73 48 61 73 68 20 7b 76 61 6c 75 65  c _isHash {value
1110: 7d 20 7b 0a 09 09 73 65 74 20 76 61 6c 75 65 20  } {...set value 
1120: 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77 65 72 20  [string tolower 
1130: 24 76 61 6c 75 65 5d 0a 0a 09 09 69 66 20 7b 5b  $value]....if {[
1140: 73 74 72 69 6e 67 20 6c 65 6e 67 74 68 20 24 76  string length $v
1150: 61 6c 75 65 5d 20 21 3d 20 34 30 7d 20 7b 0a 09  alue] != 40} {..
1160: 09 09 72 65 74 75 72 6e 20 66 61 6c 73 65 0a 09  ..return false..
1170: 09 7d 0a 0a 09 09 69 66 20 7b 21 5b 72 65 67 65  .}....if {![rege
1180: 78 70 20 7b 5e 5b 30 2d 39 61 2d 66 5d 2a 24 7d  xp {^[0-9a-f]*$}
1190: 20 24 76 61 6c 75 65 5d 7d 20 7b 0a 09 09 09 72   $value]} {....r
11a0: 65 74 75 72 6e 20 66 61 6c 73 65 0a 09 09 7d 0a  eturn false...}.
11b0: 0a 09 09 72 65 74 75 72 6e 20 74 72 75 65 0a 09  ...return true..
11c0: 7d 0a 0a 09 70 72 6f 63 20 5f 76 65 72 69 66 79  }...proc _verify
11d0: 53 69 67 6e 61 74 75 72 65 41 6e 64 43 65 72 74  SignatureAndCert
11e0: 69 66 69 63 61 74 65 20 7b 68 6f 73 74 6e 61 6d  ificate {hostnam
11f0: 65 20 63 65 72 74 69 66 69 63 61 74 65 20 73 69  e certificate si
1200: 67 6e 61 74 75 72 65 20 68 61 73 68 7d 20 7b 0a  gnature hash} {.
1210: 09 09 73 65 74 20 63 65 72 74 69 66 69 63 61 74  ..set certificat
1220: 65 20 5b 62 69 6e 61 72 79 20 66 6f 72 6d 61 74  e [binary format
1230: 20 22 48 2a 22 20 24 63 65 72 74 69 66 69 63 61   "H*" $certifica
1240: 74 65 5d 0a 09 09 73 65 74 20 73 69 67 6e 61 74  te]...set signat
1250: 75 72 65 20 20 20 5b 62 69 6e 61 72 79 20 66 6f  ure   [binary fo
1260: 72 6d 61 74 20 22 48 2a 22 20 24 73 69 67 6e 61  rmat "H*" $signa
1270: 74 75 72 65 5d 0a 0a 09 09 73 65 74 20 63 65 72  ture]....set cer
1280: 74 69 66 69 63 61 74 65 20 5b 3a 3a 70 6b 69 3a  tificate [::pki:
1290: 3a 78 35 30 39 3a 3a 70 61 72 73 65 5f 63 65 72  :x509::parse_cer
12a0: 74 20 24 63 65 72 74 69 66 69 63 61 74 65 5d 0a  t $certificate].
12b0: 0a 09 09 61 72 72 61 79 20 73 65 74 20 63 65 72  ...array set cer
12c0: 74 69 66 69 63 61 74 65 5f 61 72 72 20 24 63 65  tificate_arr $ce
12d0: 72 74 69 66 69 63 61 74 65 0a 09 09 73 65 74 20  rtificate...set 
12e0: 63 65 72 74 69 66 69 63 61 74 65 5f 63 6e 20 5b  certificate_cn [
12f0: 3a 3a 70 6b 69 3a 3a 78 35 30 39 3a 3a 5f 64 6e  ::pki::x509::_dn
1300: 5f 74 6f 5f 63 6e 20 24 63 65 72 74 69 66 69 63  _to_cn $certific
1310: 61 74 65 5f 61 72 72 28 73 75 62 6a 65 63 74 29  ate_arr(subject)
1320: 5d 0a 0a 09 09 69 66 20 7b 21 5b 3a 3a 70 6b 69  ]....if {![::pki
1330: 3a 3a 76 65 72 69 66 79 20 24 73 69 67 6e 61 74  ::verify $signat
1340: 75 72 65 20 22 24 68 61 73 68 2c 73 68 61 31 22  ure "$hash,sha1"
1350: 20 24 63 65 72 74 69 66 69 63 61 74 65 5d 7d 20   $certificate]} 
1360: 7b 0a 09 09 09 72 65 74 75 72 6e 20 66 61 6c 73  {....return fals
1370: 65 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 5b 73 74  e...}....if {[st
1380: 72 69 6e 67 20 74 6f 6c 6f 77 65 72 20 24 63 65  ring tolower $ce
1390: 72 74 69 66 69 63 61 74 65 5f 63 6e 5d 20 21 3d  rtificate_cn] !=
13a0: 20 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77 65 72   [string tolower
13b0: 20 24 68 6f 73 74 6e 61 6d 65 5d 7d 20 7b 0a 09   $hostname]} {..
13c0: 09 09 72 65 74 75 72 6e 20 66 61 6c 73 65 0a 09  ..return false..
13d0: 09 7d 0a 0a 09 09 69 66 20 7b 21 5b 3a 3a 70 6b  .}....if {![::pk
13e0: 69 3a 3a 78 35 30 39 3a 3a 76 65 72 69 66 79 5f  i::x509::verify_
13f0: 63 65 72 74 20 24 63 65 72 74 69 66 69 63 61 74  cert $certificat
1400: 65 20 24 3a 3a 61 70 70 66 73 3a 3a 74 72 75 73  e $::appfs::trus
1410: 74 65 64 5f 63 61 73 5d 7d 20 7b 0a 09 09 09 72  ted_cas]} {....r
1420: 65 74 75 72 6e 20 66 61 6c 73 65 0a 09 09 7d 0a  eturn false...}.
1430: 0a 09 09 72 65 74 75 72 6e 20 74 72 75 65 0a 09  ...return true..
1440: 7d 0a 0a 09 70 72 6f 63 20 5f 6e 6f 72 6d 61 6c  }...proc _normal
1450: 69 7a 65 4f 53 20 7b 6f 73 20 7b 74 6f 6c 65 72  izeOS {os {toler
1460: 61 6e 74 20 30 7d 7d 20 7b 0a 09 09 73 65 74 20  ant 0}} {...set 
1470: 6f 73 20 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77  os [string tolow
1480: 65 72 20 5b 73 74 72 69 6e 67 20 74 72 69 6d 20  er [string trim 
1490: 24 6f 73 5d 5d 0a 0a 09 09 73 77 69 74 63 68 20  $os]]....switch 
14a0: 2d 2d 20 24 6f 73 20 7b 0a 09 09 09 22 6c 69 6e  -- $os {...."lin
14b0: 75 78 22 20 2d 20 22 66 72 65 65 62 73 64 22 20  ux" - "freebsd" 
14c0: 2d 20 22 6f 70 65 6e 62 73 64 22 20 2d 20 22 6e  - "openbsd" - "n
14d0: 65 74 62 73 64 22 20 7b 0a 09 09 09 09 72 65 74  etbsd" {.....ret
14e0: 75 72 6e 20 24 6f 73 0a 09 09 09 7d 0a 09 09 09  urn $os....}....
14f0: 22 73 75 6e 6f 73 22 20 7b 0a 09 09 09 09 72 65  "sunos" {.....re
1500: 74 75 72 6e 20 22 73 6f 6c 61 72 69 73 22 0a 09  turn "solaris"..
1510: 09 09 7d 0a 09 09 09 22 6e 6f 61 72 63 68 22 20  ..}...."noarch" 
1520: 2d 20 22 6e 6f 6e 65 22 20 2d 20 22 61 6e 79 22  - "none" - "any"
1530: 20 2d 20 22 61 6c 6c 22 20 7b 0a 09 09 09 09 72   - "all" {.....r
1540: 65 74 75 72 6e 20 22 6e 6f 61 72 63 68 22 0a 09  eturn "noarch"..
1550: 09 09 7d 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 24  ..}...}....if {$
1560: 74 6f 6c 65 72 61 6e 74 7d 20 7b 0a 09 09 09 72  tolerant} {....r
1570: 65 74 75 72 6e 20 24 6f 73 0a 09 09 7d 0a 0a 09  eturn $os...}...
1580: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
1590: 72 6f 72 20 22 55 6e 61 62 6c 65 20 74 6f 20 6e  ror "Unable to n
15a0: 6f 72 6d 61 6c 69 7a 65 20 4f 53 3a 20 24 6f 73  ormalize OS: $os
15b0: 22 0a 09 7d 0a 0a 09 70 72 6f 63 20 5f 6e 6f 72  "..}...proc _nor
15c0: 6d 61 6c 69 7a 65 43 50 55 20 7b 63 70 75 20 7b  malizeCPU {cpu {
15d0: 74 6f 6c 65 72 61 6e 74 20 30 7d 7d 20 7b 0a 09  tolerant 0}} {..
15e0: 09 73 65 74 20 63 70 75 20 5b 73 74 72 69 6e 67  .set cpu [string
15f0: 20 74 6f 6c 6f 77 65 72 20 5b 73 74 72 69 6e 67   tolower [string
1600: 20 74 72 69 6d 20 24 63 70 75 5d 5d 0a 0a 09 09   trim $cpu]]....
1610: 73 77 69 74 63 68 20 2d 67 6c 6f 62 20 2d 2d 20  switch -glob -- 
1620: 24 63 70 75 20 7b 0a 09 09 09 22 69 3f 38 36 22  $cpu {...."i?86"
1630: 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20 22 69   {.....return "i
1640: 78 38 36 22 0a 09 09 09 7d 0a 09 09 09 22 78 38  x86"....}...."x8
1650: 36 5f 36 34 22 20 7b 0a 09 09 09 09 72 65 74 75  6_64" {.....retu
1660: 72 6e 20 24 63 70 75 0a 09 09 09 7d 0a 09 09 09  rn $cpu....}....
1670: 22 6e 6f 61 72 63 68 22 20 2d 20 22 6e 6f 6e 65  "noarch" - "none
1680: 22 20 2d 20 22 61 6e 79 22 20 2d 20 22 61 6c 6c  " - "any" - "all
1690: 22 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20 22  " {.....return "
16a0: 6e 6f 61 72 63 68 22 0a 09 09 09 7d 0a 09 09 7d  noarch"....}...}
16b0: 0a 0a 09 09 69 66 20 7b 24 74 6f 6c 65 72 61 6e  ....if {$toleran
16c0: 74 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 24  t} {....return $
16d0: 63 70 75 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72  cpu...}....retur
16e0: 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55  n -code error "U
16f0: 6e 61 62 6c 65 20 74 6f 20 6e 6f 72 6d 61 6c 69  nable to normali
1700: 7a 65 20 43 50 55 3a 20 24 63 70 75 22 0a 09 7d  ze CPU: $cpu"..}
1710: 0a 0a 09 70 72 6f 63 20 5f 61 73 5f 75 73 65 72  ...proc _as_user
1720: 20 7b 63 6f 64 65 7d 20 7b 0a 09 09 3a 3a 61 70   {code} {...::ap
1730: 70 66 73 64 3a 3a 73 69 6d 75 6c 61 74 65 5f 75  pfsd::simulate_u
1740: 73 65 72 5f 66 73 5f 65 6e 74 65 72 0a 0a 09 09  ser_fs_enter....
1750: 73 65 74 20 72 65 74 63 6f 64 65 20 5b 63 61 74  set retcode [cat
1760: 63 68 20 5b 6c 69 73 74 20 75 70 6c 65 76 65 6c  ch [list uplevel
1770: 20 24 63 6f 64 65 5d 20 72 65 74 73 74 72 5d 0a   $code] retstr].
1780: 0a 09 09 3a 3a 61 70 70 66 73 64 3a 3a 73 69 6d  ...::appfsd::sim
1790: 75 6c 61 74 65 5f 75 73 65 72 5f 66 73 5f 6c 65  ulate_user_fs_le
17a0: 61 76 65 0a 0a 09 09 72 65 74 75 72 6e 20 2d 63  ave....return -c
17b0: 6f 64 65 20 24 72 65 74 63 6f 64 65 20 24 72 65  ode $retcode $re
17c0: 74 73 74 72 0a 09 7d 0a 0a 09 70 72 6f 63 20 69  tstr..}...proc i
17d0: 6e 69 74 20 7b 7d 20 7b 0a 09 09 69 66 20 7b 5b  nit {} {...if {[
17e0: 69 6e 66 6f 20 65 78 69 73 74 73 20 3a 3a 61 70  info exists ::ap
17f0: 70 66 73 3a 3a 69 6e 69 74 5f 63 61 6c 6c 65 64  pfs::init_called
1800: 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 0a 09  ]} {....return..
1810: 09 7d 0a 0a 09 09 23 20 46 6f 72 63 65 20 5b 70  .}....# Force [p
1820: 61 72 72 61 79 5d 20 61 6e 64 20 5b 63 6c 6f 63  array] and [cloc
1830: 6b 5d 20 74 6f 20 62 65 20 6c 6f 61 64 65 64 0a  k] to be loaded.
1840: 09 09 63 61 74 63 68 20 7b 0a 09 09 09 70 61 72  ..catch {....par
1850: 72 61 79 20 64 6f 65 73 5f 6e 6f 74 5f 65 78 69  ray does_not_exi
1860: 73 74 0a 09 09 7d 0a 09 09 63 61 74 63 68 20 7b  st...}...catch {
1870: 0a 09 09 09 63 6c 6f 63 6b 20 73 65 63 6f 6e 64  ....clock second
1880: 73 0a 09 09 7d 0a 09 09 63 61 74 63 68 20 7b 0a  s...}...catch {.
1890: 09 09 09 63 6c 6f 63 6b 20 61 64 64 20 5b 63 6c  ...clock add [cl
18a0: 6f 63 6b 20 73 65 63 6f 6e 64 73 5d 20 33 20 73  ock seconds] 3 s
18b0: 65 63 6f 6e 64 73 0a 09 09 7d 0a 0a 09 09 73 65  econds...}....se
18c0: 74 20 3a 3a 61 70 70 66 73 3a 3a 69 6e 69 74 5f  t ::appfs::init_
18d0: 63 61 6c 6c 65 64 20 31 0a 0a 09 09 23 20 41 64  called 1....# Ad
18e0: 64 20 61 20 64 65 66 61 75 6c 74 20 43 41 20 74  d a default CA t
18f0: 6f 20 6c 69 73 74 20 6f 66 20 74 72 75 73 74 65  o list of truste
1900: 64 20 43 41 73 0a 09 09 6c 61 70 70 65 6e 64 20  d CAs...lappend 
1910: 3a 3a 61 70 70 66 73 3a 3a 74 72 75 73 74 65 64  ::appfs::trusted
1920: 5f 63 61 73 20 5b 3a 3a 70 6b 69 3a 3a 78 35 30  _cas [::pki::x50
1930: 39 3a 3a 70 61 72 73 65 5f 63 65 72 74 20 7b 0a  9::parse_cert {.
1940: 2d 2d 2d 2d 2d 42 45 47 49 4e 20 43 45 52 54 49  -----BEGIN CERTI
1950: 46 49 43 41 54 45 2d 2d 2d 2d 2d 0a 4d 49 49 43  FICATE-----.MIIC
1960: 37 44 43 43 41 64 53 67 41 77 49 42 41 67 49 42  7DCCAdSgAwIBAgIB
1970: 41 54 41 4e 42 67 6b 71 68 6b 69 47 39 77 30 42  ATANBgkqhkiG9w0B
1980: 41 51 55 46 41 44 41 76 4d 52 49 77 45 41 59 44  AQUFADAvMRIwEAYD
1990: 56 51 51 4b 45 77 6c 53 62 33 6b 67 0a 53 32 56  VQQKEwlSb3kg.S2V
19a0: 6c 62 6d 55 78 47 54 41 58 42 67 4e 56 42 41 4d  lbmUxGTAXBgNVBAM
19b0: 54 45 45 46 77 63 45 5a 54 49 45 74 6c 65 53 42  TEEFwcEZTIEtleSB
19c0: 4e 59 58 4e 30 5a 58 49 77 48 68 63 4e 4d 54 51  NYXN0ZXIwHhcNMTQ
19d0: 78 4d 54 45 33 4d 6a 41 78 4e 7a 49 34 0a 57 68  xMTE3MjAxNzI4.Wh
19e0: 63 4e 4d 54 6b 78 4d 54 45 33 4d 6a 41 78 4e 7a  cNMTkxMTE3MjAxNz
19f0: 49 34 57 6a 41 76 4d 52 49 77 45 41 59 44 56 51  I4WjAvMRIwEAYDVQ
1a00: 51 4b 45 77 6c 53 62 33 6b 67 53 32 56 6c 62 6d  QKEwlSb3kgS2Vlbm
1a10: 55 78 47 54 41 58 42 67 4e 56 42 41 4d 54 0a 45  UxGTAXBgNVBAMT.E
1a20: 45 46 77 63 45 5a 54 49 45 74 6c 65 53 42 4e 59  EFwcEZTIEtleSBNY
1a30: 58 4e 30 5a 58 49 77 67 67 45 69 4d 41 30 47 43  XN0ZXIwggEiMA0GC
1a40: 53 71 47 53 49 62 33 44 51 45 42 41 51 55 41 41  SqGSIb3DQEBAQUAA
1a50: 34 49 42 44 77 41 77 67 67 45 4b 41 6f 49 42 0a  4IBDwAwggEKAoIB.
1a60: 41 51 43 71 36 75 53 4b 34 36 79 47 35 62 36 52  AQCq6uSK46yG5b6R
1a70: 4a 57 77 52 6c 76 77 35 67 6c 41 6e 6a 73 63 31  JWwRlvw5glAnjsc1
1a80: 47 69 58 33 64 75 58 41 30 76 47 34 71 6e 4b 55  GiX3duXA0vG4qnKU
1a90: 6e 44 74 6c 2f 6a 63 4d 6d 71 32 47 4d 4f 42 39  nDtl/jcMmq2GMOB9
1aa0: 0a 49 79 31 74 6a 61 62 45 48 41 30 4d 68 57 32  .Iy1tjabEHA0MhW2
1ab0: 6a 37 56 77 65 2f 4f 39 4d 4c 46 4a 6b 4a 33 30  j7Vwe/O9MLFJkJ30
1ac0: 4d 31 50 56 44 37 59 5a 52 52 4e 61 41 73 7a 33  M1PVD7YZRRNaAsz3
1ad0: 55 57 49 4b 45 6a 50 49 37 42 42 63 33 32 4b 4f  UWIKEjPI7BBc32KO
1ae0: 6d 0a 42 4c 33 43 54 58 43 43 64 7a 6c 6c 4c 31  m.BL3CTXCCdzllL1
1af0: 48 68 56 62 6e 4d 35 69 43 41 6d 67 48 63 67 31  HhVbnM5iCAmgHcg1
1b00: 44 55 6b 2f 45 76 57 58 76 6e 45 44 78 58 52 79  DUk/EvWXvnEDxXRy
1b10: 32 6c 56 39 6d 51 73 6d 44 65 64 72 66 66 59 37  2lV9mQsmDedrffY7
1b20: 57 6c 0a 4f 72 35 37 6e 6c 63 7a 61 4d 75 50 4c  Wl.Or57nlczaMuPL
1b30: 70 79 52 53 6b 76 37 35 50 41 6e 6a 51 4a 78 54  pyRSkv75PAnjQJxT
1b40: 33 73 57 6c 42 70 79 2b 2f 48 39 49 6d 75 64 51  3sWlBpy+/H9ImudQ
1b50: 64 70 4a 4e 66 2f 46 74 78 63 71 4e 37 69 44 77  dpJNf/FtxcqN7iDw
1b60: 48 35 42 0a 76 49 63 65 59 45 74 44 56 78 46 73  H5B.vIceYEtDVxFs
1b70: 76 6f 35 48 4f 56 6b 53 6c 39 6a 65 6f 35 45 34  vo5HOVkSl9jeo5E4
1b80: 47 70 65 33 77 79 66 52 68 6f 71 42 32 55 6b 61  Gpe3wyfRhoqB2Uka
1b90: 57 31 4b 71 30 69 48 35 52 2b 30 30 53 37 36 30  W1Kq0iH5R+00S760
1ba0: 78 51 4d 78 0a 4c 4c 39 4c 31 64 75 68 75 31 64  xQMx.LL9L1duhu1d
1bb0: 4c 37 48 73 6d 45 77 37 49 65 59 55 52 41 67 4d  L7HsmEw7IeYURAgM
1bc0: 42 41 41 47 6a 45 7a 41 52 4d 41 38 47 41 31 55  BAAGjEzARMA8GA1U
1bd0: 64 45 77 45 42 2f 77 51 46 4d 41 4d 42 41 66 38  dEwEB/wQFMAMBAf8
1be0: 77 44 51 59 4a 0a 4b 6f 5a 49 68 76 63 4e 41 51  wDQYJ.KoZIhvcNAQ
1bf0: 45 46 42 51 41 44 67 67 45 42 41 4b 68 4f 34 5a  EFBQADggEBAKhO4Z
1c00: 53 7a 59 50 33 37 42 71 69 78 4e 48 4b 4b 39 2b  SzYP37BqixNHKK9+
1c10: 67 53 65 43 36 46 67 61 38 35 69 4c 57 68 77 70  gSeC6Fga85iLWhwp
1c20: 50 57 30 6b 53 6c 0a 7a 30 33 68 61 6c 38 30 4b  PW0kSl.z03hal80K
1c30: 5a 2b 6b 50 4d 7a 62 38 43 35 32 4e 32 38 33 74  Z+kPMzb8C52N283t
1c40: 51 4e 41 71 4a 39 51 38 61 6b 44 50 5a 78 53 7a  QNAqJ9Q8akDPZxSz
1c50: 7a 4d 55 56 4f 47 70 47 77 32 70 4a 37 5a 73 77  zMUVOGpGw2pJ7Zsw
1c60: 4b 44 7a 30 5a 54 61 0a 30 65 64 71 2f 67 64 54  KDz0ZTa.0edq/gdT
1c70: 2f 48 72 64 65 67 76 4e 74 44 50 63 32 6a 6f 6e  /HrdegvNtDPc2jon
1c80: 61 35 46 56 4f 59 71 77 64 63 7a 35 6b 62 6c 31  a5FVOYqwdcz5kbl1
1c90: 55 57 42 61 42 70 33 56 58 55 67 63 59 6a 58 53  UWBaBp3VXUgcYjXS
1ca0: 52 61 42 4b 34 33 57 64 0a 63 76 65 69 44 55 65  RaBK43Wd.cveiDUe
1cb0: 5a 77 37 67 48 71 52 53 4e 2f 41 79 59 55 43 74  Zw7gHqRSN/AyYUCt
1cc0: 4a 7a 57 6d 76 47 73 4a 75 49 46 68 4d 42 6f 6e  JzWmvGsJuIFhMBon
1cd0: 75 7a 38 6a 79 6c 68 79 4d 4a 43 59 4a 46 54 34  uz8jylhyMJCYJFT4
1ce0: 69 4d 55 43 38 4d 4e 49 77 0a 6e 69 58 31 78 78  iMUC8MNIw.niX1xx
1cf0: 2b 4e 75 36 66 50 56 35 5a 5a 48 6a 39 72 62 68  +Nu6fPV5ZZHj9rbh
1d00: 69 42 61 4c 6a 6d 2b 74 6b 44 77 74 50 67 41 33  iBaLjm+tkDwtPgA3
1d10: 6a 32 70 78 76 48 4b 59 70 74 75 57 78 65 59 4f  j2pxvHKYptuWxeYO
1d20: 2b 39 44 44 4e 61 39 73 43 62 0a 45 35 41 6e 4a  +9DDNa9sCb.E5AnJ
1d30: 49 6c 4f 6e 64 2f 74 47 65 30 43 68 66 30 73 46  IlOnd/tGe0Chf0sF
1d40: 51 67 2b 6c 39 6e 4e 69 4e 72 57 47 67 7a 64 64  Qg+l9nNiNrWGgzdd
1d50: 39 5a 50 4a 4b 34 3d 0a 2d 2d 2d 2d 2d 45 4e 44  9ZPJK4=.-----END
1d60: 20 43 45 52 54 49 46 49 43 41 54 45 2d 2d 2d 2d   CERTIFICATE----
1d70: 2d 0a 7d 5d 0a 0a 09 09 23 20 4c 6f 61 64 20 63  -.}]....# Load c
1d80: 6f 6e 66 69 67 75 72 61 74 69 6f 6e 20 66 69 6c  onfiguration fil
1d90: 65 0a 09 09 73 65 74 20 63 6f 6e 66 69 67 5f 66  e...set config_f
1da0: 69 6c 65 20 5b 66 69 6c 65 20 6a 6f 69 6e 20 24  ile [file join $
1db0: 3a 3a 61 70 70 66 73 3a 3a 63 61 63 68 65 64 69  ::appfs::cachedi
1dc0: 72 20 63 6f 6e 66 69 67 5d 0a 09 09 69 66 20 7b  r config]...if {
1dd0: 5b 66 69 6c 65 20 65 78 69 73 74 73 20 24 63 6f  [file exists $co
1de0: 6e 66 69 67 5f 66 69 6c 65 5d 7d 20 7b 0a 09 09  nfig_file]} {...
1df0: 09 73 6f 75 72 63 65 20 24 63 6f 6e 66 69 67 5f  .source $config_
1e00: 66 69 6c 65 0a 09 09 7d 0a 0a 09 09 69 66 20 7b  file...}....if {
1e10: 21 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 3a 3a  ![info exists ::
1e20: 61 70 70 66 73 3a 3a 64 62 5d 7d 20 7b 0a 09 09  appfs::db]} {...
1e30: 09 66 69 6c 65 20 6d 6b 64 69 72 20 24 3a 3a 61  .file mkdir $::a
1e40: 70 70 66 73 3a 3a 63 61 63 68 65 64 69 72 0a 0a  ppfs::cachedir..
1e50: 09 09 09 73 71 6c 69 74 65 33 20 3a 3a 61 70 70  ...sqlite3 ::app
1e60: 66 73 3a 3a 64 62 20 5b 66 69 6c 65 20 6a 6f 69  fs::db [file joi
1e70: 6e 20 24 3a 3a 61 70 70 66 73 3a 3a 63 61 63 68  n $::appfs::cach
1e80: 65 64 69 72 20 63 61 63 68 65 2e 64 62 5d 0a 0a  edir cache.db]..
1e90: 09 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20 74  ...::appfs::db t
1ea0: 69 6d 65 6f 75 74 20 33 30 30 30 30 0a 09 09 7d  imeout 30000...}
1eb0: 0a 0a 09 09 23 20 43 72 65 61 74 65 20 74 61 62  ....# Create tab
1ec0: 6c 65 73 0a 09 09 64 62 20 65 76 61 6c 20 7b 43  les...db eval {C
1ed0: 52 45 41 54 45 20 54 41 42 4c 45 20 49 46 20 4e  REATE TABLE IF N
1ee0: 4f 54 20 45 58 49 53 54 53 20 73 69 74 65 73 28  OT EXISTS sites(
1ef0: 68 6f 73 74 6e 61 6d 65 20 50 52 49 4d 41 52 59  hostname PRIMARY
1f00: 20 4b 45 59 2c 20 6c 61 73 74 55 70 64 61 74 65   KEY, lastUpdate
1f10: 2c 20 74 74 6c 29 3b 7d 0a 09 09 64 62 20 65 76  , ttl);}...db ev
1f20: 61 6c 20 7b 43 52 45 41 54 45 20 54 41 42 4c 45  al {CREATE TABLE
1f30: 20 49 46 20 4e 4f 54 20 45 58 49 53 54 53 20 70   IF NOT EXISTS p
1f40: 61 63 6b 61 67 65 73 28 68 6f 73 74 6e 61 6d 65  ackages(hostname
1f50: 2c 20 73 68 61 31 2c 20 70 61 63 6b 61 67 65 2c  , sha1, package,
1f60: 20 76 65 72 73 69 6f 6e 2c 20 6f 73 2c 20 63 70   version, os, cp
1f70: 75 41 72 63 68 2c 20 69 73 4c 61 74 65 73 74 2c  uArch, isLatest,
1f80: 20 68 61 76 65 4d 61 6e 69 66 65 73 74 29 3b 7d   haveManifest);}
1f90: 0a 09 09 64 62 20 65 76 61 6c 20 7b 43 52 45 41  ...db eval {CREA
1fa0: 54 45 20 54 41 42 4c 45 20 49 46 20 4e 4f 54 20  TE TABLE IF NOT 
1fb0: 45 58 49 53 54 53 20 66 69 6c 65 73 28 70 61 63  EXISTS files(pac
1fc0: 6b 61 67 65 5f 73 68 61 31 2c 20 74 79 70 65 2c  kage_sha1, type,
1fd0: 20 74 69 6d 65 2c 20 73 6f 75 72 63 65 2c 20 73   time, source, s
1fe0: 69 7a 65 2c 20 70 65 72 6d 73 2c 20 66 69 6c 65  ize, perms, file
1ff0: 5f 73 68 61 31 2c 20 66 69 6c 65 5f 6e 61 6d 65  _sha1, file_name
2000: 2c 20 66 69 6c 65 5f 64 69 72 65 63 74 6f 72 79  , file_directory
2010: 29 3b 7d 0a 0a 09 09 23 20 43 72 65 61 74 65 20  );}....# Create 
2020: 69 6e 64 65 78 65 73 0a 09 09 64 62 20 65 76 61  indexes...db eva
2030: 6c 20 7b 43 52 45 41 54 45 20 49 4e 44 45 58 20  l {CREATE INDEX 
2040: 49 46 20 4e 4f 54 20 45 58 49 53 54 53 20 73 69  IF NOT EXISTS si
2050: 74 65 73 5f 69 6e 64 65 78 20 4f 4e 20 73 69 74  tes_index ON sit
2060: 65 73 20 28 68 6f 73 74 6e 61 6d 65 29 3b 7d 0a  es (hostname);}.
2070: 09 09 64 62 20 65 76 61 6c 20 7b 43 52 45 41 54  ..db eval {CREAT
2080: 45 20 49 4e 44 45 58 20 49 46 20 4e 4f 54 20 45  E INDEX IF NOT E
2090: 58 49 53 54 53 20 70 61 63 6b 61 67 65 73 5f 69  XISTS packages_i
20a0: 6e 64 65 78 20 4f 4e 20 70 61 63 6b 61 67 65 73  ndex ON packages
20b0: 20 28 68 6f 73 74 6e 61 6d 65 2c 20 73 68 61 31   (hostname, sha1
20c0: 2c 20 70 61 63 6b 61 67 65 2c 20 76 65 72 73 69  , package, versi
20d0: 6f 6e 2c 20 6f 73 2c 20 63 70 75 41 72 63 68 29  on, os, cpuArch)
20e0: 3b 7d 0a 09 09 64 62 20 65 76 61 6c 20 7b 43 52  ;}...db eval {CR
20f0: 45 41 54 45 20 49 4e 44 45 58 20 49 46 20 4e 4f  EATE INDEX IF NO
2100: 54 20 45 58 49 53 54 53 20 66 69 6c 65 73 5f 69  T EXISTS files_i
2110: 6e 64 65 78 20 4f 4e 20 66 69 6c 65 73 20 28 70  ndex ON files (p
2120: 61 63 6b 61 67 65 5f 73 68 61 31 2c 20 66 69 6c  ackage_sha1, fil
2130: 65 5f 6e 61 6d 65 2c 20 66 69 6c 65 5f 64 69 72  e_name, file_dir
2140: 65 63 74 6f 72 79 29 3b 7d 0a 09 7d 0a 0a 09 70  ectory);}..}...p
2150: 72 6f 63 20 64 6f 77 6e 6c 6f 61 64 20 7b 68 6f  roc download {ho
2160: 73 74 6e 61 6d 65 20 68 61 73 68 20 7b 6d 65 74  stname hash {met
2170: 68 6f 64 20 73 68 61 31 7d 7d 20 7b 0a 09 09 73  hod sha1}} {...s
2180: 65 74 20 75 72 6c 20 5b 3a 3a 61 70 70 66 73 3a  et url [::appfs:
2190: 3a 75 73 65 72 3a 3a 63 6f 6e 73 74 72 75 63 74  :user::construct
21a0: 5f 75 72 6c 20 24 68 6f 73 74 6e 61 6d 65 20 24  _url $hostname $
21b0: 68 61 73 68 20 24 6d 65 74 68 6f 64 5d 0a 09 09  hash $method]...
21c0: 73 65 74 20 66 69 6c 65 20 5b 5f 63 61 63 68 65  set file [_cache
21d0: 66 69 6c 65 20 24 75 72 6c 20 24 68 61 73 68 20  file $url $hash 
21e0: 24 6d 65 74 68 6f 64 5d 0a 0a 09 09 69 66 20 7b  $method]....if {
21f0: 21 5b 66 69 6c 65 20 65 78 69 73 74 73 20 24 66  ![file exists $f
2200: 69 6c 65 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72  ile]} {....retur
2210: 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55  n -code error "U
2220: 6e 61 62 6c 65 20 74 6f 20 66 65 74 63 68 20 28  nable to fetch (
2230: 66 69 6c 65 20 64 6f 65 73 20 6e 6f 74 20 65 78  file does not ex
2240: 69 73 74 3a 20 24 66 69 6c 65 29 22 0a 09 09 7d  ist: $file)"...}
2250: 0a 0a 09 09 72 65 74 75 72 6e 20 24 66 69 6c 65  ....return $file
2260: 0a 09 7d 0a 0a 09 70 72 6f 63 20 67 65 74 69 6e  ..}...proc getin
2270: 64 65 78 20 7b 68 6f 73 74 6e 61 6d 65 7d 20 7b  dex {hostname} {
2280: 0a 09 09 69 66 20 7b 5b 73 74 72 69 6e 67 20 6d  ...if {[string m
2290: 61 74 63 68 20 22 2a 5c 5b 2f 7e 5c 5d 2a 22 20  atch "*\[/~\]*" 
22a0: 24 68 6f 73 74 6e 61 6d 65 5d 7d 20 7b 0a 09 09  $hostname]} {...
22b0: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
22c0: 72 6f 72 20 22 49 6e 76 61 6c 69 64 20 68 6f 73  ror "Invalid hos
22d0: 74 6e 61 6d 65 22 0a 09 09 7d 0a 0a 09 09 73 65  tname"...}....se
22e0: 74 20 6e 6f 77 20 5b 63 6c 6f 63 6b 20 73 65 63  t now [clock sec
22f0: 6f 6e 64 73 5d 0a 0a 09 09 73 65 74 20 6c 61 73  onds]....set las
2300: 74 55 70 64 61 74 65 73 20 5b 64 62 20 65 76 61  tUpdates [db eva
2310: 6c 20 7b 53 45 4c 45 43 54 20 6c 61 73 74 55 70  l {SELECT lastUp
2320: 64 61 74 65 2c 20 74 74 6c 20 46 52 4f 4d 20 73  date, ttl FROM s
2330: 69 74 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e  ites WHERE hostn
2340: 61 6d 65 20 3d 20 24 68 6f 73 74 6e 61 6d 65 20  ame = $hostname 
2350: 4c 49 4d 49 54 20 31 3b 7d 5d 0a 09 09 69 66 20  LIMIT 1;}]...if 
2360: 7b 5b 6c 6c 65 6e 67 74 68 20 24 6c 61 73 74 55  {[llength $lastU
2370: 70 64 61 74 65 73 5d 20 3d 3d 20 30 7d 20 7b 0a  pdates] == 0} {.
2380: 09 09 09 73 65 74 20 6c 61 73 74 55 70 64 61 74  ...set lastUpdat
2390: 65 20 30 0a 09 09 09 73 65 74 20 74 74 6c 20 30  e 0....set ttl 0
23a0: 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 73  ...} else {....s
23b0: 65 74 20 6c 61 73 74 55 70 64 61 74 65 20 5b 6c  et lastUpdate [l
23c0: 69 6e 64 65 78 20 24 6c 61 73 74 55 70 64 61 74  index $lastUpdat
23d0: 65 73 20 30 5d 0a 09 09 09 73 65 74 20 74 74 6c  es 0]....set ttl
23e0: 20 5b 6c 69 6e 64 65 78 20 24 6c 61 73 74 55 70   [lindex $lastUp
23f0: 64 61 74 65 73 20 31 5d 0a 09 09 7d 0a 0a 09 09  dates 1]...}....
2400: 69 66 20 7b 24 6e 6f 77 20 3c 20 28 24 6c 61 73  if {$now < ($las
2410: 74 55 70 64 61 74 65 20 2b 20 24 74 74 6c 29 7d  tUpdate + $ttl)}
2420: 20 7b 0a 09 09 09 72 65 74 75 72 6e 20 43 4f 4d   {....return COM
2430: 50 4c 45 54 45 0a 09 09 7d 0a 0a 09 09 73 65 74  PLETE...}....set
2440: 20 75 72 6c 20 22 68 74 74 70 3a 2f 2f 24 68 6f   url "http://$ho
2450: 73 74 6e 61 6d 65 2f 61 70 70 66 73 2f 69 6e 64  stname/appfs/ind
2460: 65 78 22 0a 0a 09 09 63 61 74 63 68 20 7b 0a 09  ex"....catch {..
2470: 09 09 73 65 74 20 69 6e 64 65 78 68 61 73 68 5f  ..set indexhash_
2480: 64 61 74 61 20 5b 3a 3a 61 70 70 66 73 3a 3a 75  data [::appfs::u
2490: 73 65 72 3a 3a 64 6f 77 6e 6c 6f 61 64 5f 66 69  ser::download_fi
24a0: 6c 65 20 24 75 72 6c 5d 0a 09 09 7d 0a 0a 09 09  le $url]...}....
24b0: 23 20 4e 6f 74 65 20 74 68 61 74 20 77 65 20 61  # Note that we a
24c0: 74 74 65 6d 70 74 65 64 20 74 6f 20 66 65 74 63  ttempted to fetc
24d0: 68 20 74 68 69 73 20 69 6e 64 65 78 20 61 6e 64  h this index and
24e0: 20 64 6f 20 6e 6f 74 20 74 72 79 0a 09 09 23 20   do not try...# 
24f0: 61 67 61 69 6e 20 66 6f 72 20 61 20 77 68 69 6c  again for a whil
2500: 65 0a 09 09 64 62 20 65 76 61 6c 20 7b 49 4e 53  e...db eval {INS
2510: 45 52 54 20 4f 52 20 52 45 50 4c 41 43 45 20 49  ERT OR REPLACE I
2520: 4e 54 4f 20 73 69 74 65 73 20 28 68 6f 73 74 6e  NTO sites (hostn
2530: 61 6d 65 2c 20 6c 61 73 74 55 70 64 61 74 65 2c  ame, lastUpdate,
2540: 20 74 74 6c 29 20 56 41 4c 55 45 53 20 28 24 68   ttl) VALUES ($h
2550: 6f 73 74 6e 61 6d 65 2c 20 24 6e 6f 77 2c 20 24  ostname, $now, $
2560: 3a 3a 61 70 70 66 73 3a 3a 6e 74 74 6c 29 3b 7d  ::appfs::nttl);}
2570: 0a 0a 09 09 69 66 20 7b 21 5b 69 6e 66 6f 20 65  ....if {![info e
2580: 78 69 73 74 73 20 69 6e 64 65 78 68 61 73 68 5f  xists indexhash_
2590: 64 61 74 61 5d 7d 20 7b 0a 09 09 09 72 65 74 75  data]} {....retu
25a0: 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22  rn -code error "
25b0: 55 6e 61 62 6c 65 20 74 6f 20 66 65 74 63 68 20  Unable to fetch 
25c0: 24 75 72 6c 22 0a 09 09 7d 0a 0a 09 09 73 65 74  $url"...}....set
25d0: 20 69 6e 64 65 78 68 61 73 68 5f 64 61 74 61 20   indexhash_data 
25e0: 5b 73 74 72 69 6e 67 20 74 72 69 6d 20 24 69 6e  [string trim $in
25f0: 64 65 78 68 61 73 68 5f 64 61 74 61 20 22 5c 72  dexhash_data "\r
2600: 5c 6e 22 5d 0a 09 09 73 65 74 20 69 6e 64 65 78  \n"]...set index
2610: 68 61 73 68 5f 64 61 74 61 20 5b 73 70 6c 69 74  hash_data [split
2620: 20 24 69 6e 64 65 78 68 61 73 68 5f 64 61 74 61   $indexhash_data
2630: 20 22 2c 22 5d 0a 09 09 73 65 74 20 69 6e 64 65   ","]...set inde
2640: 78 68 61 73 68 20 20 20 20 20 20 20 5b 6c 69 6e  xhash       [lin
2650: 64 65 78 20 24 69 6e 64 65 78 68 61 73 68 5f 64  dex $indexhash_d
2660: 61 74 61 20 30 5d 0a 09 09 73 65 74 20 69 6e 64  ata 0]...set ind
2670: 65 78 68 61 73 68 6d 65 74 68 6f 64 20 5b 6c 69  exhashmethod [li
2680: 6e 64 65 78 20 24 69 6e 64 65 78 68 61 73 68 5f  ndex $indexhash_
2690: 64 61 74 61 20 31 5d 0a 09 09 73 65 74 20 69 6e  data 1]...set in
26a0: 64 65 78 68 61 73 68 63 65 72 74 20 20 20 5b 6c  dexhashcert   [l
26b0: 69 6e 64 65 78 20 24 69 6e 64 65 78 68 61 73 68  index $indexhash
26c0: 5f 64 61 74 61 20 32 5d 0a 09 09 73 65 74 20 69  _data 2]...set i
26d0: 6e 64 65 78 68 61 73 68 73 69 67 20 20 20 20 5b  ndexhashsig    [
26e0: 6c 69 6e 64 65 78 20 24 69 6e 64 65 78 68 61 73  lindex $indexhas
26f0: 68 5f 64 61 74 61 20 33 5d 0a 0a 09 09 69 66 20  h_data 3]....if 
2700: 7b 21 5b 5f 69 73 48 61 73 68 20 24 69 6e 64 65  {![_isHash $inde
2710: 78 68 61 73 68 5d 7d 20 7b 0a 09 09 09 72 65 74  xhash]} {....ret
2720: 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20  urn -code error 
2730: 22 49 6e 76 61 6c 69 64 20 68 61 73 68 3a 20 24  "Invalid hash: $
2740: 69 6e 64 65 78 68 61 73 68 22 0a 09 09 7d 0a 0a  indexhash"...}..
2750: 09 09 69 66 20 7b 21 5b 5f 76 65 72 69 66 79 53  ..if {![_verifyS
2760: 69 67 6e 61 74 75 72 65 41 6e 64 43 65 72 74 69  ignatureAndCerti
2770: 66 69 63 61 74 65 20 24 68 6f 73 74 6e 61 6d 65  ficate $hostname
2780: 20 24 69 6e 64 65 78 68 61 73 68 63 65 72 74 20   $indexhashcert 
2790: 24 69 6e 64 65 78 68 61 73 68 73 69 67 20 24 69  $indexhashsig $i
27a0: 6e 64 65 78 68 61 73 68 5d 7d 20 7b 0a 09 09 09  ndexhash]} {....
27b0: 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72  return -code err
27c0: 6f 72 20 22 49 6e 76 61 6c 69 64 20 73 69 67 6e  or "Invalid sign
27d0: 61 74 75 72 65 20 6f 72 20 63 65 72 74 69 66 69  ature or certifi
27e0: 63 61 74 65 20 66 72 6f 6d 20 24 68 6f 73 74 6e  cate from $hostn
27f0: 61 6d 65 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20  ame"...}....set 
2800: 66 69 6c 65 20 5b 64 6f 77 6e 6c 6f 61 64 20 24  file [download $
2810: 68 6f 73 74 6e 61 6d 65 20 24 69 6e 64 65 78 68  hostname $indexh
2820: 61 73 68 5d 0a 09 09 63 61 74 63 68 20 7b 0a 09  ash]...catch {..
2830: 09 09 73 65 74 20 66 64 20 5b 6f 70 65 6e 20 24  ..set fd [open $
2840: 66 69 6c 65 5d 0a 09 09 7d 0a 0a 09 09 69 66 20  file]...}....if 
2850: 7b 21 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 66  {![info exists f
2860: 64 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  d]} {....return 
2870: 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 55 6e 61  -code error "Una
2880: 62 6c 65 20 74 6f 20 64 6f 77 6e 6c 6f 61 64 20  ble to download 
2890: 6f 72 20 6f 70 65 6e 20 24 66 69 6c 65 22 0a 09  or open $file"..
28a0: 09 7d 0a 0a 09 09 75 6e 73 65 74 20 2d 6e 6f 63  .}....unset -noc
28b0: 6f 6d 70 6c 61 69 6e 20 64 61 74 61 0a 09 09 63  omplain data...c
28c0: 61 74 63 68 20 7b 0a 09 09 09 73 65 74 20 64 61  atch {....set da
28d0: 74 61 20 5b 72 65 61 64 20 24 66 64 5d 0a 09 09  ta [read $fd]...
28e0: 7d 0a 0a 09 09 63 6c 6f 73 65 20 24 66 64 0a 0a  }....close $fd..
28f0: 09 09 73 65 74 20 63 75 72 72 5f 70 61 63 6b 61  ..set curr_packa
2900: 67 65 73 20 5b 6c 69 73 74 5d 0a 09 09 66 6f 72  ges [list]...for
2910: 65 61 63 68 20 6c 69 6e 65 20 5b 73 70 6c 69 74  each line [split
2920: 20 24 64 61 74 61 20 22 5c 6e 22 5d 20 7b 0a 09   $data "\n"] {..
2930: 09 09 73 65 74 20 6c 69 6e 65 20 5b 73 74 72 69  ..set line [stri
2940: 6e 67 20 74 72 69 6d 20 24 6c 69 6e 65 5d 0a 0a  ng trim $line]..
2950: 09 09 09 69 66 20 7b 5b 73 74 72 69 6e 67 20 6d  ...if {[string m
2960: 61 74 63 68 20 22 2a 2f 2a 22 20 24 6c 69 6e 65  atch "*/*" $line
2970: 5d 7d 20 7b 0a 09 09 09 09 63 6f 6e 74 69 6e 75  ]} {.....continu
2980: 65 0a 09 09 09 7d 0a 0a 09 09 09 69 66 20 7b 24  e....}.....if {$
2990: 6c 69 6e 65 20 3d 3d 20 22 22 7d 20 7b 0a 09 09  line == ""} {...
29a0: 09 09 63 6f 6e 74 69 6e 75 65 0a 09 09 09 7d 0a  ..continue....}.
29b0: 0a 09 09 09 73 65 74 20 77 6f 72 6b 20 5b 73 70  ....set work [sp
29c0: 6c 69 74 20 24 6c 69 6e 65 20 22 2c 22 5d 0a 0a  lit $line ","]..
29d0: 09 09 09 75 6e 73 65 74 20 2d 6e 6f 63 6f 6d 70  ...unset -nocomp
29e0: 6c 61 69 6e 20 70 6b 67 49 6e 66 6f 0a 09 09 09  lain pkgInfo....
29f0: 69 66 20 7b 5b 63 61 74 63 68 20 7b 0a 09 09 09  if {[catch {....
2a00: 09 73 65 74 20 70 6b 67 49 6e 66 6f 28 70 61 63  .set pkgInfo(pac
2a10: 6b 61 67 65 29 20 20 5b 6c 69 6e 64 65 78 20 24  kage)  [lindex $
2a20: 77 6f 72 6b 20 30 5d 0a 09 09 09 09 73 65 74 20  work 0].....set 
2a30: 70 6b 67 49 6e 66 6f 28 76 65 72 73 69 6f 6e 29  pkgInfo(version)
2a40: 20 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20    [lindex $work 
2a50: 31 5d 0a 09 09 09 09 73 65 74 20 70 6b 67 49 6e  1].....set pkgIn
2a60: 66 6f 28 6f 73 29 20 20 20 20 20 20 20 5b 5f 6e  fo(os)       [_n
2a70: 6f 72 6d 61 6c 69 7a 65 4f 53 20 5b 6c 69 6e 64  ormalizeOS [lind
2a80: 65 78 20 24 77 6f 72 6b 20 32 5d 5d 0a 09 09 09  ex $work 2]]....
2a90: 09 73 65 74 20 70 6b 67 49 6e 66 6f 28 63 70 75  .set pkgInfo(cpu
2aa0: 41 72 63 68 29 20 20 5b 5f 6e 6f 72 6d 61 6c 69  Arch)  [_normali
2ab0: 7a 65 43 50 55 20 5b 6c 69 6e 64 65 78 20 24 77  zeCPU [lindex $w
2ac0: 6f 72 6b 20 33 5d 5d 0a 09 09 09 09 73 65 74 20  ork 3]].....set 
2ad0: 70 6b 67 49 6e 66 6f 28 68 61 73 68 29 20 20 20  pkgInfo(hash)   
2ae0: 20 20 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77 65    [string tolowe
2af0: 72 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20  r [lindex $work 
2b00: 34 5d 5d 0a 09 09 09 09 73 65 74 20 70 6b 67 49  4]].....set pkgI
2b10: 6e 66 6f 28 68 61 73 68 5f 74 79 70 65 29 20 22  nfo(hash_type) "
2b20: 73 68 61 31 22 0a 09 09 09 09 73 65 74 20 70 6b  sha1".....set pk
2b30: 67 49 6e 66 6f 28 69 73 4c 61 74 65 73 74 29 20  gInfo(isLatest) 
2b40: 5b 65 78 70 72 20 7b 21 21 5b 6c 69 6e 64 65 78  [expr {!![lindex
2b50: 20 24 77 6f 72 6b 20 35 5d 7d 5d 0a 09 09 09 7d   $work 5]}]....}
2b60: 5d 7d 20 7b 0a 09 09 09 09 63 6f 6e 74 69 6e 75  ]} {.....continu
2b70: 65 0a 09 09 09 7d 0a 0a 09 09 09 69 66 20 7b 21  e....}.....if {!
2b80: 5b 5f 69 73 48 61 73 68 20 24 70 6b 67 49 6e 66  [_isHash $pkgInf
2b90: 6f 28 68 61 73 68 29 5d 7d 20 7b 0a 09 09 09 09  o(hash)]} {.....
2ba0: 63 6f 6e 74 69 6e 75 65 0a 09 09 09 7d 0a 0a 09  continue....}...
2bb0: 09 09 6c 61 70 70 65 6e 64 20 63 75 72 72 5f 70  ..lappend curr_p
2bc0: 61 63 6b 61 67 65 73 20 24 70 6b 67 49 6e 66 6f  ackages $pkgInfo
2bd0: 28 68 61 73 68 29 0a 0a 09 09 09 23 20 44 6f 20  (hash).....# Do 
2be0: 6e 6f 74 20 64 6f 20 61 6e 79 20 61 64 64 69 74  not do any addit
2bf0: 69 6f 6e 61 6c 20 77 6f 72 6b 20 69 66 20 77 65  ional work if we
2c00: 20 61 6c 72 65 61 64 79 20 68 61 76 65 20 74 68   already have th
2c10: 69 73 20 70 61 63 6b 61 67 65 0a 09 09 09 73 65  is package....se
2c20: 74 20 65 78 69 73 74 69 6e 67 5f 70 61 63 6b 61  t existing_packa
2c30: 67 65 73 20 5b 64 62 20 65 76 61 6c 20 7b 53 45  ges [db eval {SE
2c40: 4c 45 43 54 20 70 61 63 6b 61 67 65 20 46 52 4f  LECT package FRO
2c50: 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45 52 45  M packages WHERE
2c60: 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 68 6f 73   hostname = $hos
2c70: 74 6e 61 6d 65 20 41 4e 44 20 73 68 61 31 20 3d  tname AND sha1 =
2c80: 20 24 70 6b 67 49 6e 66 6f 28 68 61 73 68 29 3b   $pkgInfo(hash);
2c90: 7d 5d 0a 09 09 09 69 66 20 7b 5b 6c 73 65 61 72  }]....if {[lsear
2ca0: 63 68 20 2d 65 78 61 63 74 20 24 65 78 69 73 74  ch -exact $exist
2cb0: 69 6e 67 5f 70 61 63 6b 61 67 65 73 20 24 70 6b  ing_packages $pk
2cc0: 67 49 6e 66 6f 28 70 61 63 6b 61 67 65 29 5d 20  gInfo(package)] 
2cd0: 21 3d 20 2d 31 7d 20 7b 0a 09 09 09 09 63 6f 6e  != -1} {.....con
2ce0: 74 69 6e 75 65 0a 09 09 09 7d 0a 0a 09 09 09 69  tinue....}.....i
2cf0: 66 20 7b 24 70 6b 67 49 6e 66 6f 28 69 73 4c 61  f {$pkgInfo(isLa
2d00: 74 65 73 74 29 7d 20 7b 0a 09 09 09 09 64 62 20  test)} {.....db 
2d10: 65 76 61 6c 20 7b 55 50 44 41 54 45 20 70 61 63  eval {UPDATE pac
2d20: 6b 61 67 65 73 20 53 45 54 20 69 73 4c 61 74 65  kages SET isLate
2d30: 73 74 20 3d 20 30 20 57 48 45 52 45 20 68 6f 73  st = 0 WHERE hos
2d40: 74 6e 61 6d 65 20 3d 20 24 68 6f 73 74 6e 61 6d  tname = $hostnam
2d50: 65 20 41 4e 44 20 70 61 63 6b 61 67 65 20 3d 20  e AND package = 
2d60: 24 70 6b 67 49 6e 66 6f 28 70 61 63 6b 61 67 65  $pkgInfo(package
2d70: 29 20 41 4e 44 20 6f 73 20 3d 20 24 70 6b 67 49  ) AND os = $pkgI
2d80: 6e 66 6f 28 6f 73 29 20 41 4e 44 20 63 70 75 41  nfo(os) AND cpuA
2d90: 72 63 68 20 3d 20 24 70 6b 67 49 6e 66 6f 28 63  rch = $pkgInfo(c
2da0: 70 75 41 72 63 68 29 3b 7d 0a 09 09 09 7d 0a 0a  puArch);}....}..
2db0: 09 09 09 64 62 20 65 76 61 6c 20 7b 49 4e 53 45  ...db eval {INSE
2dc0: 52 54 20 49 4e 54 4f 20 70 61 63 6b 61 67 65 73  RT INTO packages
2dd0: 20 28 68 6f 73 74 6e 61 6d 65 2c 20 73 68 61 31   (hostname, sha1
2de0: 2c 20 70 61 63 6b 61 67 65 2c 20 76 65 72 73 69  , package, versi
2df0: 6f 6e 2c 20 6f 73 2c 20 63 70 75 41 72 63 68 2c  on, os, cpuArch,
2e00: 20 69 73 4c 61 74 65 73 74 2c 20 68 61 76 65 4d   isLatest, haveM
2e10: 61 6e 69 66 65 73 74 29 20 56 41 4c 55 45 53 20  anifest) VALUES 
2e20: 28 24 68 6f 73 74 6e 61 6d 65 2c 20 24 70 6b 67  ($hostname, $pkg
2e30: 49 6e 66 6f 28 68 61 73 68 29 2c 20 24 70 6b 67  Info(hash), $pkg
2e40: 49 6e 66 6f 28 70 61 63 6b 61 67 65 29 2c 20 24  Info(package), $
2e50: 70 6b 67 49 6e 66 6f 28 76 65 72 73 69 6f 6e 29  pkgInfo(version)
2e60: 2c 20 24 70 6b 67 49 6e 66 6f 28 6f 73 29 2c 20  , $pkgInfo(os), 
2e70: 24 70 6b 67 49 6e 66 6f 28 63 70 75 41 72 63 68  $pkgInfo(cpuArch
2e80: 29 2c 20 24 70 6b 67 49 6e 66 6f 28 69 73 4c 61  ), $pkgInfo(isLa
2e90: 74 65 73 74 29 2c 20 30 29 3b 7d 0a 09 09 7d 0a  test), 0);}...}.
2ea0: 0a 09 09 23 20 4c 6f 6f 6b 20 66 6f 72 20 70 61  ...# Look for pa
2eb0: 63 6b 61 67 65 73 20 74 68 61 74 20 68 61 76 65  ckages that have
2ec0: 20 62 65 65 6e 20 64 65 6c 65 74 65 64 0a 09 09   been deleted...
2ed0: 73 65 74 20 66 6f 75 6e 64 5f 70 61 63 6b 61 67  set found_packag
2ee0: 65 73 20 5b 64 62 20 65 76 61 6c 20 7b 53 45 4c  es [db eval {SEL
2ef0: 45 43 54 20 73 68 61 31 20 46 52 4f 4d 20 70 61  ECT sha1 FROM pa
2f00: 63 6b 61 67 65 73 20 57 48 45 52 45 20 68 6f 73  ckages WHERE hos
2f10: 74 6e 61 6d 65 20 3d 20 24 68 6f 73 74 6e 61 6d  tname = $hostnam
2f20: 65 3b 7d 5d 0a 09 09 66 6f 72 65 61 63 68 20 70  e;}]...foreach p
2f30: 61 63 6b 61 67 65 20 24 66 6f 75 6e 64 5f 70 61  ackage $found_pa
2f40: 63 6b 61 67 65 73 20 7b 0a 09 09 09 73 65 74 20  ckages {....set 
2f50: 66 6f 75 6e 64 5f 70 61 63 6b 61 67 65 73 5f 61  found_packages_a
2f60: 72 72 28 24 70 61 63 6b 61 67 65 29 20 31 0a 09  rr($package) 1..
2f70: 09 7d 0a 0a 09 09 66 6f 72 65 61 63 68 20 70 61  .}....foreach pa
2f80: 63 6b 61 67 65 20 24 63 75 72 72 5f 70 61 63 6b  ckage $curr_pack
2f90: 61 67 65 73 20 7b 0a 09 09 09 75 6e 73 65 74 20  ages {....unset 
2fa0: 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 66 6f 75 6e  -nocomplain foun
2fb0: 64 5f 70 61 63 6b 61 67 65 73 5f 61 72 72 28 24  d_packages_arr($
2fc0: 70 61 63 6b 61 67 65 29 0a 09 09 7d 0a 0a 09 09  package)...}....
2fd0: 66 6f 72 65 61 63 68 20 70 61 63 6b 61 67 65 20  foreach package 
2fe0: 5b 61 72 72 61 79 20 6e 61 6d 65 73 20 66 6f 75  [array names fou
2ff0: 6e 64 5f 70 61 63 6b 61 67 65 73 5f 61 72 72 5d  nd_packages_arr]
3000: 20 7b 0a 09 09 09 64 62 20 65 76 61 6c 20 7b 44   {....db eval {D
3010: 45 4c 45 54 45 20 46 52 4f 4d 20 70 61 63 6b 61  ELETE FROM packa
3020: 67 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61  ges WHERE hostna
3030: 6d 65 20 3d 20 24 68 6f 73 74 6e 61 6d 65 20 41  me = $hostname A
3040: 4e 44 20 73 68 61 31 20 3d 20 24 70 61 63 6b 61  ND sha1 = $packa
3050: 67 65 3b 7d 0a 09 09 7d 0a 0a 09 09 64 62 20 65  ge;}...}....db e
3060: 76 61 6c 20 7b 49 4e 53 45 52 54 20 4f 52 20 52  val {INSERT OR R
3070: 45 50 4c 41 43 45 20 49 4e 54 4f 20 73 69 74 65  EPLACE INTO site
3080: 73 20 28 68 6f 73 74 6e 61 6d 65 2c 20 6c 61 73  s (hostname, las
3090: 74 55 70 64 61 74 65 2c 20 74 74 6c 29 20 56 41  tUpdate, ttl) VA
30a0: 4c 55 45 53 20 28 24 68 6f 73 74 6e 61 6d 65 2c  LUES ($hostname,
30b0: 20 24 6e 6f 77 2c 20 24 3a 3a 61 70 70 66 73 3a   $now, $::appfs:
30c0: 3a 74 74 6c 29 3b 7d 0a 0a 09 09 61 70 70 66 73  :ttl);}....appfs
30d0: 64 3a 3a 67 65 74 5f 70 61 74 68 5f 69 6e 66 6f  d::get_path_info
30e0: 5f 63 61 63 68 65 5f 66 6c 75 73 68 0a 0a 09 09  _cache_flush....
30f0: 72 65 74 75 72 6e 20 43 4f 4d 50 4c 45 54 45 0a  return COMPLETE.
3100: 09 7d 0a 0a 09 70 72 6f 63 20 67 65 74 70 6b 67  .}...proc getpkg
3110: 6d 61 6e 69 66 65 73 74 20 7b 68 6f 73 74 6e 61  manifest {hostna
3120: 6d 65 20 70 61 63 6b 61 67 65 5f 73 68 61 31 7d  me package_sha1}
3130: 20 7b 0a 09 09 73 65 74 20 68 61 76 65 4d 61 6e   {...set haveMan
3140: 69 66 65 73 74 20 5b 64 62 20 6f 6e 65 63 6f 6c  ifest [db onecol
3150: 75 6d 6e 20 7b 53 45 4c 45 43 54 20 68 61 76 65  umn {SELECT have
3160: 4d 61 6e 69 66 65 73 74 20 46 52 4f 4d 20 70 61  Manifest FROM pa
3170: 63 6b 61 67 65 73 20 57 48 45 52 45 20 73 68 61  ckages WHERE sha
3180: 31 20 3d 20 24 70 61 63 6b 61 67 65 5f 73 68 61  1 = $package_sha
3190: 31 20 4c 49 4d 49 54 20 31 3b 7d 5d 0a 0a 09 09  1 LIMIT 1;}]....
31a0: 69 66 20 7b 24 68 61 76 65 4d 61 6e 69 66 65 73  if {$haveManifes
31b0: 74 20 3d 3d 20 22 31 22 7d 20 7b 0a 09 09 09 72  t == "1"} {....r
31c0: 65 74 75 72 6e 20 43 4f 4d 50 4c 45 54 45 0a 09  eturn COMPLETE..
31d0: 09 7d 0a 0a 09 09 69 66 20 7b 21 5b 5f 69 73 48  .}....if {![_isH
31e0: 61 73 68 20 24 70 61 63 6b 61 67 65 5f 73 68 61  ash $package_sha
31f0: 31 5d 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e 20  1]} {....return 
3200: 46 41 49 4c 0a 09 09 7d 0a 0a 09 09 73 65 74 20  FAIL...}....set 
3210: 66 69 6c 65 20 5b 64 6f 77 6e 6c 6f 61 64 20 24  file [download $
3220: 68 6f 73 74 6e 61 6d 65 20 24 70 61 63 6b 61 67  hostname $packag
3230: 65 5f 73 68 61 31 5d 0a 0a 09 09 63 61 74 63 68  e_sha1]....catch
3240: 20 7b 0a 09 09 09 73 65 74 20 66 64 20 5b 6f 70   {....set fd [op
3250: 65 6e 20 24 66 69 6c 65 5d 0a 09 09 7d 0a 0a 09  en $file]...}...
3260: 09 69 66 20 7b 21 5b 69 6e 66 6f 20 65 78 69 73  .if {![info exis
3270: 74 73 20 66 64 5d 7d 20 7b 0a 09 09 09 72 65 74  ts fd]} {....ret
3280: 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20  urn -code error 
3290: 22 55 6e 61 62 6c 65 20 74 6f 20 64 6f 77 6e 6c  "Unable to downl
32a0: 6f 61 64 20 6f 72 20 6f 70 65 6e 20 24 66 69 6c  oad or open $fil
32b0: 65 22 0a 09 09 7d 0a 0a 09 09 63 61 74 63 68 20  e"...}....catch 
32c0: 7b 0a 09 09 09 73 65 74 20 70 6b 67 64 61 74 61  {....set pkgdata
32d0: 20 5b 72 65 61 64 20 24 66 64 5d 0a 09 09 7d 0a   [read $fd]...}.
32e0: 0a 09 09 63 6c 6f 73 65 20 24 66 64 0a 0a 09 09  ...close $fd....
32f0: 64 62 20 74 72 61 6e 73 61 63 74 69 6f 6e 20 7b  db transaction {
3300: 0a 09 09 09 66 6f 72 65 61 63 68 20 6c 69 6e 65  ....foreach line
3310: 20 5b 73 70 6c 69 74 20 24 70 6b 67 64 61 74 61   [split $pkgdata
3320: 20 22 5c 6e 22 5d 20 7b 0a 09 09 09 09 73 65 74   "\n"] {.....set
3330: 20 6c 69 6e 65 20 5b 73 74 72 69 6e 67 20 74 72   line [string tr
3340: 69 6d 20 24 6c 69 6e 65 5d 0a 0a 09 09 09 09 69  im $line]......i
3350: 66 20 7b 24 6c 69 6e 65 20 3d 3d 20 22 22 7d 20  f {$line == ""} 
3360: 7b 0a 09 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a  {......continue.
3370: 09 09 09 09 7d 0a 0a 09 09 09 09 73 65 74 20 77  ....}......set w
3380: 6f 72 6b 20 5b 73 70 6c 69 74 20 24 6c 69 6e 65  ork [split $line
3390: 20 22 2c 22 5d 0a 0a 09 09 09 09 75 6e 73 65 74   ","]......unset
33a0: 20 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 66 69 6c   -nocomplain fil
33b0: 65 49 6e 66 6f 0a 09 09 09 09 73 65 74 20 66 69  eInfo.....set fi
33c0: 6c 65 49 6e 66 6f 28 74 79 70 65 29 20 5b 6c 69  leInfo(type) [li
33d0: 6e 64 65 78 20 24 77 6f 72 6b 20 30 5d 0a 09 09  ndex $work 0]...
33e0: 09 09 73 65 74 20 66 69 6c 65 49 6e 66 6f 28 74  ..set fileInfo(t
33f0: 69 6d 65 29 20 5b 6c 69 6e 64 65 78 20 24 77 6f  ime) [lindex $wo
3400: 72 6b 20 31 5d 0a 0a 09 09 09 09 73 65 74 20 77  rk 1]......set w
3410: 6f 72 6b 20 5b 6c 72 61 6e 67 65 20 24 77 6f 72  ork [lrange $wor
3420: 6b 20 32 20 65 6e 64 5d 0a 09 09 09 09 73 77 69  k 2 end].....swi
3430: 74 63 68 20 2d 2d 20 24 66 69 6c 65 49 6e 66 6f  tch -- $fileInfo
3440: 28 74 79 70 65 29 20 7b 0a 09 09 09 09 09 22 23  (type) {......"#
3450: 6d 61 6e 69 66 65 73 74 6d 65 74 61 64 61 74 61  manifestmetadata
3460: 22 20 7b 0a 09 09 09 09 09 09 75 6e 73 65 74 20  " {.......unset 
3470: 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 66 69 6c 65  -nocomplain file
3480: 49 6e 66 6f 0a 09 09 09 09 09 09 63 6f 6e 74 69  Info.......conti
3490: 6e 75 65 0a 09 09 09 09 09 7d 0a 09 09 09 09 09  nue......}......
34a0: 22 66 69 6c 65 22 20 7b 0a 09 09 09 09 09 09 73  "file" {.......s
34b0: 65 74 20 66 69 6c 65 49 6e 66 6f 28 73 69 7a 65  et fileInfo(size
34c0: 29 20 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20  ) [lindex $work 
34d0: 30 5d 0a 0a 09 09 09 09 09 09 23 20 57 65 20 6c  0]........# We l
34e0: 6f 77 65 72 2d 63 61 73 65 20 74 68 65 20 70 65  ower-case the pe
34f0: 72 6d 69 73 73 69 6f 6e 73 20 62 65 63 61 75 73  rmissions becaus
3500: 65 20 75 70 70 65 72 2d 63 61 73 65 20 70 65 72  e upper-case per
3510: 6d 69 73 73 69 6f 6e 73 0a 09 09 09 09 09 09 23  missions.......#
3520: 20 73 68 6f 75 6c 64 20 6e 6f 74 20 62 65 20 73   should not be s
3530: 65 74 20 72 65 6d 6f 74 65 6c 79 20 61 73 20 74  et remotely as t
3540: 68 65 79 20 6d 61 79 20 69 6e 66 6c 75 65 6e 63  hey may influenc
3550: 65 20 74 68 65 20 73 65 63 75 72 69 74 79 0a 09  e the security..
3560: 09 09 09 09 09 23 20 6f 66 20 74 68 65 20 73 79  .....# of the sy
3570: 73 74 65 6d 2e 0a 09 09 09 09 09 09 73 65 74 20  stem........set 
3580: 66 69 6c 65 49 6e 66 6f 28 70 65 72 6d 73 29 20  fileInfo(perms) 
3590: 5b 73 74 72 69 6e 67 20 74 6f 6c 6f 77 65 72 20  [string tolower 
35a0: 5b 6c 69 6e 64 65 78 20 24 77 6f 72 6b 20 31 5d  [lindex $work 1]
35b0: 5d 0a 0a 09 09 09 09 09 09 73 65 74 20 66 69 6c  ]........set fil
35c0: 65 49 6e 66 6f 28 73 68 61 31 29 20 5b 6c 69 6e  eInfo(sha1) [lin
35d0: 64 65 78 20 24 77 6f 72 6b 20 32 5d 0a 0a 09 09  dex $work 2]....
35e0: 09 09 09 09 73 65 74 20 77 6f 72 6b 20 5b 6c 72  ....set work [lr
35f0: 61 6e 67 65 20 24 77 6f 72 6b 20 33 20 65 6e 64  ange $work 3 end
3600: 5d 0a 09 09 09 09 09 7d 0a 09 09 09 09 09 22 73  ]......}......"s
3610: 79 6d 6c 69 6e 6b 22 20 7b 0a 09 09 09 09 09 09  ymlink" {.......
3620: 73 65 74 20 66 69 6c 65 49 6e 66 6f 28 73 6f 75  set fileInfo(sou
3630: 72 63 65 29 20 5b 6c 69 6e 64 65 78 20 24 77 6f  rce) [lindex $wo
3640: 72 6b 20 30 5d 0a 09 09 09 09 09 09 73 65 74 20  rk 0].......set 
3650: 77 6f 72 6b 20 5b 6c 72 61 6e 67 65 20 24 77 6f  work [lrange $wo
3660: 72 6b 20 31 20 65 6e 64 5d 0a 09 09 09 09 09 7d  rk 1 end]......}
3670: 0a 09 09 09 09 09 22 64 69 72 65 63 74 6f 72 79  ......"directory
3680: 22 20 7b 0a 09 09 09 09 09 09 23 20 4e 6f 20 65  " {.......# No e
3690: 78 74 72 61 20 64 61 74 61 20 72 65 71 75 69 72  xtra data requir
36a0: 65 64 0a 09 09 09 09 09 7d 0a 09 09 09 09 09 64  ed......}......d
36b0: 65 66 61 75 6c 74 20 7b 0a 09 09 09 09 09 09 23  efault {.......#
36c0: 20 48 61 6e 64 6c 65 20 75 6e 6b 6e 6f 77 6e 20   Handle unknown 
36d0: 74 79 70 65 73 0a 09 09 09 09 09 09 69 66 20 7b  types.......if {
36e0: 5b 73 74 72 69 6e 67 20 69 6e 64 65 78 20 24 66  [string index $f
36f0: 69 6c 65 49 6e 66 6f 28 74 79 70 65 29 20 30 5d  ileInfo(type) 0]
3700: 20 3d 3d 20 22 23 22 7d 20 7b 0a 09 09 09 09 09   == "#"} {......
3710: 09 09 23 20 4d 65 74 61 64 61 74 61 20 74 79 70  ..# Metadata typ
3720: 65 2c 20 69 67 6e 6f 72 65 0a 09 09 09 09 09 09  e, ignore.......
3730: 09 23 20 69 74 20 69 66 20 77 65 20 64 6f 6e 27  .# it if we don'
3740: 74 0a 09 09 09 09 09 09 09 23 20 75 6e 64 65 72  t........# under
3750: 73 74 61 6e 64 20 74 68 69 73 20 74 79 70 65 0a  stand this type.
3760: 09 09 09 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a  .......continue.
3770: 09 09 09 09 09 09 7d 20 65 6c 73 65 20 7b 0a 09  ......} else {..
3780: 09 09 09 09 09 09 23 20 55 6e 6b 6e 6f 77 6e 20  ......# Unknown 
3790: 74 79 70 65 2c 0a 09 09 09 09 09 09 09 23 20 67  type,........# g
37a0: 65 6e 65 72 61 74 65 20 61 6e 20 65 72 72 6f 72  enerate an error
37b0: 0a 09 09 09 09 09 09 09 65 72 72 6f 72 20 22 4d  ........error "M
37c0: 61 6e 69 66 65 73 74 20 63 61 6e 6e 6f 74 20 62  anifest cannot b
37d0: 65 20 70 61 72 73 65 64 22 0a 09 09 09 09 09 09  e parsed".......
37e0: 7d 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 0a 0a  }......}.....}..
37f0: 09 09 09 09 73 65 74 20 66 69 6c 65 49 6e 66 6f  ....set fileInfo
3800: 28 6e 61 6d 65 29 20 5b 6a 6f 69 6e 20 24 77 6f  (name) [join $wo
3810: 72 6b 20 22 2c 22 5d 0a 09 09 09 09 73 65 74 20  rk ","].....set 
3820: 66 69 6c 65 49 6e 66 6f 28 6e 61 6d 65 29 20 5b  fileInfo(name) [
3830: 73 70 6c 69 74 20 5b 73 74 72 69 6e 67 20 74 72  split [string tr
3840: 69 6d 20 24 66 69 6c 65 49 6e 66 6f 28 6e 61 6d  im $fileInfo(nam
3850: 65 29 20 22 2f 22 5d 20 22 2f 22 5d 0a 09 09 09  e) "/"] "/"]....
3860: 09 73 65 74 20 66 69 6c 65 49 6e 66 6f 28 64 69  .set fileInfo(di
3870: 72 65 63 74 6f 72 79 29 20 5b 6a 6f 69 6e 20 5b  rectory) [join [
3880: 6c 72 61 6e 67 65 20 24 66 69 6c 65 49 6e 66 6f  lrange $fileInfo
3890: 28 6e 61 6d 65 29 20 30 20 65 6e 64 2d 31 5d 20  (name) 0 end-1] 
38a0: 22 2f 22 5d 0a 09 09 09 09 73 65 74 20 66 69 6c  "/"].....set fil
38b0: 65 49 6e 66 6f 28 6e 61 6d 65 29 20 5b 6c 69 6e  eInfo(name) [lin
38c0: 64 65 78 20 24 66 69 6c 65 49 6e 66 6f 28 6e 61  dex $fileInfo(na
38d0: 6d 65 29 20 65 6e 64 5d 0a 0a 09 09 09 09 64 62  me) end]......db
38e0: 20 65 76 61 6c 20 7b 49 4e 53 45 52 54 20 49 4e   eval {INSERT IN
38f0: 54 4f 20 66 69 6c 65 73 20 28 70 61 63 6b 61 67  TO files (packag
3900: 65 5f 73 68 61 31 2c 20 74 79 70 65 2c 20 74 69  e_sha1, type, ti
3910: 6d 65 2c 20 73 6f 75 72 63 65 2c 20 73 69 7a 65  me, source, size
3920: 2c 20 70 65 72 6d 73 2c 20 66 69 6c 65 5f 73 68  , perms, file_sh
3930: 61 31 2c 20 66 69 6c 65 5f 6e 61 6d 65 2c 20 66  a1, file_name, f
3940: 69 6c 65 5f 64 69 72 65 63 74 6f 72 79 29 20 56  ile_directory) V
3950: 41 4c 55 45 53 20 28 24 70 61 63 6b 61 67 65 5f  ALUES ($package_
3960: 73 68 61 31 2c 20 24 66 69 6c 65 49 6e 66 6f 28  sha1, $fileInfo(
3970: 74 79 70 65 29 2c 20 24 66 69 6c 65 49 6e 66 6f  type), $fileInfo
3980: 28 74 69 6d 65 29 2c 20 24 66 69 6c 65 49 6e 66  (time), $fileInf
3990: 6f 28 73 6f 75 72 63 65 29 2c 20 24 66 69 6c 65  o(source), $file
39a0: 49 6e 66 6f 28 73 69 7a 65 29 2c 20 24 66 69 6c  Info(size), $fil
39b0: 65 49 6e 66 6f 28 70 65 72 6d 73 29 2c 20 24 66  eInfo(perms), $f
39c0: 69 6c 65 49 6e 66 6f 28 73 68 61 31 29 2c 20 24  ileInfo(sha1), $
39d0: 66 69 6c 65 49 6e 66 6f 28 6e 61 6d 65 29 2c 20  fileInfo(name), 
39e0: 24 66 69 6c 65 49 6e 66 6f 28 64 69 72 65 63 74  $fileInfo(direct
39f0: 6f 72 79 29 20 29 3b 7d 0a 09 09 09 09 64 62 20  ory) );}.....db 
3a00: 65 76 61 6c 20 7b 55 50 44 41 54 45 20 70 61 63  eval {UPDATE pac
3a10: 6b 61 67 65 73 20 53 45 54 20 68 61 76 65 4d 61  kages SET haveMa
3a20: 6e 69 66 65 73 74 20 3d 20 31 20 57 48 45 52 45  nifest = 1 WHERE
3a30: 20 73 68 61 31 20 3d 20 24 70 61 63 6b 61 67 65   sha1 = $package
3a40: 5f 73 68 61 31 3b 7d 0a 09 09 09 7d 0a 09 09 7d  _sha1;}....}...}
3a50: 0a 0a 09 09 61 70 70 66 73 64 3a 3a 67 65 74 5f  ....appfsd::get_
3a60: 70 61 74 68 5f 69 6e 66 6f 5f 63 61 63 68 65 5f  path_info_cache_
3a70: 66 6c 75 73 68 0a 0a 09 09 72 65 74 75 72 6e 20  flush....return 
3a80: 43 4f 4d 50 4c 45 54 45 0a 09 7d 0a 0a 09 70 72  COMPLETE..}...pr
3a90: 6f 63 20 5f 6c 6f 63 61 6c 70 61 74 68 20 7b 70  oc _localpath {p
3aa0: 61 63 6b 61 67 65 20 68 6f 73 74 6e 61 6d 65 20  ackage hostname 
3ab0: 66 69 6c 65 7d 20 7b 0a 09 09 73 65 74 20 64 69  file} {...set di
3ac0: 72 20 22 22 0a 09 09 63 61 74 63 68 20 7b 0a 09  r ""...catch {..
3ad0: 09 09 73 65 74 20 68 6f 6d 65 64 69 72 20 5b 3a  ..set homedir [:
3ae0: 3a 61 70 70 66 73 3a 3a 75 73 65 72 3a 3a 67 65  :appfs::user::ge
3af0: 74 5f 68 6f 6d 65 64 69 72 5d 0a 09 09 09 73 65  t_homedir]....se
3b00: 74 20 64 69 72 20 5b 66 69 6c 65 20 6a 6f 69 6e  t dir [file join
3b10: 20 24 68 6f 6d 65 64 69 72 20 2e 61 70 70 66 73   $homedir .appfs
3b20: 20 22 2e 2f 24 7b 70 61 63 6b 61 67 65 7d 40 24   "./${package}@$
3b30: 7b 68 6f 73 74 6e 61 6d 65 7d 22 20 22 2e 2f 24  {hostname}" "./$
3b40: 7b 66 69 6c 65 7d 22 5d 0a 09 09 7d 0a 09 09 72  {file}"]...}...r
3b50: 65 74 75 72 6e 20 24 64 69 72 0a 09 7d 0a 0a 09  eturn $dir..}...
3b60: 70 72 6f 63 20 5f 77 68 69 74 65 6f 75 74 70 61  proc _whiteoutpa
3b70: 74 68 20 7b 70 61 63 6b 61 67 65 20 68 6f 73 74  th {package host
3b80: 6e 61 6d 65 20 66 69 6c 65 7d 20 7b 0a 09 09 73  name file} {...s
3b90: 65 74 20 64 69 72 20 22 22 0a 09 09 63 61 74 63  et dir ""...catc
3ba0: 68 20 7b 0a 09 09 09 73 65 74 20 68 6f 6d 65 64  h {....set homed
3bb0: 69 72 20 5b 3a 3a 61 70 70 66 73 3a 3a 75 73 65  ir [::appfs::use
3bc0: 72 3a 3a 67 65 74 5f 68 6f 6d 65 64 69 72 5d 0a  r::get_homedir].
3bd0: 09 09 09 73 65 74 20 64 69 72 20 5b 66 69 6c 65  ...set dir [file
3be0: 20 6a 6f 69 6e 20 24 68 6f 6d 65 64 69 72 20 2e   join $homedir .
3bf0: 61 70 70 66 73 20 22 2e 2f 24 7b 70 61 63 6b 61  appfs "./${packa
3c00: 67 65 7d 40 24 7b 68 6f 73 74 6e 61 6d 65 7d 22  ge}@${hostname}"
3c10: 20 22 2e 41 50 50 46 53 2e 57 48 49 54 45 4f 55   ".APPFS.WHITEOU
3c20: 54 22 20 22 2e 2f 24 7b 66 69 6c 65 7d 2e 41 50  T" "./${file}.AP
3c30: 50 46 53 2e 57 48 49 54 45 4f 55 54 22 5d 0a 09  PFS.WHITEOUT"]..
3c40: 09 7d 0a 09 09 72 65 74 75 72 6e 20 24 64 69 72  .}...return $dir
3c50: 0a 09 7d 0a 0a 09 70 72 6f 63 20 5f 70 61 72 73  ..}...proc _pars
3c60: 65 70 61 74 68 20 7b 70 61 74 68 7d 20 7b 0a 09  epath {path} {..
3c70: 09 73 65 74 20 70 61 74 68 20 5b 73 74 72 69 6e  .set path [strin
3c80: 67 20 74 72 69 6d 20 24 70 61 74 68 20 22 2f 22  g trim $path "/"
3c90: 5d 0a 09 09 73 65 74 20 70 61 74 68 20 5b 73 70  ]...set path [sp
3ca0: 6c 69 74 20 24 70 61 74 68 20 22 2f 22 5d 0a 09  lit $path "/"]..
3cb0: 09 73 65 74 20 70 61 74 68 6c 65 6e 20 5b 6c 6c  .set pathlen [ll
3cc0: 65 6e 67 74 68 20 24 70 61 74 68 5d 0a 0a 09 09  ength $path]....
3cd0: 61 72 72 61 79 20 73 65 74 20 72 65 74 76 61 6c  array set retval
3ce0: 20 5b 6c 69 73 74 20 5f 63 68 69 6c 64 72 65 6e   [list _children
3cf0: 20 73 69 74 65 73 20 5f 74 79 70 65 20 74 6f 70   sites _type top
3d00: 6c 65 76 65 6c 5d 0a 0a 09 09 69 66 20 7b 24 70  level]....if {$p
3d10: 61 74 68 6c 65 6e 20 3e 20 30 7d 20 7b 0a 09 09  athlen > 0} {...
3d20: 09 73 65 74 20 72 65 74 76 61 6c 28 68 6f 73 74  .set retval(host
3d30: 6e 61 6d 65 29 20 5b 6c 69 6e 64 65 78 20 24 70  name) [lindex $p
3d40: 61 74 68 20 30 5d 0a 09 09 09 73 65 74 20 72 65  ath 0]....set re
3d50: 74 76 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29 20  tval(_children) 
3d60: 70 61 63 6b 61 67 65 73 0a 09 09 09 73 65 74 20  packages....set 
3d70: 72 65 74 76 61 6c 28 5f 74 79 70 65 29 20 73 69  retval(_type) si
3d80: 74 65 73 0a 0a 09 09 09 69 66 20 7b 24 70 61 74  tes.....if {$pat
3d90: 68 6c 65 6e 20 3e 20 31 7d 20 7b 0a 09 09 09 09  hlen > 1} {.....
3da0: 73 65 74 20 70 61 63 6b 61 67 65 20 5b 6c 69 6e  set package [lin
3db0: 64 65 78 20 24 70 61 74 68 20 31 5d 0a 09 09 09  dex $path 1]....
3dc0: 09 69 66 20 7b 5b 73 74 72 69 6e 67 20 6c 65 6e  .if {[string len
3dd0: 67 74 68 20 24 70 61 63 6b 61 67 65 5d 20 3d 3d  gth $package] ==
3de0: 20 22 34 30 22 20 26 26 20 5b 72 65 67 65 78 70   "40" && [regexp
3df0: 20 7b 5e 5b 61 2d 66 41 2d 46 30 2d 39 5d 2a 24   {^[a-fA-F0-9]*$
3e00: 7d 20 24 70 61 63 6b 61 67 65 5d 7d 20 7b 0a 09  } $package]} {..
3e10: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 70  ....set retval(p
3e20: 61 63 6b 61 67 65 5f 73 68 61 31 29 20 24 70 61  ackage_sha1) $pa
3e30: 63 6b 61 67 65 0a 09 09 09 09 09 73 65 74 20 72  ckage......set r
3e40: 65 74 76 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29  etval(_children)
3e50: 20 66 69 6c 65 73 0a 09 09 09 09 09 73 65 74 20   files......set 
3e60: 72 65 74 76 61 6c 28 5f 74 79 70 65 29 20 66 69  retval(_type) fi
3e70: 6c 65 73 0a 0a 09 09 09 09 09 3a 3a 61 70 70 66  les.......::appf
3e80: 73 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45 4c 45  s::db eval {SELE
3e90: 43 54 20 70 61 63 6b 61 67 65 2c 20 6f 73 2c 20  CT package, os, 
3ea0: 63 70 75 41 72 63 68 2c 20 76 65 72 73 69 6f 6e  cpuArch, version
3eb0: 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57   FROM packages W
3ec0: 48 45 52 45 20 73 68 61 31 20 3d 20 24 72 65 74  HERE sha1 = $ret
3ed0: 76 61 6c 28 70 61 63 6b 61 67 65 5f 73 68 61 31  val(package_sha1
3ee0: 29 3b 7d 20 70 6b 67 69 6e 66 6f 20 7b 7d 0a 09  );} pkginfo {}..
3ef0: 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 70  ....set retval(p
3f00: 61 63 6b 61 67 65 29 20 24 70 6b 67 69 6e 66 6f  ackage) $pkginfo
3f10: 28 70 61 63 6b 61 67 65 29 0a 09 09 09 09 09 73  (package)......s
3f20: 65 74 20 72 65 74 76 61 6c 28 6f 73 29 20 24 70  et retval(os) $p
3f30: 6b 67 69 6e 66 6f 28 6f 73 29 0a 09 09 09 09 09  kginfo(os)......
3f40: 73 65 74 20 72 65 74 76 61 6c 28 63 70 75 29 20  set retval(cpu) 
3f50: 24 70 6b 67 69 6e 66 6f 28 63 70 75 41 72 63 68  $pkginfo(cpuArch
3f60: 29 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61  )......set retva
3f70: 6c 28 76 65 72 73 69 6f 6e 29 20 24 70 6b 67 69  l(version) $pkgi
3f80: 6e 66 6f 28 76 65 72 73 69 6f 6e 29 0a 0a 09 09  nfo(version)....
3f90: 09 09 09 69 66 20 7b 24 70 61 74 68 6c 65 6e 20  ...if {$pathlen 
3fa0: 3e 20 32 7d 20 7b 0a 09 09 09 09 09 09 73 65 74  > 2} {.......set
3fb0: 20 72 65 74 76 61 6c 28 66 69 6c 65 29 20 5b 6a   retval(file) [j
3fc0: 6f 69 6e 20 5b 6c 72 61 6e 67 65 20 24 70 61 74  oin [lrange $pat
3fd0: 68 20 32 20 65 6e 64 5d 20 22 2f 22 5d 0a 09 09  h 2 end] "/"]...
3fe0: 09 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09  ...} else {.....
3ff0: 09 09 73 65 74 20 72 65 74 76 61 6c 28 66 69 6c  ..set retval(fil
4000: 65 29 20 22 22 0a 09 09 09 09 09 7d 0a 09 09 09  e) ""......}....
4010: 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 09 73  .} else {......s
4020: 65 74 20 72 65 74 76 61 6c 28 70 61 63 6b 61 67  et retval(packag
4030: 65 29 20 24 70 61 63 6b 61 67 65 0a 09 09 09 09  e) $package.....
4040: 09 73 65 74 20 72 65 74 76 61 6c 28 5f 63 68 69  .set retval(_chi
4050: 6c 64 72 65 6e 29 20 6f 73 2d 63 70 75 0a 09 09  ldren) os-cpu...
4060: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 5f 74  ...set retval(_t
4070: 79 70 65 29 20 70 61 63 6b 61 67 65 73 0a 0a 09  ype) packages...
4080: 09 09 09 09 69 66 20 7b 24 70 61 74 68 6c 65 6e  ....if {$pathlen
4090: 20 3e 20 32 7d 20 7b 0a 09 09 09 09 09 09 73 65   > 2} {.......se
40a0: 74 20 6f 73 5f 63 70 75 20 5b 6c 69 6e 64 65 78  t os_cpu [lindex
40b0: 20 24 70 61 74 68 20 32 5d 0a 09 09 09 09 09 09   $path 2].......
40c0: 73 65 74 20 6f 73 5f 63 70 75 20 5b 73 70 6c 69  set os_cpu [spli
40d0: 74 20 24 6f 73 5f 63 70 75 20 22 2d 22 5d 0a 0a  t $os_cpu "-"]..
40e0: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
40f0: 28 6f 73 29 20 5b 5f 6e 6f 72 6d 61 6c 69 7a 65  (os) [_normalize
4100: 4f 53 20 5b 6c 69 6e 64 65 78 20 24 6f 73 5f 63  OS [lindex $os_c
4110: 70 75 20 30 5d 20 31 5d 0a 09 09 09 09 09 09 73  pu 0] 1].......s
4120: 65 74 20 72 65 74 76 61 6c 28 63 70 75 29 20 5b  et retval(cpu) [
4130: 5f 6e 6f 72 6d 61 6c 69 7a 65 43 50 55 20 5b 6c  _normalizeCPU [l
4140: 69 6e 64 65 78 20 24 6f 73 5f 63 70 75 20 31 5d  index $os_cpu 1]
4150: 20 31 5d 0a 09 09 09 09 09 09 73 65 74 20 72 65   1].......set re
4160: 74 76 61 6c 28 5f 63 68 69 6c 64 72 65 6e 29 20  tval(_children) 
4170: 76 65 72 73 69 6f 6e 73 0a 09 09 09 09 09 09 73  versions.......s
4180: 65 74 20 72 65 74 76 61 6c 28 5f 74 79 70 65 29  et retval(_type)
4190: 20 6f 73 2d 63 70 75 0a 0a 09 09 09 09 09 09 69   os-cpu........i
41a0: 66 20 7b 24 70 61 74 68 6c 65 6e 20 3e 20 33 7d  f {$pathlen > 3}
41b0: 20 7b 0a 09 09 09 09 09 09 09 73 65 74 20 72 65   {........set re
41c0: 74 76 61 6c 28 76 65 72 73 69 6f 6e 29 20 5b 6c  tval(version) [l
41d0: 69 6e 64 65 78 20 24 70 61 74 68 20 33 5d 0a 09  index $path 3]..
41e0: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
41f0: 28 5f 63 68 69 6c 64 72 65 6e 29 20 66 69 6c 65  (_children) file
4200: 73 0a 09 09 09 09 09 09 09 73 65 74 20 72 65 74  s........set ret
4210: 76 61 6c 28 5f 74 79 70 65 29 20 76 65 72 73 69  val(_type) versi
4220: 6f 6e 73 0a 0a 09 09 09 09 09 09 09 73 65 74 20  ons.........set 
4230: 72 65 74 76 61 6c 28 70 61 63 6b 61 67 65 5f 73  retval(package_s
4240: 68 61 31 29 20 5b 3a 3a 61 70 70 66 73 3a 3a 64  ha1) [::appfs::d
4250: 62 20 6f 6e 65 63 6f 6c 75 6d 6e 20 7b 53 45 4c  b onecolumn {SEL
4260: 45 43 54 20 73 68 61 31 20 46 52 4f 4d 20 70 61  ECT sha1 FROM pa
4270: 63 6b 61 67 65 73 20 57 48 45 52 45 20 68 6f 73  ckages WHERE hos
4280: 74 6e 61 6d 65 20 3d 20 24 72 65 74 76 61 6c 28  tname = $retval(
4290: 68 6f 73 74 6e 61 6d 65 29 20 41 4e 44 20 70 61  hostname) AND pa
42a0: 63 6b 61 67 65 20 3d 20 24 72 65 74 76 61 6c 28  ckage = $retval(
42b0: 70 61 63 6b 61 67 65 29 20 41 4e 44 20 6f 73 20  package) AND os 
42c0: 3d 20 24 72 65 74 76 61 6c 28 6f 73 29 20 41 4e  = $retval(os) AN
42d0: 44 20 63 70 75 41 72 63 68 20 3d 20 24 72 65 74  D cpuArch = $ret
42e0: 76 61 6c 28 63 70 75 29 20 41 4e 44 20 76 65 72  val(cpu) AND ver
42f0: 73 69 6f 6e 20 3d 20 24 72 65 74 76 61 6c 28 76  sion = $retval(v
4300: 65 72 73 69 6f 6e 29 3b 7d 5d 0a 09 09 09 09 09  ersion);}]......
4310: 09 09 69 66 20 7b 24 72 65 74 76 61 6c 28 70 61  ..if {$retval(pa
4320: 63 6b 61 67 65 5f 73 68 61 31 29 20 3d 3d 20 22  ckage_sha1) == "
4330: 22 7d 20 7b 0a 09 09 09 09 09 09 09 09 73 65 74  "} {.........set
4340: 20 72 65 74 76 61 6c 28 5f 63 68 69 6c 64 72 65   retval(_childre
4350: 6e 29 20 64 65 61 64 0a 09 09 09 09 09 09 09 09  n) dead.........
4360: 72 65 74 75 72 6e 20 5b 61 72 72 61 79 20 67 65  return [array ge
4370: 74 20 72 65 74 76 61 6c 5d 0a 09 09 09 09 09 09  t retval].......
4380: 09 7d 0a 0a 09 09 09 09 09 09 09 69 66 20 7b 24  .}.........if {$
4390: 70 61 74 68 6c 65 6e 20 3e 20 34 7d 20 7b 0a 09  pathlen > 4} {..
43a0: 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61  .......set retva
43b0: 6c 28 5f 74 79 70 65 29 20 66 69 6c 65 73 0a 09  l(_type) files..
43c0: 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61  .......set retva
43d0: 6c 28 66 69 6c 65 29 20 5b 6a 6f 69 6e 20 5b 6c  l(file) [join [l
43e0: 72 61 6e 67 65 20 24 70 61 74 68 20 34 20 65 6e  range $path 4 en
43f0: 64 5d 20 22 2f 22 5d 0a 09 09 09 09 09 09 09 7d  d] "/"]........}
4400: 20 65 6c 73 65 20 7b 0a 09 09 09 09 09 09 09 09   else {.........
4410: 73 65 74 20 72 65 74 76 61 6c 28 5f 74 79 70 65  set retval(_type
4420: 29 20 66 69 6c 65 73 0a 09 09 09 09 09 09 09 09  ) files.........
4430: 73 65 74 20 72 65 74 76 61 6c 28 66 69 6c 65 29  set retval(file)
4440: 20 22 22 0a 09 09 09 09 09 09 09 7d 0a 09 09 09   ""........}....
4450: 09 09 09 7d 0a 09 09 09 09 09 7d 0a 09 09 09 09  ...}......}.....
4460: 7d 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09 09 72 65  }....}...}....re
4470: 74 75 72 6e 20 5b 61 72 72 61 79 20 67 65 74 20  turn [array get 
4480: 72 65 74 76 61 6c 5d 0a 09 7d 0a 0a 09 70 72 6f  retval]..}...pro
4490: 63 20 67 65 74 63 68 69 6c 64 72 65 6e 20 7b 64  c getchildren {d
44a0: 69 72 7d 20 7b 0a 09 09 61 72 72 61 79 20 73 65  ir} {...array se
44b0: 74 20 70 61 74 68 69 6e 66 6f 20 5b 5f 70 61 72  t pathinfo [_par
44c0: 73 65 70 61 74 68 20 24 64 69 72 5d 0a 0a 09 09  sepath $dir]....
44d0: 73 77 69 74 63 68 20 2d 2d 20 24 70 61 74 68 69  switch -- $pathi
44e0: 6e 66 6f 28 5f 63 68 69 6c 64 72 65 6e 29 20 7b  nfo(_children) {
44f0: 0a 09 09 09 22 73 69 74 65 73 22 20 7b 0a 09 09  ...."sites" {...
4500: 09 09 72 65 74 75 72 6e 20 5b 3a 3a 61 70 70 66  ..return [::appf
4510: 73 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45 4c 45  s::db eval {SELE
4520: 43 54 20 44 49 53 54 49 4e 43 54 20 68 6f 73 74  CT DISTINCT host
4530: 6e 61 6d 65 20 46 52 4f 4d 20 70 61 63 6b 61 67  name FROM packag
4540: 65 73 3b 7d 5d 0a 09 09 09 7d 0a 09 09 09 22 70  es;}]....}...."p
4550: 61 63 6b 61 67 65 73 22 20 7b 0a 09 09 09 09 63  ackages" {.....c
4560: 61 74 63 68 20 7b 0a 09 09 09 09 09 3a 3a 61 70  atch {......::ap
4570: 70 66 73 3a 3a 67 65 74 69 6e 64 65 78 20 24 70  pfs::getindex $p
4580: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
4590: 29 0a 09 09 09 09 7d 0a 0a 09 09 09 09 72 65 74  ).....}......ret
45a0: 75 72 6e 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62  urn [::appfs::db
45b0: 20 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 44 49   eval {SELECT DI
45c0: 53 54 49 4e 43 54 20 70 61 63 6b 61 67 65 20 46  STINCT package F
45d0: 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45  ROM packages WHE
45e0: 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 70  RE hostname = $p
45f0: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
4600: 29 3b 7d 5d 0a 09 09 09 7d 0a 09 09 09 22 6f 73  );}]....}...."os
4610: 2d 63 70 75 22 20 7b 0a 09 09 09 09 73 65 74 20  -cpu" {.....set 
4620: 72 65 74 76 61 6c 20 5b 3a 3a 61 70 70 66 73 3a  retval [::appfs:
4630: 3a 64 62 20 65 76 61 6c 20 7b 53 45 4c 45 43 54  :db eval {SELECT
4640: 20 44 49 53 54 49 4e 43 54 20 6f 73 20 7c 7c 20   DISTINCT os || 
4650: 22 2d 22 20 7c 7c 20 63 70 75 41 72 63 68 20 46  "-" || cpuArch F
4660: 52 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45  ROM packages WHE
4670: 52 45 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 70  RE hostname = $p
4680: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
4690: 29 20 41 4e 44 20 70 61 63 6b 61 67 65 20 3d 20  ) AND package = 
46a0: 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67  $pathinfo(packag
46b0: 65 29 3b 7d 5d 0a 0a 09 09 09 09 6c 61 70 70 65  e);}]......lappe
46c0: 6e 64 20 72 65 74 76 61 6c 20 22 70 6c 61 74 66  nd retval "platf
46d0: 6f 72 6d 22 0a 0a 09 09 09 09 72 65 74 75 72 6e  orm"......return
46e0: 20 24 72 65 74 76 61 6c 0a 09 09 09 7d 0a 09 09   $retval....}...
46f0: 09 22 76 65 72 73 69 6f 6e 73 22 20 7b 0a 09 09  ."versions" {...
4700: 09 09 73 65 74 20 72 65 74 76 61 6c 20 5b 3a 3a  ..set retval [::
4710: 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b  appfs::db eval {
4720: 0a 09 09 09 09 09 53 45 4c 45 43 54 20 44 49 53  ......SELECT DIS
4730: 54 49 4e 43 54 20 76 65 72 73 69 6f 6e 20 46 52  TINCT version FR
4740: 4f 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45 52  OM packages WHER
4750: 45 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 70 61  E hostname = $pa
4760: 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29  thinfo(hostname)
4770: 20 41 4e 44 20 70 61 63 6b 61 67 65 20 3d 20 24   AND package = $
4780: 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65  pathinfo(package
4790: 29 20 41 4e 44 20 6f 73 20 3d 20 24 70 61 74 68  ) AND os = $path
47a0: 69 6e 66 6f 28 6f 73 29 20 41 4e 44 20 63 70 75  info(os) AND cpu
47b0: 41 72 63 68 20 3d 20 24 70 61 74 68 69 6e 66 6f  Arch = $pathinfo
47c0: 28 63 70 75 29 3b 0a 09 09 09 09 7d 5d 0a 0a 09  (cpu);.....}]...
47d0: 09 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62 20 65  ...::appfs::db e
47e0: 76 61 6c 20 7b 53 45 4c 45 43 54 20 76 65 72 73  val {SELECT vers
47f0: 69 6f 6e 20 46 52 4f 4d 20 70 61 63 6b 61 67 65  ion FROM package
4800: 73 20 57 48 45 52 45 20 69 73 4c 61 74 65 73 74  s WHERE isLatest
4810: 20 3d 20 31 20 41 4e 44 20 68 6f 73 74 6e 61 6d   = 1 AND hostnam
4820: 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f  e = $pathinfo(ho
4830: 73 74 6e 61 6d 65 29 20 41 4e 44 20 70 61 63 6b  stname) AND pack
4840: 61 67 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28  age = $pathinfo(
4850: 70 61 63 6b 61 67 65 29 20 41 4e 44 20 6f 73 20  package) AND os 
4860: 3d 20 24 70 61 74 68 69 6e 66 6f 28 6f 73 29 20  = $pathinfo(os) 
4870: 41 4e 44 20 63 70 75 41 72 63 68 20 3d 20 24 70  AND cpuArch = $p
4880: 61 74 68 69 6e 66 6f 28 63 70 75 29 20 4c 49 4d  athinfo(cpu) LIM
4890: 49 54 20 31 3b 7d 20 6c 61 74 65 73 74 5f 69 6e  IT 1;} latest_in
48a0: 66 6f 20 7b 7d 0a 0a 09 09 09 09 69 66 20 7b 5b  fo {}......if {[
48b0: 69 6e 66 6f 20 65 78 69 73 74 73 20 6c 61 74 65  info exists late
48c0: 73 74 5f 69 6e 66 6f 28 76 65 72 73 69 6f 6e 29  st_info(version)
48d0: 5d 7d 20 7b 0a 09 09 09 09 09 6c 61 70 70 65 6e  ]} {......lappen
48e0: 64 20 72 65 74 76 61 6c 20 22 6c 61 74 65 73 74  d retval "latest
48f0: 22 0a 09 09 09 09 7d 0a 0a 09 09 09 09 72 65 74  ".....}......ret
4900: 75 72 6e 20 24 72 65 74 76 61 6c 0a 09 09 09 7d  urn $retval....}
4910: 0a 09 09 09 22 66 69 6c 65 73 22 20 7b 0a 09 09  ...."files" {...
4920: 09 09 63 61 74 63 68 20 7b 0a 09 09 09 09 09 3a  ..catch {......:
4930: 3a 61 70 70 66 73 3a 3a 67 65 74 69 6e 64 65 78  :appfs::getindex
4940: 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e   $pathinfo(hostn
4950: 61 6d 65 29 0a 09 09 09 09 09 3a 3a 61 70 70 66  ame)......::appf
4960: 73 3a 3a 67 65 74 70 6b 67 6d 61 6e 69 66 65 73  s::getpkgmanifes
4970: 74 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74  t $pathinfo(host
4980: 6e 61 6d 65 29 20 24 70 61 74 68 69 6e 66 6f 28  name) $pathinfo(
4990: 70 61 63 6b 61 67 65 5f 73 68 61 31 29 0a 09 09  package_sha1)...
49a0: 09 09 7d 0a 0a 09 09 09 09 73 65 74 20 72 65 74  ..}......set ret
49b0: 76 61 6c 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62  val [::appfs::db
49c0: 20 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 44 49   eval {SELECT DI
49d0: 53 54 49 4e 43 54 20 66 69 6c 65 5f 6e 61 6d 65  STINCT file_name
49e0: 20 46 52 4f 4d 20 66 69 6c 65 73 20 57 48 45 52   FROM files WHER
49f0: 45 20 70 61 63 6b 61 67 65 5f 73 68 61 31 20 3d  E package_sha1 =
4a00: 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61   $pathinfo(packa
4a10: 67 65 5f 73 68 61 31 29 20 41 4e 44 20 66 69 6c  ge_sha1) AND fil
4a20: 65 5f 64 69 72 65 63 74 6f 72 79 20 3d 20 24 70  e_directory = $p
4a30: 61 74 68 69 6e 66 6f 28 66 69 6c 65 29 3b 7d 5d  athinfo(file);}]
4a40: 0a 0a 09 09 09 09 69 66 20 7b 5b 69 6e 66 6f 20  ......if {[info 
4a50: 65 78 69 73 74 73 20 70 61 74 68 69 6e 66 6f 28  exists pathinfo(
4a60: 70 61 63 6b 61 67 65 29 5d 20 26 26 20 5b 69 6e  package)] && [in
4a70: 66 6f 20 65 78 69 73 74 73 20 70 61 74 68 69 6e  fo exists pathin
4a80: 66 6f 28 68 6f 73 74 6e 61 6d 65 29 5d 20 26 26  fo(hostname)] &&
4a90: 20 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 70 61   [info exists pa
4aa0: 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d 7d 20 7b  thinfo(file)]} {
4ab0: 0a 09 09 09 09 09 5f 61 73 5f 75 73 65 72 20 7b  ......_as_user {
4ac0: 0a 09 09 09 09 09 09 73 65 74 20 64 69 72 20 5b  .......set dir [
4ad0: 5f 6c 6f 63 61 6c 70 61 74 68 20 24 70 61 74 68  _localpath $path
4ae0: 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 20 24 70  info(package) $p
4af0: 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65  athinfo(hostname
4b00: 29 20 24 70 61 74 68 69 6e 66 6f 28 66 69 6c 65  ) $pathinfo(file
4b10: 29 5d 0a 09 09 09 09 09 09 73 65 74 20 77 68 69  )].......set whi
4b20: 74 65 6f 75 74 64 69 72 20 5b 73 74 72 69 6e 67  teoutdir [string
4b30: 20 72 61 6e 67 65 20 5b 5f 77 68 69 74 65 6f 75   range [_whiteou
4b40: 74 70 61 74 68 20 24 70 61 74 68 69 6e 66 6f 28  tpath $pathinfo(
4b50: 70 61 63 6b 61 67 65 29 20 24 70 61 74 68 69 6e  package) $pathin
4b60: 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 24 70 61  fo(hostname) $pa
4b70: 74 68 69 6e 66 6f 28 66 69 6c 65 29 5d 20 30 20  thinfo(file)] 0 
4b80: 65 6e 64 2d 31 35 5d 0a 0a 09 09 09 09 09 09 69  end-15]........i
4b90: 66 20 7b 24 77 68 69 74 65 6f 75 74 64 69 72 20  f {$whiteoutdir 
4ba0: 21 3d 20 22 22 7d 20 7b 0a 09 09 09 09 09 09 09  != ""} {........
4bb0: 66 6f 72 65 61 63 68 20 66 69 6c 65 20 5b 67 6c  foreach file [gl
4bc0: 6f 62 20 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 2d  ob -nocomplain -
4bd0: 74 61 69 6c 73 20 2d 64 69 72 65 63 74 6f 72 79  tails -directory
4be0: 20 24 77 68 69 74 65 6f 75 74 64 69 72 20 7b 7b   $whiteoutdir {{
4bf0: 2e 2c 7d 2a 2e 41 50 50 46 53 2e 57 48 49 54 45  .,}*.APPFS.WHITE
4c00: 4f 55 54 7d 5d 20 7b 0a 09 09 09 09 09 09 09 09  OUT}] {.........
4c10: 73 65 74 20 72 65 6d 6f 76 65 20 5b 73 74 72 69  set remove [stri
4c20: 6e 67 20 72 61 6e 67 65 20 24 66 69 6c 65 20 30  ng range $file 0
4c30: 20 65 6e 64 2d 31 35 5d 0a 09 09 09 09 09 09 09   end-15]........
4c40: 09 73 65 74 20 69 64 78 20 5b 6c 73 65 61 72 63  .set idx [lsearc
4c50: 68 20 2d 65 78 61 63 74 20 24 72 65 74 76 61 6c  h -exact $retval
4c60: 20 24 72 65 6d 6f 76 65 5d 0a 09 09 09 09 09 09   $remove].......
4c70: 09 09 69 66 20 7b 24 69 64 78 20 21 3d 20 2d 31  ..if {$idx != -1
4c80: 7d 20 7b 0a 09 09 09 09 09 09 09 09 09 73 65 74  } {..........set
4c90: 20 72 65 74 76 61 6c 20 5b 6c 72 65 70 6c 61 63   retval [lreplac
4ca0: 65 20 24 72 65 74 76 61 6c 20 24 69 64 78 20 24  e $retval $idx $
4cb0: 69 64 78 5d 0a 09 09 09 09 09 09 09 09 7d 0a 09  idx].........}..
4cc0: 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09 7d 0a  ......}.......}.
4cd0: 0a 09 09 09 09 09 09 69 66 20 7b 24 64 69 72 20  .......if {$dir 
4ce0: 21 3d 20 22 22 7d 20 7b 0a 09 09 09 09 09 09 09  != ""} {........
4cf0: 66 6f 72 65 61 63 68 20 66 69 6c 65 20 5b 67 6c  foreach file [gl
4d00: 6f 62 20 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 2d  ob -nocomplain -
4d10: 74 61 69 6c 73 20 2d 64 69 72 65 63 74 6f 72 79  tails -directory
4d20: 20 24 64 69 72 20 7b 7b 2e 2c 7d 2a 7d 5d 20 7b   $dir {{.,}*}] {
4d30: 0a 09 09 09 09 09 09 09 09 69 66 20 7b 24 66 69  .........if {$fi
4d40: 6c 65 20 3d 3d 20 22 2e 22 20 7c 7c 20 24 66 69  le == "." || $fi
4d50: 6c 65 20 3d 3d 20 22 2e 2e 22 7d 20 7b 0a 09 09  le == ".."} {...
4d60: 09 09 09 09 09 09 09 63 6f 6e 74 69 6e 75 65 0a  .......continue.
4d70: 09 09 09 09 09 09 09 09 7d 0a 0a 09 09 09 09 09  ........}.......
4d80: 09 09 09 69 66 20 7b 24 66 69 6c 65 20 3d 3d 20  ...if {$file == 
4d90: 22 2e 41 50 50 46 53 2e 57 48 49 54 45 4f 55 54  ".APPFS.WHITEOUT
4da0: 22 7d 20 7b 0a 09 09 09 09 09 09 09 09 09 63 6f  "} {..........co
4db0: 6e 74 69 6e 75 65 0a 09 09 09 09 09 09 09 09 7d  ntinue.........}
4dc0: 0a 0a 09 09 09 09 09 09 09 09 69 66 20 7b 5b 6c  ..........if {[l
4dd0: 73 65 61 72 63 68 20 2d 65 78 61 63 74 20 24 72  search -exact $r
4de0: 65 74 76 61 6c 20 24 66 69 6c 65 5d 20 21 3d 20  etval $file] != 
4df0: 2d 31 7d 20 7b 0a 09 09 09 09 09 09 09 09 09 63  -1} {..........c
4e00: 6f 6e 74 69 6e 75 65 0a 09 09 09 09 09 09 09 09  ontinue.........
4e10: 7d 0a 0a 09 09 09 09 09 09 09 09 6c 61 70 70 65  }..........lappe
4e20: 6e 64 20 72 65 74 76 61 6c 20 24 66 69 6c 65 0a  nd retval $file.
4e30: 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09 09 7d  .......}.......}
4e40: 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 0a 0a 09  ......}.....}...
4e50: 09 09 09 72 65 74 75 72 6e 20 24 72 65 74 76 61  ...return $retva
4e60: 6c 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09 09 72 65  l....}...}....re
4e70: 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72  turn -code error
4e80: 20 22 49 6e 76 61 6c 69 64 20 6f 72 20 75 6e 61   "Invalid or una
4e90: 63 63 65 70 74 61 62 6c 65 20 70 61 74 68 3a 20  cceptable path: 
4ea0: 24 64 69 72 22 0a 09 7d 0a 0a 09 70 72 6f 63 20  $dir"..}...proc 
4eb0: 67 65 74 61 74 74 72 20 7b 70 61 74 68 7d 20 7b  getattr {path} {
4ec0: 0a 09 09 61 72 72 61 79 20 73 65 74 20 70 61 74  ...array set pat
4ed0: 68 69 6e 66 6f 20 5b 5f 70 61 72 73 65 70 61 74  hinfo [_parsepat
4ee0: 68 20 24 70 61 74 68 5d 0a 09 09 61 72 72 61 79  h $path]...array
4ef0: 20 73 65 74 20 72 65 74 76 61 6c 20 5b 6c 69 73   set retval [lis
4f00: 74 5d 0a 0a 09 09 63 61 74 63 68 20 7b 0a 09 09  t]....catch {...
4f10: 09 3a 3a 61 70 70 66 73 3a 3a 67 65 74 69 6e 64  .::appfs::getind
4f20: 65 78 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73  ex $pathinfo(hos
4f30: 74 6e 61 6d 65 29 0a 09 09 09 3a 3a 61 70 70 66  tname)....::appf
4f40: 73 3a 3a 67 65 74 70 6b 67 6d 61 6e 69 66 65 73  s::getpkgmanifes
4f50: 74 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74  t $pathinfo(host
4f60: 6e 61 6d 65 29 20 24 70 61 74 68 69 6e 66 6f 28  name) $pathinfo(
4f70: 70 61 63 6b 61 67 65 5f 73 68 61 31 29 0a 09 09  package_sha1)...
4f80: 7d 0a 0a 09 09 73 65 74 20 72 65 74 76 61 6c 28  }....set retval(
4f90: 70 61 74 68 5f 74 79 70 65 29 20 24 70 61 74 68  path_type) $path
4fa0: 69 6e 66 6f 28 5f 74 79 70 65 29 0a 0a 09 09 73  info(_type)....s
4fb0: 77 69 74 63 68 20 2d 2d 20 24 70 61 74 68 69 6e  witch -- $pathin
4fc0: 66 6f 28 5f 74 79 70 65 29 20 7b 0a 09 09 09 22  fo(_type) {...."
4fd0: 74 6f 70 6c 65 76 65 6c 22 20 7b 0a 09 09 09 09  toplevel" {.....
4fe0: 73 65 74 20 72 65 74 76 61 6c 28 74 79 70 65 29  set retval(type)
4ff0: 20 64 69 72 65 63 74 6f 72 79 0a 09 09 09 09 73   directory.....s
5000: 65 74 20 72 65 74 76 61 6c 28 63 68 69 6c 64 63  et retval(childc
5010: 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74 68 20 5b  ount) [llength [
5020: 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74  getchildren $pat
5030: 68 5d 5d 0a 09 09 09 7d 0a 09 09 09 22 73 69 74  h]]....}...."sit
5040: 65 73 22 20 7b 0a 09 09 09 09 73 65 74 20 63 68  es" {.....set ch
5050: 65 63 6b 20 5b 3a 3a 61 70 70 66 73 3a 3a 64 62  eck [::appfs::db
5060: 20 6f 6e 65 63 6f 6c 75 6d 6e 20 7b 53 45 4c 45   onecolumn {SELE
5070: 43 54 20 31 20 46 52 4f 4d 20 70 61 63 6b 61 67  CT 1 FROM packag
5080: 65 73 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d  es WHERE hostnam
5090: 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f  e = $pathinfo(ho
50a0: 73 74 6e 61 6d 65 29 3b 7d 5d 0a 09 09 09 09 69  stname);}].....i
50b0: 66 20 7b 24 63 68 65 63 6b 20 3d 3d 20 22 31 22  f {$check == "1"
50c0: 7d 20 7b 0a 09 09 09 09 09 73 65 74 20 72 65 74  } {......set ret
50d0: 76 61 6c 28 74 79 70 65 29 20 64 69 72 65 63 74  val(type) direct
50e0: 6f 72 79 0a 09 09 09 09 09 73 65 74 20 72 65 74  ory......set ret
50f0: 76 61 6c 28 63 68 69 6c 64 63 6f 75 6e 74 29 20  val(childcount) 
5100: 30 0a 09 09 09 09 7d 0a 09 09 09 7d 0a 09 09 09  0.....}....}....
5110: 22 70 61 63 6b 61 67 65 73 22 20 7b 0a 09 09 09  "packages" {....
5120: 09 73 65 74 20 63 68 65 63 6b 20 5b 3a 3a 61 70  .set check [::ap
5130: 70 66 73 3a 3a 64 62 20 6f 6e 65 63 6f 6c 75 6d  pfs::db onecolum
5140: 6e 20 7b 53 45 4c 45 43 54 20 31 20 46 52 4f 4d  n {SELECT 1 FROM
5150: 20 70 61 63 6b 61 67 65 73 20 57 48 45 52 45 20   packages WHERE 
5160: 68 6f 73 74 6e 61 6d 65 20 3d 20 24 70 61 74 68  hostname = $path
5170: 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20 41  info(hostname) A
5180: 4e 44 20 70 61 63 6b 61 67 65 20 3d 20 24 70 61  ND package = $pa
5190: 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29 3b  thinfo(package);
51a0: 7d 5d 0a 09 09 09 09 69 66 20 7b 24 63 68 65 63  }].....if {$chec
51b0: 6b 20 3d 3d 20 22 31 22 7d 20 7b 0a 09 09 09 09  k == "1"} {.....
51c0: 09 73 65 74 20 72 65 74 76 61 6c 28 74 79 70 65  .set retval(type
51d0: 29 20 64 69 72 65 63 74 6f 72 79 0a 09 09 09 09  ) directory.....
51e0: 09 73 65 74 20 72 65 74 76 61 6c 28 63 68 69 6c  .set retval(chil
51f0: 64 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74 68  dcount) [llength
5200: 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70   [getchildren $p
5210: 61 74 68 5d 5d 0a 09 09 09 09 7d 0a 09 09 09 7d  ath]].....}....}
5220: 0a 09 09 09 22 6f 73 2d 63 70 75 22 20 7b 0a 09  ...."os-cpu" {..
5230: 09 09 09 69 66 20 7b 24 70 61 74 68 69 6e 66 6f  ...if {$pathinfo
5240: 28 6f 73 29 20 3d 3d 20 22 70 6c 61 74 66 6f 72  (os) == "platfor
5250: 6d 22 20 26 26 20 24 70 61 74 68 69 6e 66 6f 28  m" && $pathinfo(
5260: 63 70 75 29 20 3d 3d 20 22 22 7d 20 7b 0a 09 09  cpu) == ""} {...
5270: 09 09 09 73 65 74 20 63 68 65 63 6b 20 5b 3a 3a  ...set check [::
5280: 61 70 70 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b  appfs::db eval {
5290: 0a 09 09 09 09 09 09 53 45 4c 45 43 54 20 44 49  .......SELECT DI
52a0: 53 54 49 4e 43 54 20 6f 73 2c 20 63 70 75 41 72  STINCT os, cpuAr
52b0: 63 68 20 46 52 4f 4d 20 70 61 63 6b 61 67 65 73  ch FROM packages
52c0: 20 57 48 45 52 45 20 68 6f 73 74 6e 61 6d 65 20   WHERE hostname 
52d0: 3d 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74  = $pathinfo(host
52e0: 6e 61 6d 65 29 20 41 4e 44 20 70 61 63 6b 61 67  name) AND packag
52f0: 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 70 61  e = $pathinfo(pa
5300: 63 6b 61 67 65 29 3b 0a 09 09 09 09 09 7d 5d 0a  ckage);......}].
5310: 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
5320: 28 74 79 70 65 29 20 73 79 6d 6c 69 6e 6b 0a 0a  (type) symlink..
5330: 09 09 09 09 09 69 66 20 7b 24 63 68 65 63 6b 20  .....if {$check 
5340: 3d 3d 20 5b 6c 69 73 74 20 22 6e 6f 61 72 63 68  == [list "noarch
5350: 22 20 22 6e 6f 61 72 63 68 22 5d 7d 20 7b 0a 09  " "noarch"]} {..
5360: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
5370: 73 6f 75 72 63 65 29 20 22 6e 6f 61 72 63 68 2d  source) "noarch-
5380: 6e 6f 61 72 63 68 22 0a 09 09 09 09 09 7d 20 65  noarch"......} e
5390: 6c 73 65 20 7b 0a 09 09 09 09 09 09 73 65 74 20  lse {.......set 
53a0: 72 65 74 76 61 6c 28 73 6f 75 72 63 65 29 20 24  retval(source) $
53b0: 3a 3a 61 70 70 66 73 3a 3a 70 6c 61 74 66 6f 72  ::appfs::platfor
53c0: 6d 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d 20 65  m......}.....} e
53d0: 6c 73 65 20 7b 0a 09 09 09 09 09 73 65 74 20 63  lse {......set c
53e0: 68 65 63 6b 20 5b 3a 3a 61 70 70 66 73 3a 3a 64  heck [::appfs::d
53f0: 62 20 6f 6e 65 63 6f 6c 75 6d 6e 20 7b 0a 09 09  b onecolumn {...
5400: 09 09 09 09 53 45 4c 45 43 54 20 31 20 46 52 4f  ....SELECT 1 FRO
5410: 4d 20 70 61 63 6b 61 67 65 73 20 57 48 45 52 45  M packages WHERE
5420: 20 68 6f 73 74 6e 61 6d 65 20 3d 20 24 70 61 74   hostname = $pat
5430: 68 69 6e 66 6f 28 68 6f 73 74 6e 61 6d 65 29 20  hinfo(hostname) 
5440: 41 4e 44 20 70 61 63 6b 61 67 65 20 3d 20 24 70  AND package = $p
5450: 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29  athinfo(package)
5460: 20 41 4e 44 20 6f 73 20 3d 20 24 70 61 74 68 69   AND os = $pathi
5470: 6e 66 6f 28 6f 73 29 20 41 4e 44 20 63 70 75 41  nfo(os) AND cpuA
5480: 72 63 68 20 3d 20 24 70 61 74 68 69 6e 66 6f 28  rch = $pathinfo(
5490: 63 70 75 29 3b 0a 09 09 09 09 09 7d 5d 0a 09 09  cpu);......}]...
54a0: 09 09 09 69 66 20 7b 24 63 68 65 63 6b 20 3d 3d  ...if {$check ==
54b0: 20 22 31 22 7d 20 7b 0a 09 09 09 09 09 09 73 65   "1"} {.......se
54c0: 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 64  t retval(type) d
54d0: 69 72 65 63 74 6f 72 79 0a 09 09 09 09 09 09 73  irectory.......s
54e0: 65 74 20 72 65 74 76 61 6c 28 63 68 69 6c 64 63  et retval(childc
54f0: 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74 68 20 5b  ount) [llength [
5500: 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74  getchildren $pat
5510: 68 5d 5d 0a 09 09 09 09 09 7d 0a 09 09 09 09 7d  h]]......}.....}
5520: 0a 09 09 09 7d 0a 09 09 09 22 76 65 72 73 69 6f  ....}...."versio
5530: 6e 73 22 20 7b 0a 09 09 09 09 69 66 20 7b 24 70  ns" {.....if {$p
5540: 61 74 68 69 6e 66 6f 28 76 65 72 73 69 6f 6e 29  athinfo(version)
5550: 20 3d 3d 20 22 6c 61 74 65 73 74 22 7d 20 7b 0a   == "latest"} {.
5560: 09 09 09 09 09 3a 3a 61 70 70 66 73 3a 3a 64 62  .....::appfs::db
5570: 20 65 76 61 6c 20 7b 53 45 4c 45 43 54 20 76 65   eval {SELECT ve
5580: 72 73 69 6f 6e 20 46 52 4f 4d 20 70 61 63 6b 61  rsion FROM packa
5590: 67 65 73 20 57 48 45 52 45 20 69 73 4c 61 74 65  ges WHERE isLate
55a0: 73 74 20 3d 20 31 20 41 4e 44 20 68 6f 73 74 6e  st = 1 AND hostn
55b0: 61 6d 65 20 3d 20 24 70 61 74 68 69 6e 66 6f 28  ame = $pathinfo(
55c0: 68 6f 73 74 6e 61 6d 65 29 20 41 4e 44 20 70 61  hostname) AND pa
55d0: 63 6b 61 67 65 20 3d 20 24 70 61 74 68 69 6e 66  ckage = $pathinf
55e0: 6f 28 70 61 63 6b 61 67 65 29 20 41 4e 44 20 6f  o(package) AND o
55f0: 73 20 3d 20 24 70 61 74 68 69 6e 66 6f 28 6f 73  s = $pathinfo(os
5600: 29 20 41 4e 44 20 63 70 75 41 72 63 68 20 3d 20  ) AND cpuArch = 
5610: 24 70 61 74 68 69 6e 66 6f 28 63 70 75 29 20 4c  $pathinfo(cpu) L
5620: 49 4d 49 54 20 31 3b 7d 20 6c 61 74 65 73 74 5f  IMIT 1;} latest_
5630: 69 6e 66 6f 20 7b 7d 0a 0a 09 09 09 09 09 69 66  info {}.......if
5640: 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 6c   {[info exists l
5650: 61 74 65 73 74 5f 69 6e 66 6f 28 76 65 72 73 69  atest_info(versi
5660: 6f 6e 29 5d 7d 20 7b 0a 09 09 09 09 09 09 73 65  on)]} {.......se
5670: 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 73  t retval(type) s
5680: 79 6d 6c 69 6e 6b 0a 09 09 09 09 09 09 73 65 74  ymlink.......set
5690: 20 72 65 74 76 61 6c 28 73 6f 75 72 63 65 29 20   retval(source) 
56a0: 24 6c 61 74 65 73 74 5f 69 6e 66 6f 28 76 65 72  $latest_info(ver
56b0: 73 69 6f 6e 29 0a 09 09 09 09 09 7d 0a 09 09 09  sion)......}....
56c0: 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 09 69  .} else {......i
56d0: 66 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20  f {[info exists 
56e0: 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65  pathinfo(package
56f0: 5f 73 68 61 31 29 5d 20 26 26 20 24 70 61 74 68  _sha1)] && $path
5700: 69 6e 66 6f 28 70 61 63 6b 61 67 65 5f 73 68 61  info(package_sha
5710: 31 29 20 21 3d 20 22 22 7d 20 7b 0a 09 09 09 09  1) != ""} {.....
5720: 09 09 73 65 74 20 72 65 74 76 61 6c 28 74 79 70  ..set retval(typ
5730: 65 29 20 64 69 72 65 63 74 6f 72 79 0a 09 09 09  e) directory....
5740: 09 09 09 73 65 74 20 72 65 74 76 61 6c 28 63 68  ...set retval(ch
5750: 69 6c 64 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67  ildcount) [lleng
5760: 74 68 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20  th [getchildren 
5770: 24 70 61 74 68 5d 5d 0a 09 09 09 09 09 7d 0a 09  $path]]......}..
5780: 09 09 09 7d 0a 09 09 09 7d 0a 09 09 09 22 66 69  ...}....}...."fi
5790: 6c 65 73 22 20 7b 0a 09 09 09 09 73 65 74 20 72  les" {.....set r
57a0: 65 74 76 61 6c 28 70 61 63 6b 61 67 65 64 29 20  etval(packaged) 
57b0: 31 0a 0a 09 09 09 09 73 65 74 20 6c 6f 63 61 6c  1......set local
57c0: 70 61 74 68 20 5b 5f 6c 6f 63 61 6c 70 61 74 68  path [_localpath
57d0: 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61   $pathinfo(packa
57e0: 67 65 29 20 24 70 61 74 68 69 6e 66 6f 28 68 6f  ge) $pathinfo(ho
57f0: 73 74 6e 61 6d 65 29 20 24 70 61 74 68 69 6e 66  stname) $pathinf
5800: 6f 28 66 69 6c 65 29 5d 0a 09 09 09 09 73 65 74  o(file)].....set
5810: 20 77 68 69 74 65 6f 75 74 70 61 74 68 20 20 5b   whiteoutpath  [
5820: 5f 77 68 69 74 65 6f 75 74 70 61 74 68 20 24 70  _whiteoutpath $p
5830: 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67 65 29  athinfo(package)
5840: 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73 74 6e   $pathinfo(hostn
5850: 61 6d 65 29 20 24 70 61 74 68 69 6e 66 6f 28 66  ame) $pathinfo(f
5860: 69 6c 65 29 5d 0a 0a 09 09 09 09 73 65 74 20 72  ile)]......set r
5870: 65 74 76 61 6c 28 6c 6f 63 61 6c 70 61 74 68 29  etval(localpath)
5880: 20 24 6c 6f 63 61 6c 70 61 74 68 0a 09 09 09 09   $localpath.....
5890: 73 65 74 20 72 65 74 76 61 6c 28 77 68 69 74 65  set retval(white
58a0: 6f 75 74 70 61 74 68 29 20 24 77 68 69 74 65 6f  outpath) $whiteo
58b0: 75 74 70 61 74 68 0a 0a 09 09 09 09 75 6e 73 65  utpath......unse
58c0: 74 20 2d 6e 6f 63 6f 6d 70 6c 61 69 6e 20 6c 6f  t -nocomplain lo
58d0: 63 61 6c 70 61 74 68 69 6e 66 6f 0a 09 09 09 09  calpathinfo.....
58e0: 69 66 20 7b 24 6c 6f 63 61 6c 70 61 74 68 20 21  if {$localpath !
58f0: 3d 20 22 22 7d 20 7b 0a 09 09 09 09 09 5f 61 73  = ""} {......_as
5900: 5f 75 73 65 72 20 7b 0a 09 09 09 09 09 09 63 61  _user {.......ca
5910: 74 63 68 20 7b 0a 09 09 09 09 09 09 09 66 69 6c  tch {........fil
5920: 65 20 6c 73 74 61 74 20 24 6c 6f 63 61 6c 70 61  e lstat $localpa
5930: 74 68 20 6c 6f 63 61 6c 70 61 74 68 69 6e 66 6f  th localpathinfo
5940: 0a 09 09 09 09 09 09 7d 0a 09 09 09 09 09 7d 0a  .......}......}.
5950: 09 09 09 09 7d 0a 0a 09 09 09 09 69 66 20 7b 24  ....}......if {$
5960: 6c 6f 63 61 6c 70 61 74 68 20 21 3d 20 22 22 20  localpath != "" 
5970: 26 26 20 5b 69 6e 66 6f 20 65 78 69 73 74 73 20  && [info exists 
5980: 6c 6f 63 61 6c 70 61 74 68 69 6e 66 6f 5d 7d 20  localpathinfo]} 
5990: 7b 0a 09 09 09 09 09 73 65 74 20 72 65 74 76 61  {......set retva
59a0: 6c 28 69 73 5f 6c 6f 63 61 6c 66 69 6c 65 29 20  l(is_localfile) 
59b0: 31 0a 09 09 09 09 09 75 6e 73 65 74 20 72 65 74  1......unset ret
59c0: 76 61 6c 28 70 61 63 6b 61 67 65 64 29 0a 09 09  val(packaged)...
59d0: 09 09 09 63 61 74 63 68 20 7b 0a 09 09 09 09 09  ...catch {......
59e0: 09 73 65 74 20 72 65 74 76 61 6c 28 74 69 6d 65  .set retval(time
59f0: 29 20 24 6c 6f 63 61 6c 70 61 74 68 69 6e 66 6f  ) $localpathinfo
5a00: 28 6d 74 69 6d 65 29 0a 0a 09 09 09 09 09 09 73  (mtime)........s
5a10: 77 69 74 63 68 20 2d 2d 20 24 6c 6f 63 61 6c 70  witch -- $localp
5a20: 61 74 68 69 6e 66 6f 28 74 79 70 65 29 20 7b 0a  athinfo(type) {.
5a30: 09 09 09 09 09 09 09 22 64 69 72 65 63 74 6f 72  ......."director
5a40: 79 22 20 7b 0a 09 09 09 09 09 09 09 09 73 65 74  y" {.........set
5a50: 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 22 64   retval(type) "d
5a60: 69 72 65 63 74 6f 72 79 22 0a 09 09 09 09 09 09  irectory".......
5a70: 09 09 73 65 74 20 72 65 74 76 61 6c 28 63 68 69  ..set retval(chi
5a80: 6c 64 63 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74  ldcount) [llengt
5a90: 68 20 5b 67 65 74 63 68 69 6c 64 72 65 6e 20 24  h [getchildren $
5aa0: 70 61 74 68 5d 5d 0a 09 09 09 09 09 09 09 7d 0a  path]]........}.
5ab0: 09 09 09 09 09 09 09 22 66 69 6c 65 22 20 7b 0a  ......."file" {.
5ac0: 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
5ad0: 61 6c 28 74 79 70 65 29 20 22 66 69 6c 65 22 0a  al(type) "file".
5ae0: 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
5af0: 61 6c 28 73 69 7a 65 29 20 24 6c 6f 63 61 6c 70  al(size) $localp
5b00: 61 74 68 69 6e 66 6f 28 73 69 7a 65 29 0a 0a 09  athinfo(size)...
5b10: 09 09 09 09 09 09 09 23 20 4f 6e 63 65 20 74 68  .......# Once th
5b20: 65 20 75 73 65 72 20 77 72 69 74 65 73 20 74 6f  e user writes to
5b30: 20 61 20 66 69 6c 65 2c 20 61 6c 6c 20 69 74 73   a file, all its
5b40: 20 6f 74 68 65 72 0a 09 09 09 09 09 09 09 09 23   other.........#
5b50: 20 61 74 74 72 69 62 75 74 65 73 20 28 73 75 63   attributes (suc
5b60: 68 20 61 73 20 73 75 69 64 29 20 61 72 65 20 6c  h as suid) are l
5b70: 6f 73 74 0a 0a 09 09 09 09 09 09 09 09 5f 61 73  ost.........._as
5b80: 5f 75 73 65 72 20 7b 0a 09 09 09 09 09 09 09 09  _user {.........
5b90: 09 69 66 20 7b 5b 66 69 6c 65 20 65 78 65 63 75  .if {[file execu
5ba0: 74 61 62 6c 65 20 24 6c 6f 63 61 6c 70 61 74 68  table $localpath
5bb0: 5d 7d 20 7b 0a 09 09 09 09 09 09 09 09 09 09 73  ]} {...........s
5bc0: 65 74 20 72 65 74 76 61 6c 28 70 65 72 6d 73 29  et retval(perms)
5bd0: 20 22 78 2d 22 0a 09 09 09 09 09 09 09 09 09 7d   "x-"..........}
5be0: 20 65 6c 73 65 20 7b 0a 09 09 09 09 09 09 09 09   else {.........
5bf0: 09 09 73 65 74 20 72 65 74 76 61 6c 28 70 65 72  ..set retval(per
5c00: 6d 73 29 20 22 2d 22 0a 09 09 09 09 09 09 09 09  ms) "-".........
5c10: 09 7d 0a 09 09 09 09 09 09 09 09 7d 0a 09 09 09  .}.........}....
5c20: 09 09 09 09 7d 0a 09 09 09 09 09 09 09 22 6c 69  ....}........"li
5c30: 6e 6b 22 20 7b 0a 09 09 09 09 09 09 09 09 73 65  nk" {.........se
5c40: 74 20 72 65 74 76 61 6c 28 74 79 70 65 29 20 22  t retval(type) "
5c50: 73 79 6d 6c 69 6e 6b 22 0a 0a 09 09 09 09 09 09  symlink"........
5c60: 09 09 5f 61 73 5f 75 73 65 72 20 7b 0a 09 09 09  .._as_user {....
5c70: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
5c80: 28 73 6f 75 72 63 65 29 20 5b 66 69 6c 65 20 72  (source) [file r
5c90: 65 61 64 6c 69 6e 6b 20 24 6c 6f 63 61 6c 70 61  eadlink $localpa
5ca0: 74 68 5d 0a 09 09 09 09 09 09 09 09 7d 0a 09 09  th].........}...
5cb0: 09 09 09 09 09 7d 0a 09 09 09 09 09 09 09 22 66  .....}........"f
5cc0: 69 66 6f 22 20 7b 0a 09 09 09 09 09 09 09 09 23  ifo" {.........#
5cd0: 20 43 61 70 69 74 61 6c 69 7a 65 64 20 73 6f 20   Capitalized so 
5ce0: 74 68 61 74 20 74 68 65 20 66 69 72 73 74 20 63  that the first c
5cf0: 68 61 72 20 69 73 20 75 6e 69 71 75 65 0a 09 09  har is unique...
5d00: 09 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c  ......set retval
5d10: 28 74 79 70 65 29 20 22 46 69 66 6f 22 0a 09 09  (type) "Fifo"...
5d20: 09 09 09 09 09 7d 0a 09 09 09 09 09 09 09 22 73  .....}........"s
5d30: 6f 63 6b 65 74 22 20 7b 0a 09 09 09 09 09 09 09  ocket" {........
5d40: 09 23 20 43 61 70 69 74 61 6c 69 7a 65 64 20 73  .# Capitalized s
5d50: 6f 20 74 68 61 74 20 74 68 65 20 66 69 72 73 74  o that the first
5d60: 20 63 68 61 72 20 69 73 20 75 6e 69 71 75 65 0a   char is unique.
5d70: 09 09 09 09 09 09 09 09 73 65 74 20 72 65 74 76  ........set retv
5d80: 61 6c 28 74 79 70 65 29 20 22 53 6f 63 6b 65 74  al(type) "Socket
5d90: 22 0a 09 09 09 09 09 09 09 7d 0a 09 09 09 09 09  "........}......
5da0: 09 7d 0a 09 09 09 09 09 7d 20 65 72 72 0a 09 09  .}......} err...
5db0: 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09 09 09  ..} else {......
5dc0: 69 66 20 7b 24 77 68 69 74 65 6f 75 74 70 61 74  if {$whiteoutpat
5dd0: 68 20 3d 3d 20 22 22 20 7c 7c 20 21 5b 66 69 6c  h == "" || ![fil
5de0: 65 20 65 78 69 73 74 73 20 24 77 68 69 74 65 6f  e exists $whiteo
5df0: 75 74 70 61 74 68 5d 7d 20 7b 0a 09 09 09 09 09  utpath]} {......
5e00: 09 73 65 74 20 72 65 74 76 61 6c 28 69 73 5f 72  .set retval(is_r
5e10: 65 6d 6f 74 65 66 69 6c 65 29 20 31 0a 0a 09 09  emotefile) 1....
5e20: 09 09 09 09 73 65 74 20 77 6f 72 6b 20 5b 73 70  ....set work [sp
5e30: 6c 69 74 20 24 70 61 74 68 69 6e 66 6f 28 66 69  lit $pathinfo(fi
5e40: 6c 65 29 20 22 2f 22 5d 0a 09 09 09 09 09 09 73  le) "/"].......s
5e50: 65 74 20 64 69 72 65 63 74 6f 72 79 20 5b 6a 6f  et directory [jo
5e60: 69 6e 20 5b 6c 72 61 6e 67 65 20 24 77 6f 72 6b  in [lrange $work
5e70: 20 30 20 65 6e 64 2d 31 5d 20 22 2f 22 5d 0a 09   0 end-1] "/"]..
5e80: 09 09 09 09 09 73 65 74 20 66 69 6c 65 20 5b 6c  .....set file [l
5e90: 69 6e 64 65 78 20 24 77 6f 72 6b 20 65 6e 64 5d  index $work end]
5ea0: 0a 0a 09 09 09 09 09 09 69 66 20 7b 24 64 69 72  ........if {$dir
5eb0: 65 63 74 6f 72 79 20 3d 3d 20 22 22 20 26 26 20  ectory == "" && 
5ec0: 24 66 69 6c 65 20 3d 3d 20 22 22 7d 20 7b 0a 09  $file == ""} {..
5ed0: 09 09 09 09 09 09 61 72 72 61 79 20 73 65 74 20  ......array set 
5ee0: 72 65 74 76 61 6c 20 5b 6c 69 73 74 20 74 79 70  retval [list typ
5ef0: 65 20 64 69 72 65 63 74 6f 72 79 5d 0a 09 09 09  e directory]....
5f00: 09 09 09 7d 0a 0a 09 09 09 09 09 09 3a 3a 61 70  ...}........::ap
5f10: 70 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45  pfs::db eval {SE
5f20: 4c 45 43 54 20 74 79 70 65 2c 20 74 69 6d 65 2c  LECT type, time,
5f30: 20 73 6f 75 72 63 65 2c 20 73 69 7a 65 2c 20 70   source, size, p
5f40: 65 72 6d 73 2c 20 66 69 6c 65 5f 73 68 61 31 20  erms, file_sha1 
5f50: 46 52 4f 4d 20 66 69 6c 65 73 20 57 48 45 52 45  FROM files WHERE
5f60: 20 70 61 63 6b 61 67 65 5f 73 68 61 31 20 3d 20   package_sha1 = 
5f70: 24 70 61 74 68 69 6e 66 6f 28 70 61 63 6b 61 67  $pathinfo(packag
5f80: 65 5f 73 68 61 31 29 20 41 4e 44 20 66 69 6c 65  e_sha1) AND file
5f90: 5f 64 69 72 65 63 74 6f 72 79 20 3d 20 24 64 69  _directory = $di
5fa0: 72 65 63 74 6f 72 79 20 41 4e 44 20 66 69 6c 65  rectory AND file
5fb0: 5f 6e 61 6d 65 20 3d 20 24 66 69 6c 65 3b 7d 20  _name = $file;} 
5fc0: 72 65 74 76 61 6c 20 7b 7d 0a 0a 09 09 09 09 09  retval {}.......
5fd0: 09 23 20 41 6c 6c 6f 77 20 61 6e 20 61 64 6d 69  .# Allow an admi
5fe0: 6e 69 73 74 72 61 74 6f 72 20 74 6f 20 73 75 70  nistrator to sup
5ff0: 70 6c 79 20 61 64 64 69 74 69 6f 6e 61 6c 20 70  ply additional p
6000: 65 72 6d 69 73 73 69 6f 6e 73 20 74 6f 20 72 65  ermissions to re
6010: 6d 6f 74 65 20 66 69 6c 65 73 0a 09 09 09 09 09  mote files......
6020: 09 69 66 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74  .if {[info exist
6030: 73 20 72 65 74 76 61 6c 28 70 65 72 6d 73 29 5d  s retval(perms)]
6040: 7d 20 7b 0a 09 09 09 09 09 09 09 23 20 4c 6f 77  } {........# Low
6050: 65 72 20 63 61 73 65 20 74 68 69 73 20 69 6e 20  er case this in 
6060: 63 61 73 65 20 61 6e 20 75 70 70 65 72 2d 63 61  case an upper-ca
6070: 73 65 64 20 76 61 6c 75 65 20 77 61 73 20 70 75  sed value was pu
6080: 74 20 69 6e 0a 09 09 09 09 09 09 09 23 20 74 68  t in........# th
6090: 65 20 64 61 74 61 62 61 73 65 20 62 65 66 6f 72  e database befor
60a0: 65 20 77 65 20 73 74 61 72 74 65 64 20 6c 6f 77  e we started low
60b0: 65 72 63 61 73 69 6e 67 20 74 68 65 6d 0a 09 09  ercasing them...
60c0: 09 09 09 09 09 73 65 74 20 72 65 74 76 61 6c 28  .....set retval(
60d0: 70 65 72 6d 73 29 20 5b 73 74 72 69 6e 67 20 74  perms) [string t
60e0: 6f 6c 6f 77 65 72 20 24 72 65 74 76 61 6c 28 70  olower $retval(p
60f0: 65 72 6d 73 29 5d 0a 0a 09 09 09 09 09 09 09 73  erms)].........s
6100: 65 74 20 72 65 74 76 61 6c 28 70 65 72 6d 73 29  et retval(perms)
6110: 20 5b 3a 3a 61 70 70 66 73 3a 3a 75 73 65 72 3a   [::appfs::user:
6120: 3a 63 68 61 6e 67 65 5f 70 65 72 6d 73 20 24 70  :change_perms $p
6130: 61 74 68 20 24 72 65 74 76 61 6c 28 66 69 6c 65  ath $retval(file
6140: 5f 73 68 61 31 29 20 24 72 65 74 76 61 6c 28 70  _sha1) $retval(p
6150: 65 72 6d 73 29 5d 0a 09 09 09 09 09 09 7d 0a 0a  erms)].......}..
6160: 09 09 09 09 09 09 69 66 20 7b 5b 69 6e 66 6f 20  ......if {[info 
6170: 65 78 69 73 74 73 20 72 65 74 76 61 6c 28 74 79  exists retval(ty
6180: 70 65 29 5d 20 26 26 20 24 72 65 74 76 61 6c 28  pe)] && $retval(
6190: 74 79 70 65 29 20 3d 3d 20 22 64 69 72 65 63 74  type) == "direct
61a0: 6f 72 79 22 7d 20 7b 0a 09 09 09 09 09 09 09 73  ory"} {........s
61b0: 65 74 20 72 65 74 76 61 6c 28 63 68 69 6c 64 63  et retval(childc
61c0: 6f 75 6e 74 29 20 5b 6c 6c 65 6e 67 74 68 20 5b  ount) [llength [
61d0: 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61 74  getchildren $pat
61e0: 68 5d 5d 0a 09 09 09 09 09 09 7d 0a 0a 09 09 09  h]].......}.....
61f0: 09 09 09 75 6e 73 65 74 20 2d 6e 6f 63 6f 6d 70  ...unset -nocomp
6200: 6c 61 69 6e 20 72 65 74 76 61 6c 28 2a 29 0a 09  lain retval(*)..
6210: 09 09 09 09 7d 0a 09 09 09 09 7d 0a 0a 09 09 09  ....}.....}.....
6220: 7d 0a 09 09 7d 0a 0a 09 09 69 66 20 7b 21 5b 69  }...}....if {![i
6230: 6e 66 6f 20 65 78 69 73 74 73 20 72 65 74 76 61  nfo exists retva
6240: 6c 28 74 79 70 65 29 5d 7d 20 7b 0a 09 09 09 72  l(type)]} {....r
6250: 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f  eturn -code erro
6260: 72 20 22 4e 6f 20 73 75 63 68 20 66 69 6c 65 20  r "No such file 
6270: 6f 72 20 64 69 72 65 63 74 6f 72 79 22 0a 09 09  or directory"...
6280: 7d 0a 0a 09 09 72 65 74 75 72 6e 20 5b 61 72 72  }....return [arr
6290: 61 79 20 67 65 74 20 72 65 74 76 61 6c 5d 0a 09  ay get retval]..
62a0: 7d 0a 0a 09 70 72 6f 63 20 6f 70 65 6e 70 61 74  }...proc openpat
62b0: 68 20 7b 70 61 74 68 20 6d 6f 64 65 7d 20 7b 0a  h {path mode} {.
62c0: 09 09 61 72 72 61 79 20 73 65 74 20 70 61 74 68  ..array set path
62d0: 69 6e 66 6f 20 5b 5f 70 61 72 73 65 70 61 74 68  info [_parsepath
62e0: 20 24 70 61 74 68 5d 0a 0a 09 09 69 66 20 7b 24   $path]....if {$
62f0: 70 61 74 68 69 6e 66 6f 28 5f 74 79 70 65 29 20  pathinfo(_type) 
6300: 21 3d 20 22 66 69 6c 65 73 22 7d 20 7b 0a 09 09  != "files"} {...
6310: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
6320: 72 6f 72 20 22 69 6e 76 61 6c 69 64 20 70 61 74  ror "invalid pat
6330: 68 20 74 79 70 65 3a 20 47 6f 74 20 5c 22 24 70  h type: Got \"$p
6340: 61 74 68 69 6e 66 6f 28 5f 74 79 70 65 29 5c 22  athinfo(_type)\"
6350: 2c 20 6e 65 65 64 20 5c 22 66 69 6c 65 73 5c 22  , need \"files\"
6360: 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20 6c 6f 63  "...}....set loc
6370: 61 6c 70 61 74 68 20 5b 5f 6c 6f 63 61 6c 70 61  alpath [_localpa
6380: 74 68 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63  th $pathinfo(pac
6390: 6b 61 67 65 29 20 24 70 61 74 68 69 6e 66 6f 28  kage) $pathinfo(
63a0: 68 6f 73 74 6e 61 6d 65 29 20 24 70 61 74 68 69  hostname) $pathi
63b0: 6e 66 6f 28 66 69 6c 65 29 5d 0a 0a 09 09 69 66  nfo(file)]....if
63c0: 20 7b 24 6d 6f 64 65 20 3d 3d 20 22 63 72 65 61   {$mode == "crea
63d0: 74 65 22 7d 20 7b 0a 09 09 09 69 66 20 7b 24 6c  te"} {....if {$l
63e0: 6f 63 61 6c 70 61 74 68 20 3d 3d 20 22 22 7d 20  ocalpath == ""} 
63f0: 7b 0a 09 09 09 09 72 65 74 75 72 6e 20 2d 63 6f  {.....return -co
6400: 64 65 20 65 72 72 6f 72 20 22 41 73 6b 65 64 20  de error "Asked 
6410: 74 6f 20 63 72 65 61 74 65 2c 20 62 75 74 20 6e  to create, but n
6420: 6f 20 68 6f 6d 65 20 64 69 72 65 63 74 6f 72 79  o home directory
6430: 2e 22 0a 09 09 09 7d 0a 0a 09 09 09 72 65 74 75  ."....}.....retu
6440: 72 6e 20 24 6c 6f 63 61 6c 70 61 74 68 0a 09 09  rn $localpath...
6450: 7d 0a 0a 09 09 69 66 20 7b 24 6c 6f 63 61 6c 70  }....if {$localp
6460: 61 74 68 20 21 3d 20 22 22 20 26 26 20 5b 66 69  ath != "" && [fi
6470: 6c 65 20 65 78 69 73 74 73 20 24 6c 6f 63 61 6c  le exists $local
6480: 70 61 74 68 5d 7d 20 7b 0a 09 09 09 72 65 74 75  path]} {....retu
6490: 72 6e 20 24 6c 6f 63 61 6c 70 61 74 68 0a 09 09  rn $localpath...
64a0: 7d 0a 0a 09 09 73 65 74 20 77 6f 72 6b 20 5b 73  }....set work [s
64b0: 70 6c 69 74 20 24 70 61 74 68 69 6e 66 6f 28 66  plit $pathinfo(f
64c0: 69 6c 65 29 20 22 2f 22 5d 0a 09 09 73 65 74 20  ile) "/"]...set 
64d0: 64 69 72 65 63 74 6f 72 79 20 5b 6a 6f 69 6e 20  directory [join 
64e0: 5b 6c 72 61 6e 67 65 20 24 77 6f 72 6b 20 30 20  [lrange $work 0 
64f0: 65 6e 64 2d 31 5d 20 22 2f 22 5d 0a 09 09 73 65  end-1] "/"]...se
6500: 74 20 66 69 6c 65 20 5b 6c 69 6e 64 65 78 20 24  t file [lindex $
6510: 77 6f 72 6b 20 65 6e 64 5d 0a 09 09 3a 3a 61 70  work end]...::ap
6520: 70 66 73 3a 3a 64 62 20 65 76 61 6c 20 7b 53 45  pfs::db eval {SE
6530: 4c 45 43 54 20 66 69 6c 65 5f 73 68 61 31 2c 20  LECT file_sha1, 
6540: 70 65 72 6d 73 20 46 52 4f 4d 20 66 69 6c 65 73  perms FROM files
6550: 20 57 48 45 52 45 20 70 61 63 6b 61 67 65 5f 73   WHERE package_s
6560: 68 61 31 20 3d 20 24 70 61 74 68 69 6e 66 6f 28  ha1 = $pathinfo(
6570: 70 61 63 6b 61 67 65 5f 73 68 61 31 29 20 41 4e  package_sha1) AN
6580: 44 20 66 69 6c 65 5f 6e 61 6d 65 20 3d 20 24 66  D file_name = $f
6590: 69 6c 65 20 41 4e 44 20 66 69 6c 65 5f 64 69 72  ile AND file_dir
65a0: 65 63 74 6f 72 79 20 3d 20 24 64 69 72 65 63 74  ectory = $direct
65b0: 6f 72 79 3b 7d 20 70 6b 67 70 61 74 68 69 6e 66  ory;} pkgpathinf
65c0: 6f 20 7b 7d 0a 0a 09 09 69 66 20 7b 24 70 6b 67  o {}....if {$pkg
65d0: 70 61 74 68 69 6e 66 6f 28 66 69 6c 65 5f 73 68  pathinfo(file_sh
65e0: 61 31 29 20 3d 3d 20 22 22 7d 20 7b 0a 09 09 09  a1) == ""} {....
65f0: 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72  return -code err
6600: 6f 72 20 22 4e 6f 20 73 75 63 68 20 66 69 6c 65  or "No such file
6610: 20 6f 72 20 64 69 72 65 63 74 6f 72 79 22 0a 09   or directory"..
6620: 09 7d 0a 0a 09 09 73 65 74 20 6c 6f 63 61 6c 63  .}....set localc
6630: 61 63 68 65 66 69 6c 65 20 5b 64 6f 77 6e 6c 6f  achefile [downlo
6640: 61 64 20 24 70 61 74 68 69 6e 66 6f 28 68 6f 73  ad $pathinfo(hos
6650: 74 6e 61 6d 65 29 20 24 70 6b 67 70 61 74 68 69  tname) $pkgpathi
6660: 6e 66 6f 28 66 69 6c 65 5f 73 68 61 31 29 5d 0a  nfo(file_sha1)].
6670: 0a 09 09 69 66 20 7b 24 6d 6f 64 65 20 3d 3d 20  ...if {$mode == 
6680: 22 77 72 69 74 65 22 7d 20 7b 0a 09 09 09 5f 61  "write"} {...._a
6690: 73 5f 75 73 65 72 20 7b 0a 09 09 09 09 73 65 74  s_user {.....set
66a0: 20 74 6d 70 6c 6f 63 61 6c 70 61 74 68 20 22 24   tmplocalpath "$
66b0: 7b 6c 6f 63 61 6c 70 61 74 68 7d 2e 5b 65 78 70  {localpath}.[exp
66c0: 72 20 72 61 6e 64 28 29 5d 5b 63 6c 6f 63 6b 20  r rand()][clock 
66d0: 63 6c 69 63 6b 73 5d 22 0a 0a 09 09 09 09 73 65  clicks]"......se
66e0: 74 20 66 61 69 6c 65 64 20 30 0a 09 09 09 09 69  t failed 0.....i
66f0: 66 20 7b 5b 63 61 74 63 68 20 7b 0a 09 09 09 09  f {[catch {.....
6700: 09 66 69 6c 65 20 6d 6b 64 69 72 20 5b 66 69 6c  .file mkdir [fil
6710: 65 20 64 69 72 6e 61 6d 65 20 24 6c 6f 63 61 6c  e dirname $local
6720: 70 61 74 68 5d 0a 09 09 09 09 09 66 69 6c 65 20  path]......file 
6730: 63 6f 70 79 20 2d 66 6f 72 63 65 20 2d 2d 20 24  copy -force -- $
6740: 6c 6f 63 61 6c 63 61 63 68 65 66 69 6c 65 20 24  localcachefile $
6750: 74 6d 70 6c 6f 63 61 6c 70 61 74 68 0a 0a 09 09  tmplocalpath....
6760: 09 09 09 69 66 20 7b 24 70 6b 67 70 61 74 68 69  ...if {$pkgpathi
6770: 6e 66 6f 28 70 65 72 6d 73 29 20 3d 3d 20 22 78  nfo(perms) == "x
6780: 22 7d 20 7b 0a 09 09 09 09 09 09 66 69 6c 65 20  "} {.......file 
6790: 61 74 74 72 69 62 75 74 65 73 20 24 74 6d 70 6c  attributes $tmpl
67a0: 6f 63 61 6c 70 61 74 68 20 2d 70 65 72 6d 69 73  ocalpath -permis
67b0: 73 69 6f 6e 73 20 2b 78 0a 09 09 09 09 09 7d 0a  sions +x......}.
67c0: 0a 09 09 09 09 09 66 69 6c 65 20 72 65 6e 61 6d  ......file renam
67d0: 65 20 2d 66 6f 72 63 65 20 2d 2d 20 24 74 6d 70  e -force -- $tmp
67e0: 6c 6f 63 61 6c 70 61 74 68 20 24 6c 6f 63 61 6c  localpath $local
67f0: 70 61 74 68 0a 09 09 09 09 7d 20 65 72 72 5d 7d  path.....} err]}
6800: 20 7b 0a 09 09 09 09 09 73 65 74 20 66 61 69 6c   {......set fail
6810: 65 64 20 31 0a 09 09 09 09 7d 0a 09 09 09 09 63  ed 1.....}.....c
6820: 61 74 63 68 20 7b 0a 09 09 09 09 09 66 69 6c 65  atch {......file
6830: 20 64 65 6c 65 74 65 20 2d 66 6f 72 63 65 20 2d   delete -force -
6840: 2d 20 24 74 6d 70 6c 6f 63 61 6c 70 61 74 68 0a  - $tmplocalpath.
6850: 09 09 09 09 7d 0a 09 09 09 7d 0a 0a 09 09 09 69  ....}....}.....i
6860: 66 20 7b 24 66 61 69 6c 65 64 7d 20 7b 0a 09 09  f {$failed} {...
6870: 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65  ..return -code e
6880: 72 72 6f 72 20 24 65 72 72 0a 09 09 09 7d 0a 0a  rror $err....}..
6890: 09 09 09 72 65 74 75 72 6e 20 24 6c 6f 63 61 6c  ...return $local
68a0: 70 61 74 68 0a 09 09 7d 0a 0a 09 09 72 65 74 75  path...}....retu
68b0: 72 6e 20 24 6c 6f 63 61 6c 63 61 63 68 65 66 69  rn $localcachefi
68c0: 6c 65 0a 09 7d 0a 0a 09 70 72 6f 63 20 6c 6f 63  le..}...proc loc
68d0: 61 6c 70 61 74 68 20 7b 70 61 74 68 7d 20 7b 0a  alpath {path} {.
68e0: 09 09 61 72 72 61 79 20 73 65 74 20 70 61 74 68  ..array set path
68f0: 69 6e 66 6f 20 5b 5f 70 61 72 73 65 70 61 74 68  info [_parsepath
6900: 20 24 70 61 74 68 5d 0a 0a 09 09 69 66 20 7b 24   $path]....if {$
6910: 70 61 74 68 69 6e 66 6f 28 5f 74 79 70 65 29 20  pathinfo(_type) 
6920: 21 3d 20 22 66 69 6c 65 73 22 7d 20 7b 0a 09 09  != "files"} {...
6930: 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72  .return -code er
6940: 72 6f 72 20 22 69 6e 76 61 6c 69 64 20 70 61 74  ror "invalid pat
6950: 68 20 74 79 70 65 3a 20 47 6f 74 20 5c 22 24 70  h type: Got \"$p
6960: 61 74 68 69 6e 66 6f 28 5f 74 79 70 65 29 5c 22  athinfo(_type)\"
6970: 2c 20 6e 65 65 64 20 5c 22 66 69 6c 65 73 5c 22  , need \"files\"
6980: 22 0a 09 09 7d 0a 0a 09 09 73 65 74 20 6c 6f 63  "...}....set loc
6990: 61 6c 70 61 74 68 20 5b 5f 6c 6f 63 61 6c 70 61  alpath [_localpa
69a0: 74 68 20 24 70 61 74 68 69 6e 66 6f 28 70 61 63  th $pathinfo(pac
69b0: 6b 61 67 65 29 20 24 70 61 74 68 69 6e 66 6f 28  kage) $pathinfo(
69c0: 68 6f 73 74 6e 61 6d 65 29 20 24 70 61 74 68 69  hostname) $pathi
69d0: 6e 66 6f 28 66 69 6c 65 29 5d 0a 0a 09 09 72 65  nfo(file)]....re
69e0: 74 75 72 6e 20 24 6c 6f 63 61 6c 70 61 74 68 0a  turn $localpath.
69f0: 09 7d 0a 0a 09 70 72 6f 63 20 65 78 69 73 74 73  .}...proc exists
6a00: 20 7b 70 61 74 68 7d 20 7b 0a 09 09 63 61 74 63   {path} {...catc
6a10: 68 20 7b 0a 09 09 09 73 65 74 20 69 6e 66 6f 20  h {....set info 
6a20: 5b 67 65 74 61 74 74 72 20 24 70 61 74 68 5d 0a  [getattr $path].
6a30: 09 09 7d 20 65 72 72 0a 0a 09 09 69 66 20 7b 21  ..} err....if {!
6a40: 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 69 6e 66  [info exists inf
6a50: 6f 5d 7d 20 7b 0a 09 09 09 69 66 20 7b 24 65 72  o]} {....if {$er
6a60: 72 20 3d 3d 20 22 4e 6f 20 73 75 63 68 20 66 69  r == "No such fi
6a70: 6c 65 20 6f 72 20 64 69 72 65 63 74 6f 72 79 22  le or directory"
6a80: 7d 20 7b 0a 09 09 09 09 72 65 74 75 72 6e 20 5b  } {.....return [
6a90: 6c 69 73 74 5d 0a 09 09 09 7d 20 65 6c 73 65 20  list]....} else 
6aa0: 7b 0a 09 09 09 09 72 65 74 75 72 6e 20 2d 63 6f  {.....return -co
6ab0: 64 65 20 65 72 72 6f 72 20 24 65 72 72 0a 09 09  de error $err...
6ac0: 09 7d 0a 09 09 7d 0a 0a 09 09 72 65 74 75 72 6e  .}...}....return
6ad0: 20 24 69 6e 66 6f 0a 09 7d 0a 0a 09 70 72 6f 63   $info..}...proc
6ae0: 20 70 72 65 70 61 72 65 5f 74 6f 5f 63 72 65 61   prepare_to_crea
6af0: 74 65 20 7b 70 61 74 68 20 7b 6d 75 73 74 5f 6e  te {path {must_n
6b00: 6f 74 5f 65 78 69 73 74 20 31 7d 7d 20 7b 0a 09  ot_exist 1}} {..
6b10: 09 69 66 20 7b 24 6d 75 73 74 5f 6e 6f 74 5f 65  .if {$must_not_e
6b20: 78 69 73 74 7d 20 7b 0a 09 09 09 69 66 20 7b 5b  xist} {....if {[
6b30: 65 78 69 73 74 73 20 24 70 61 74 68 5d 20 21 3d  exists $path] !=
6b40: 20 22 22 7d 20 7b 0a 09 09 09 09 72 65 74 75 72   ""} {.....retur
6b50: 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 46  n -code error "F
6b60: 69 6c 65 20 61 6c 72 65 61 64 79 20 65 78 69 73  ile already exis
6b70: 74 73 22 0a 09 09 09 7d 0a 09 09 7d 0a 0a 09 09  ts"....}...}....
6b80: 73 65 74 20 66 69 6c 65 6e 61 6d 65 20 5b 6c 6f  set filename [lo
6b90: 63 61 6c 70 61 74 68 20 24 70 61 74 68 5d 0a 0a  calpath $path]..
6ba0: 09 09 69 66 20 7b 24 66 69 6c 65 6e 61 6d 65 20  ..if {$filename 
6bb0: 3d 3d 20 22 22 7d 20 7b 0a 09 09 09 72 65 74 75  == ""} {....retu
6bc0: 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22  rn -code error "
6bd0: 41 73 6b 65 64 20 74 6f 20 63 72 65 61 74 65 2c  Asked to create,
6be0: 20 62 75 74 20 6e 6f 20 68 6f 6d 65 20 64 69 72   but no home dir
6bf0: 65 63 74 6f 72 79 2e 22 0a 09 09 7d 0a 0a 09 09  ectory."...}....
6c00: 73 65 74 20 64 69 72 6e 61 6d 65 20 5b 66 69 6c  set dirname [fil
6c10: 65 20 64 69 72 6e 61 6d 65 20 24 66 69 6c 65 6e  e dirname $filen
6c20: 61 6d 65 5d 0a 0a 09 09 5f 61 73 5f 75 73 65 72  ame]...._as_user
6c30: 20 7b 0a 09 09 09 66 69 6c 65 20 6d 6b 64 69 72   {....file mkdir
6c40: 20 24 64 69 72 6e 61 6d 65 0a 09 09 7d 0a 0a 09   $dirname...}...
6c50: 09 72 65 74 75 72 6e 20 24 66 69 6c 65 6e 61 6d  .return $filenam
6c60: 65 0a 09 7d 0a 0a 09 70 72 6f 63 20 75 6e 6c 69  e..}...proc unli
6c70: 6e 6b 70 61 74 68 20 7b 70 61 74 68 7d 20 7b 0a  nkpath {path} {.
6c80: 09 09 61 72 72 61 79 20 73 65 74 20 70 61 74 68  ..array set path
6c90: 61 74 74 72 73 20 5b 65 78 69 73 74 73 20 24 70  attrs [exists $p
6ca0: 61 74 68 5d 0a 0a 09 09 69 66 20 7b 24 70 61 74  ath]....if {$pat
6cb0: 68 61 74 74 72 73 28 70 61 74 68 5f 74 79 70 65  hattrs(path_type
6cc0: 29 20 21 3d 20 22 66 69 6c 65 73 22 7d 20 7b 0a  ) != "files"} {.
6cd0: 09 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20  ...return -code 
6ce0: 65 72 72 6f 72 20 22 69 6e 76 61 6c 69 64 20 70  error "invalid p
6cf0: 61 74 68 20 74 79 70 65 3a 20 63 61 6e 20 6f 6e  ath type: can on
6d00: 6c 79 20 64 65 6c 65 74 65 20 74 79 70 65 20 5c  ly delete type \
6d10: 22 66 69 6c 65 73 5c 22 20 74 68 69 73 20 69 73  "files\" this is
6d20: 20 74 79 70 65 20 5c 22 24 70 61 74 68 61 74 74   type \"$pathatt
6d30: 72 73 28 70 61 74 68 5f 74 79 70 65 29 5c 22 22  rs(path_type)\""
6d40: 0a 09 09 7d 0a 0a 09 09 73 65 74 20 6c 6f 63 61  ...}....set loca
6d50: 6c 70 61 74 68 20 24 70 61 74 68 61 74 74 72 73  lpath $pathattrs
6d60: 28 6c 6f 63 61 6c 70 61 74 68 29 0a 0a 09 09 69  (localpath)....i
6d70: 66 20 7b 24 6c 6f 63 61 6c 70 61 74 68 20 3d 3d  f {$localpath ==
6d80: 20 22 22 7d 20 7b 0a 09 09 09 72 65 74 75 72 6e   ""} {....return
6d90: 20 2d 63 6f 64 65 20 65 72 72 6f 72 20 22 41 73   -code error "As
6da0: 6b 65 64 20 74 6f 20 64 65 6c 65 74 65 2c 20 62  ked to delete, b
6db0: 75 74 20 6e 6f 20 68 6f 6d 65 20 64 69 72 65 63  ut no home direc
6dc0: 74 6f 72 79 2e 22 0a 09 09 7d 0a 0a 09 09 69 66  tory."...}....if
6dd0: 20 7b 5b 69 6e 66 6f 20 65 78 69 73 74 73 20 70   {[info exists p
6de0: 61 74 68 61 74 74 72 73 28 69 73 5f 6c 6f 63 61  athattrs(is_loca
6df0: 6c 66 69 6c 65 29 5d 7d 20 7b 0a 09 09 09 69 66  lfile)]} {....if
6e00: 20 7b 5b 66 69 6c 65 20 69 73 64 69 72 65 63 74   {[file isdirect
6e10: 6f 72 79 20 24 6c 6f 63 61 6c 70 61 74 68 5d 7d  ory $localpath]}
6e20: 20 7b 0a 09 09 09 09 73 65 74 20 63 68 69 6c 64   {.....set child
6e30: 72 65 6e 20 5b 67 65 74 63 68 69 6c 64 72 65 6e  ren [getchildren
6e40: 20 24 70 61 74 68 5d 0a 0a 09 09 09 09 69 66 20   $path]......if 
6e50: 7b 5b 6c 6c 65 6e 67 74 68 20 24 63 68 69 6c 64  {[llength $child
6e60: 72 65 6e 5d 20 21 3d 20 30 7d 20 7b 0a 09 09 09  ren] != 0} {....
6e70: 09 09 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65  ..return -code e
6e80: 72 72 6f 72 20 22 41 73 6b 65 64 20 74 6f 20 64  rror "Asked to d
6e90: 65 6c 65 74 65 20 6e 6f 6e 2d 65 6d 70 74 79 20  elete non-empty 
6ea0: 64 69 72 65 63 74 6f 72 79 22 0a 09 09 09 09 7d  directory".....}
6eb0: 0a 09 09 09 7d 0a 0a 09 09 09 5f 61 73 5f 75 73  ....}....._as_us
6ec0: 65 72 20 7b 0a 09 09 09 09 66 69 6c 65 20 64 65  er {.....file de
6ed0: 6c 65 74 65 20 2d 66 6f 72 63 65 20 2d 2d 20 24  lete -force -- $
6ee0: 6c 6f 63 61 6c 70 61 74 68 0a 09 09 09 7d 0a 09  localpath....}..
6ef0: 09 7d 20 65 6c 73 65 69 66 20 7b 5b 69 6e 66 6f  .} elseif {[info
6f00: 20 65 78 69 73 74 73 20 70 61 74 68 61 74 74 72   exists pathattr
6f10: 73 28 69 73 5f 72 65 6d 6f 74 65 66 69 6c 65 29  s(is_remotefile)
6f20: 5d 7d 20 7b 0a 09 09 09 69 66 20 7b 24 70 61 74  ]} {....if {$pat
6f30: 68 61 74 74 72 73 28 74 79 70 65 29 20 3d 3d 20  hattrs(type) == 
6f40: 22 64 69 72 65 63 74 6f 72 79 22 7d 20 7b 0a 09  "directory"} {..
6f50: 09 09 09 73 65 74 20 63 68 69 6c 64 72 65 6e 20  ...set children 
6f60: 5b 67 65 74 63 68 69 6c 64 72 65 6e 20 24 70 61  [getchildren $pa
6f70: 74 68 5d 0a 0a 09 09 09 09 69 66 20 7b 5b 6c 6c  th]......if {[ll
6f80: 65 6e 67 74 68 20 24 63 68 69 6c 64 72 65 6e 5d  ength $children]
6f90: 20 21 3d 20 30 7d 20 7b 0a 09 09 09 09 09 72 65   != 0} {......re
6fa0: 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72 6f 72  turn -code error
6fb0: 20 22 41 73 6b 65 64 20 74 6f 20 64 65 6c 65 74   "Asked to delet
6fc0: 65 20 6e 6f 6e 2d 65 6d 70 74 79 20 64 69 72 65  e non-empty dire
6fd0: 63 74 6f 72 79 22 0a 09 09 09 09 7d 0a 09 09 09  ctory".....}....
6fe0: 7d 0a 09 09 7d 20 65 6c 73 65 20 7b 0a 09 09 09  }...} else {....
6ff0: 72 65 74 75 72 6e 20 2d 63 6f 64 65 20 65 72 72  return -code err
7000: 6f 72 20 22 55 6e 6b 6e 6f 77 6e 20 69 66 20 66  or "Unknown if f
7010: 69 6c 65 20 69 73 20 72 65 6d 6f 74 65 20 6f 72  ile is remote or
7020: 20 6c 6f 63 61 6c 20 21 3f 22 0a 09 09 7d 0a 0a   local !?"...}..
7030: 09 09 73 65 74 20 77 68 69 74 65 6f 75 74 66 69  ..set whiteoutfi
7040: 6c 65 20 24 70 61 74 68 61 74 74 72 73 28 77 68  le $pathattrs(wh
7050: 69 74 65 6f 75 74 70 61 74 68 29 0a 09 09 73 65  iteoutpath)...se
7060: 74 20 77 68 69 74 65 6f 75 74 64 69 72 20 5b 66  t whiteoutdir [f
7070: 69 6c 65 20 64 69 72 6e 61 6d 65 20 24 77 68 69  ile dirname $whi
7080: 74 65 6f 75 74 66 69 6c 65 5d 0a 0a 09 09 5f 61  teoutfile]...._a
7090: 73 5f 75 73 65 72 20 7b 0a 09 09 09 66 69 6c 65  s_user {....file
70a0: 20 6d 6b 64 69 72 20 24 77 68 69 74 65 6f 75 74   mkdir $whiteout
70b0: 64 69 72 0a 09 09 09 63 6c 6f 73 65 20 5b 6f 70  dir....close [op
70c0: 65 6e 20 24 77 68 69 74 65 6f 75 74 66 69 6c 65  en $whiteoutfile
70d0: 20 77 5d 0a 09 09 7d 0a 09 7d 0a 7d 0a            w]...}..}.}.