# Copyright 2017-2018 Xuan Sang LE # AnTOS Web desktop is is licensed under the GNU General Public # License v3.0, see the LICENCE file for more information # This program is free software: you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, either version 3 of # the License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # You should have received a copy of the GNU General Public License #along with this program. If not, see https://www.gnu.org/licenses/. String.prototype.asFileHandle = () -> list = @split "://" handles = Ant.OS.API.VFS.findHandles list[0] if not handles or handles.length is 0 Ant.OS.announcer.osfail __("VFS unknown handle: {0}", @), (Ant.OS.API.throwe "OS.VFS"), @ return null return new handles[0](@) this.OS.API.VFS = handles: { } register: ( protos, cls ) -> return Ant.OS.API.VFS.handles[protos] = cls # if typeof protos is "string" #Ant.OS.API.VFS.handles[v] = cls for v in protos findHandles: (proto) -> l = (v for k, v of Ant.OS.API.VFS.handles when proto.trim().match (new RegExp k , "g")) return l class BaseFileHandle constructor: (path) -> @dirty = false @cache = undefined @setPath path setPath: (p) -> @ready = false return unless p @path = p.toString() list = @path.split "://" @protocol = list[0] return unless list.length > 1 re = list[1].replace(/^\/+|\/+$/g, '') return if re is "" @genealogy = re.split("/") @basename = @genealogy[@genealogy.length - 1] unless @isRoot() @ext = @basename.split( "." ).pop() unless @basename.lastIndexOf(".") is 0 or @basename.indexOf( "." ) is -1 setCache: (v) -> @cache = v @ asFileHandle: () -> @ isRoot: () -> (not @genealogy) or (@genealogy.size is 0) child: (name) -> if @isRoot() return @path + name else return @path + "/" + name isHidden: () -> return false if not @basename @basename[0] is "." hash: () -> return -1 unless @path return @path.hash() b64: (t) -> # t is object or mime type new Promise (resolve, reject) => m = if t is "object" then "text/plain" else t return resolve "" unless @cache if t is "object" or typeof @cache is "string" if t is "object" b64 = JSON.stringify(@cache, undefined, 4).asBase64() else b64 = @cache.asBase64() b64 = "data:#{m};base64,#{b64}" resolve(b64) else reader = new FileReader() reader.readAsDataURL(@cache) reader.onload = () -> resolve reader.result reader.onerror = (e) -> reject e parent: () -> return @ if @isRoot() return (@protocol + "://" + (@genealogy.slice 0 , @genealogy.length - 1).join "/") .asFileHandle() onready: () -> # read meta data new Promise (resolve, reject) => return resolve(@info) if @ready @meta() .then (d) => return reject d if d.errors @info = d.result @ready = true resolve(d.result) .catch (e) -> reject e read: (t) -> new Promise (resolve, reject) => @onready() .then (r) => @_rd(t) .then (d) -> # Ant.OS.announcer.ostrigger "VFS", { m: "read", file: me } resolve d .catch (e) -> reject e .catch (e) -> reject e write: (t) -> new Promise (resolve, reject) => @_wr(t) .then (r) => Ant.OS.announcer.ostrigger "VFS", { m: "write", file: @ } resolve r .catch (e) -> reject e mk: (d) -> new Promise (resolve, reject) => @onready() .then (r) => @_mk(d) .then (d) => Ant.OS.announcer.ostrigger "VFS", { m: "mk", file: @ } resolve d .catch (e) -> reject e .catch (e) -> reject e remove: () -> new Promise (resolve, reject) => @onready() .then (r) => @_rm() .then (d) => Ant.OS.announcer.ostrigger "VFS", { m: "remove", file: @ } resolve d .catch (e) -> reject e .catch (e) -> reject e upload: () -> new Promise (resolve, reject) => @onready() .then (r) => @_up() .then (d) => Ant.OS.announcer.ostrigger "VFS", { m: "upload", file: @ } resolve d .catch (e) -> reject e .catch (e) -> reject e publish: () -> new Promise (resolve, reject) => @onready() .then (r) => @_pub() .then (d) => Ant.OS.announcer.ostrigger "VFS", { m: "publish", file: @ } resolve d .catch (e) -> reject e .catch (e) -> reject e download: () -> new Promise (resolve, reject) => @onready() .then (r) => @_down() .then (d) => Ant.OS.announcer.ostrigger "VFS", { m: "download", file: @ } resolve d .catch (e) -> reject e .catch (e) -> reject e move: (d) -> new Promise (resolve, reject) => @onready() .then (r) => @_mv(d) .then (data) => Ant.OS.announcer.ostrigger "VFS", { m: "move", file: @ } resolve data .catch (e) -> reject e .catch (e) -> reject e execute: () -> new Promise (resolve, reject) => @onready() .then (r) => @_exec() .then (d) => Ant.OS.announcer.ostrigger "VFS", { m: "execute", file: @ } resolve d .catch (e) -> reject e .catch (e) -> reject e getlink: () -> @path unsupported: (t) -> new Promise (resolve, reject) => reject { error: __("Action {0} is unsupported on: {1}", t, @path) } # actions must be implemented by subclasses _rd: (t) -> @unsupported "read" _wr: (d, t) -> @unsupported "write" _mk: (d) -> @unsupported "mk" _rm: () -> @unsupported "remove" _mv: (d) -> @unsupported "move" _up: () -> @unsupported "upload" _down: () -> @unsupported "download" _exec: () -> @unsupported "execute" _pub: () -> @unsupported "publish" # now export the class Ant.OS.API.VFS.BaseFileHandle = BaseFileHandle # Remote file handle class RemoteFileHandle extends Ant.OS.API.VFS.BaseFileHandle constructor: (path) -> super path meta: () -> Ant.OS.API.handle.fileinfo @path getlink: () -> Ant.OS.API.handle.get + "/" + @path _rd: (t) -> # t: binary, text, any type if not @info return new Promise (resolve, reject) => reject Ant.OS.API.throwe __( "file meta-data not found: {0}", @path) return Ant.OS.API.handle.scandir @path if @info.type is "dir" #read the file return Ant.OS.API.handle.fileblob @path if t is "binary" Ant.OS.API.handle.readfile @path, if t then t else "text" _wr: (t) -> # t is base64 or undefined return Ant.OS.API.handle.write @path, @cache if t is "base64" new Promise (resolve, reject) => @b64(t) .then (r) => Ant.OS.API.handle.write @path, r .then (result) -> resolve result .catch (e) -> reject e .catch (e) -> reject e _mk: (d) -> if not @info return new Promise (resolve, reject) => reject Ant.OS.API.throwe __( "file meta-data not found: {0}", @path) if @info.type is "file" return new Promise (resolve, reject) => reject Ant.OS.API.throwe __("{0} is not a directory", @path) Ant.OS.API.handle.mkdir "#{@path}/#{d}" _rm: () -> Ant.OS.API.handle.delete @path _mv: (d) -> Ant.OS.API.handle.move @path, d _up: () -> if @info.type isnt "dir" return new Promise (resolve, reject) => reject Ant.OS.API.throwe __("{0} is not a file", @path) Ant.OS.API.handle.upload @path _down: () -> new Promise (resolve, reject) => if @info.type is "dir" return Ant.OS.API.throwe __("{0} is not a file", @path) Ant.OS.API.handle.fileblob(@path) .then (d) => blob = new Blob [d], { type: "octet/stream" } Ant.OS.API.saveblob @basename, blob resolve() .catch (e) -> reject e _pub: () -> Ant.OS.API.handle.sharefile @path, true Ant.OS.API.VFS.register "^(home|desktop|os|Untitled)$", RemoteFileHandle # Application Handle class ApplicationHandle extends Ant.OS.API.VFS.BaseFileHandle constructor: (path) -> super path @info = Ant.OS.setting.system.packages[@basename] if @basename @ready = true _rd: (t) -> new Promise (resolve, reject) => return resolve { result: @info } if @info return reject Ant.OS.API.throwe(__("Application meta data isnt found")) unless @isRoot() resolve { result: ( v for k, v of Ant.OS.setting.system.packages ) } Ant.OS.API.VFS.register "^app$", ApplicationHandle class BufferFileHandle extends Ant.OS.API.VFS.BaseFileHandle constructor: (path, mime, data) -> super path @cache = data if data @info = mime: mime path: path size: if data then data.length else 0 name: @basename type: "file" _rd: (t) -> new Promise (resolve, reject) => resolve { result: @cache } _wr: (d, t) -> @cache = d @onchange @ if @onchange new Promise (resolve, reject) -> resolve { result: true } _down: () -> new Promise (resolve, reject) => blob = new Blob [@cache], { type: "octet/stream" } Ant.OS.API.saveblob @basename, blob resolve() onchange: (f) -> @onchange = f Ant.OS.API.VFS.register "^mem$", BufferFileHandle class URLFileHandle extends Ant.OS.API.VFS.BaseFileHandle constructor: (path) -> super path @ready = true _rd: (t) -> Ant.OS.API.get @path, if t then t else "text" Ant.OS.API.VFS.register "^(http|https|ftp)$", URLFileHandle class SharedFileHandle extends Ant.OS.API.VFS.BaseFileHandle constructor: (path) -> super path @ready = true if @isRoot() meta: () -> Ant.OS.API.handle.fileinfo @path _rd: (t) -> return Ant.OS.API.get "#{Ant.OS.API.handle.shared}/all", t if @isRoot() #read the file return Ant.OS.API.handle.fileblob @path if t is "binary" Ant.OS.API.handle.readfile @path, if t then t else "text" _wr: (d, t) -> Ant.OS.API.handle.write @path, d _rm: () -> Ant.OS.API.handle.sharefile @basename, false _down: () -> new Promise (resolve, reject) => if @info.type is "dir" return reject Ant.OS.API.throwe __("{0} is not a file", @path) Ant.OS.API.handle.fileblob @path .then (data) => blob = new Blob [data], { type: "octet/stream" } Ant.OS.API.saveblob @basename, blob resolve() .catch (e) -> reject e _pub: () -> return new Promise (resolve, reject) => resolve { result: @basename } Ant.OS.API.VFS.register "^shared$", SharedFileHandle