Patch title: Release 94 bulk changes
Abstract:
File: /pliant/util/crypto/synchronize.pli
Key:
    Removed line
    Added line
module "/pliant/language/unsafe.pli"
module "/pliant/admin/file.pli"
module "/pliant/language/stream/filesystembase.pli"

module "/pliant/protocol/http/server.pli"
module "/pliant/protocol/http/style/default.style"

module "/pliant/fullpliant/user.pli"
module "/pliant/admin/md5.pli"
module "/pliant/language/type/text/str8.pli"
module "/pliant/util/crypto/cipher.pli"
module "/pliant/util/crypto/channel.pli"
module "/pliant/protocol/http/client.pli"
module "/pliant/protocol/dns/name.pli"
module "/pliant/protocol/http/site.pli"


function path_concat path subpath -> fullpath
  arg Str path subpath fullpath
  if (path parse (var Str base) any:(var Str options))
    fullpath := (string base+subpath)+" "+options
  else
    fullpath := path+subpath

method page file_tree_synchronize src_path dest_path options -> status
  arg_rw HtmlPage page ; arg Str src_path dest_path options ; arg Status status
  implicit page

    var Array:FileInfo src_files
    if (src_path parse (var Str src_url) any:(var Str src_options))
      src_files := file_list src_url src_options extended+recursive+relative pliant_default_file_system
    else
      src_files := file_list src_path extended+recursive+relative
    var (Dictionary Str FileInfo) src_dict
    for (var Int i) 0 src_files:size-1
      src_dict insert src_files:i:name src_files:i
  
    var Array:FileInfo dest_files
    if (dest_path parse (var Str dest_url) any:(var Str dest_options))
      dest_files := file_list dest_url dest_options extended+recursive+relative pliant_default_file_system
    else
      dest_files := file_list dest_path extended+recursive+relative
    var (Dictionary Str FileInfo) dest_dict
    for (var Int i) 0 dest_files:size-1
      dest_dict insert dest_files:i:name dest_files:i
  
    if (options option "simulate")
  
      var Int ok_count := 0
      var Int new_count := 0
      var Int changed_count := 0
      var Int deleted_count := 0
      var Int touched_count := 0
      var Intn total := 0
      fixed
        for (var Int i) 0 src_files:size-1
          var Pointer:FileInfo s :> src_files i
          var Pointer:FileInfo d :> dest_dict first s:name
          if not exists:d
            text "+ "+s:name+"[lf]"
            new_count += 1 ; total += s size
          eif d:size=s:size and d:datetime<>s:datetime and (options option "touched")
            var Str temp := file_temporary
            if (file_copy (path_concat src_path s:name) temp reduced)=success and file_md5_hexa_signature:(path_concat dest_path d:name)=file_md5_hexa_signature:temp
              text "= "+s:name+"[lf]"
              touched_count += 1 ; total += s size
            else
              text "* "+s:name+"[lf]"
              changed_count += 1 ; total += s size
            file_delete temp
          eif d:size<>s:size or d:datetime<>s:datetime
            text "* "+s:name+"[lf]"
            changed_count += 1 ; total += s size
          else
            ok_count += 1
        for (var Int i) 0 dest_files:size-1
          var Pointer:FileInfo d :> dest_files i
          var Pointer:FileInfo s :> src_dict first d:name
          if not exists:s
            text "- "+d:name+"[lf]"
            deleted_count += 1
        if not (options option "nodelete")
          for (var Int i) 0 dest_files:size-1
            var Pointer:FileInfo d :> dest_files i
            var Pointer:FileInfo s :> src_dict first d:name
            if not exists:s
              text "- "+d:name+"[lf]"
              deleted_count += 1
      para
        text string:ok_count+" file(s) up to date." ; eol
        if changed_count>0
          text string:changed_count+" modified file(s)." ; eol
        if new_count>0
          text string:new_count+" new file(s)." ; eol
        if touched_count>0
          text string:touched_count+" touched file(s)." ; eol
        if total>=10*2^20
          text (string total\2^20)+" MB to send." ; eol
        eif total>0
          text (string total\2^10)+" KB to send." ; eol
      status := success
  
    else
  
      var Intn total := 0
      for (var Int i) 0 src_files:size-1
        var Pointer:FileInfo s :> src_files i
        var Pointer:FileInfo d :> dest_dict first s:name
        if not exists:d or d:size<>s:size or d:datetime<>s:datetime
          total += s size
      fixed
        status := success
        var Intn done := 0
        for (var Int i) 0 src_files:size-1
          var Pointer:FileInfo s :> src_files i
          var Pointer:FileInfo d :> dest_dict first s:name
          if not exists:d
            text "+ "+s:name
            var FileInfo d2 := file_query (path_concat dest_path s:name) extended
            s name := path_concat src_path s:name
            if (file_copy s d2 extended)=failure
              status := failure ; text " FAILED"
            done += s size ; text "  "+(string 100*done\total)+"%[lf]"
            flush
          eif d:size<>s:size or d:datetime<>s:datetime
            text "* "+s:name
            s name := path_concat src_path s:name
            d name := path_concat dest_path d:name
            if (file_copy s d extended)=failure
              status := failure ; text " FAILED"
            done += s size ; text "  "+(string 100*done\total)+"%[lf]"
            flush
        for (var Int i) 0 dest_files:size-1
          var Pointer:FileInfo d :> dest_files i
          var Pointer:FileInfo s :> src_dict first d:name
          if not exists:s
            text "- "+d:name
            if file_delete:(path_concat dest_path d:name)=failure
              status := failure ; text " FAILED"
            eol
            flush
        if not (options option "nodelete")
          for (var Int i) 0 dest_files:size-1
            var Pointer:FileInfo d :> dest_files i
            var Pointer:FileInfo s :> src_dict first d:name
            if not exists:s
              text "- "+d:name
              if file_delete:(path_concat dest_path d:name)=failure
                status := failure ; text " FAILED"
              eol
              flush


function file_url site path user password -> url
  arg Str site path user password url
  if site<>""
    if not (site parse any:(var Str host) ":" (var Int port))
      host := site
      var Data:NameHost h :> name_database:data:host site
      if exists:h
        port := h http_port
      else
        var Data:Site s :> site_database:data:site site
        if exists:s and s:port=defined
          port := s port
        else
          port := 80
    if (path parse "/" any:(var Str subpath))
      url := "http://"+host+"/pliant/browse/file/"+subpath
    eif (path parse "file:/" any:(var Str subpath))
      url := "http://"+host+"/pliant/browse/system_file/"+subpath
    eif (path parse "embedded:/" any:(var Str subpath))
      url := "http://"+host+"/pliant/browse/system_file/debian/"+subpath
    else
      return ""
    url := string:url+" channel "+(string "channel://"+host+"/client/"+(string port+500)+"/"+host+"/"+user+"/"+password)
  else
    url := path
  
method page file_tree_synchronize src_site src_path dest_site dest_path user password options -> status
  arg_rw HtmlPage page ; arg Str src_site src_path dest_site dest_path user password options ; arg Status status
  if user_secret_database:data:user:user:key_md5<>"" and user_secret_database:data:user:user:key_md5<>string_md5_hexa_signature:(uncipher user_secret_database:data:user:user:private_key password)
  if user_secret_database:data:user:user:key_md5<>"" and user_secret_database:data:user:user:key_md5<>string_md5_hexa_signature:(uncipher (straight_to_Str user_secret_database:data:user:user:private_key) password)
    page text "Password for '"+user+"' is wrong."
    return failure
  var Str src_url := file_url src_site src_path user password
  if src_url=""
    page text "I don't know how to access "+src_path+" on "+src_site
    return failure
  var Str dest_url := file_url dest_site dest_path user password
  if dest_url=""
    page text "I don't know how to access "+dest_path+" on "+dest_site
    return failure
  status := page file_tree_synchronize src_url dest_url options


export '. file_tree_synchronize' file_url