# 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.asFileHandler = () -> list = @split "://" handlers = _API.VFS.findHandlers list[0] if not handlers or handlers.length is 0 _courrier.osfail __("VFS unknown handler: {0}", @), (_API.throwe "OS.VFS"), @ return null return new handlers[0](@) this.OS.API.VFS = handlers: { } register: ( protos, cls ) -> return self.OS.API.VFS.handlers[protos] = cls # if typeof protos is "string" #_API.VFS.handlers[v] = cls for v in protos findHandlers: (proto) -> l = (v for k, v of _API.VFS.handlers when proto.trim().match (new RegExp k , "g")) return l class BaseFileHandler 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 asFileHandler: () -> @ 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() sendB64: (m, f) -> me = @ return f "" unless @cache if typeof @cache is "string" b64 = @cache.asBase64() b64 = "data:#{m};base64,#{b64}" f(b64) else reader = new FileReader() reader.readAsDataURL(@cache) reader.onload = () -> f reader.result reader.onerror = (e) -> return _courrier.osfail __("VFS Cannot encode file: {0}", me.path), (_API.throwe "OS.VFS"), e parent: () -> return @ if @isRoot() return (@protocol + "://" + (@genealogy.slice 0 , @genealogy.length - 1).join "/") onready: (f, err) -> # read meta data return f() if @ready me = @ me.meta (d) -> if d.error return if err then err d else _courrier.osfail "#{me.path}: #{d.error}", (_API.throwe "OS.VFS"), d.error me.info = d.result me.ready = true f() read: (f, t) -> me = @ @onready (() -> me.action "read", t, f) write: (d, f) -> me = @ @action "write", d, (r) -> _courrier.ostrigger "VFS", { m: "write", file: me } if r.result f r mk: (d, f) -> me = @ @onready (() -> me.action "mk", d, (r) -> _courrier.ostrigger "VFS", { m: "mk", file: me } if r.result f r) remove: (f) -> me = @ @onready (() -> me.action "remove", null, (r) -> _courrier.ostrigger "VFS", { m: "remove", file: me } if r.result f r) upload: (f) -> me = @ @onready (() -> me.action "upload", null, (r) -> _courrier.ostrigger "VFS", { m: "upload", file: me } if r.result f r) publish: (f) -> me = @ @onready (() -> me.action "publish", null, (r) -> _courrier.ostrigger "VFS", { m: "publish", file: me } if r.result f r) download: (f) -> me = @ @onready (() -> me.action "download", null, f) move: (d, f) -> me = @ @onready (() -> me.action "move", d, (r) -> _courrier.ostrigger "VFS", { m: "move", file: d.asFileHandler() } if r.result f r) execute: (f) -> me = @ @onready (() -> me.action "execute", null, f) #mk: (f) -> meta: (f) -> # for main action read, write, remove, execute # must be implemented by subclasses action: (n, p, f) -> return _courrier.osfail __("VFS unknown action: {0}", n), (_API.throwe "OS.VFS"), n # now export the class self.OS.API.VFS.BaseFileHandler = BaseFileHandler # Remote file handle class RemoteFileHandler extends self.OS.API.VFS.BaseFileHandler constructor: (path) -> super path meta: (f) -> _API.handler.fileinfo @path, f action: (n, p, f) -> me = @ switch n when "read" return _API.handler.scandir @path, f if @info.type is "dir" #read the file return _API.handler.fileblob @path, f if p is "binary" _API.handler.readfile @path, f, if p then p else "text" when "mk" return f { error: __("{0} is not a directory", @path) } if @info.type is "file" _API.handler.mkdir "#{@path}/#{p}", f when "write" return _API.handler.write me.path, me.cache, f if p is "base64" @sendB64 p, (data) -> _API.handler.write me.path, data, f when "upload" return if @info.type is "file" _API.handler.upload @path, f when "remove" _API.handler.delete @path, f when "publish" _API.handler.sharefile @path, true , f when "download" return if @info.type is "dir" _API.handler.fileblob @path, (d) -> blob = new Blob [d], { type: "octet/stream" } _API.saveblob me.basename, blob when "move" _API.handler.move @path, p, f else return _courrier.osfail __("VFS unknown action: {0}", n), (_API.throwe "OS.VFS"), n self.OS.API.VFS.register "^(home|desktop|os|Untitled)$", RemoteFileHandler # Application Handler class ApplicationHandler extends self.OS.API.VFS.BaseFileHandler constructor: (path) -> super path @info = _OS.setting.system.packages[@basename] if @basename @ready = true meta: (f) -> f() action: (n, p, f) -> me = @ switch n when "read" return f { result: @info } if @info return unless @isRoot() f { result: ( v for k, v of _OS.setting.system.packages ) } when "mk" return when "write" return when "upload" # install return when "remove" #uninstall return when "publish" return when "download" return when "move" return else return _courrier.osfail __("VFS unknown action: {0}", n), (_API.throwe "OS.VFS"), n self.OS.API.VFS.register "^app$", ApplicationHandler class BufferFileHandler extends self.OS.API.VFS.BaseFileHandler 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" meta: (f) -> f() onchange: (f) -> @onchange = f action: (n, p, f) -> me = @ switch n when "read" return f { result: @cache } when "mk" return when "write" @cache = p @onchange @ if @onchange f { result: true } when "upload" # install return when "remove" #uninstall return when "publish" return when "download" blob = new Blob [@cache], { type: "octet/stream" } _API.saveblob me.basename, blob when "move" return else return _courrier.osfail __("VFS unknown action: {0}", n), (_API.throwe "OS.VFS"), n self.OS.API.VFS.register "^mem$", BufferFileHandler class URLFileHandler extends self.OS.API.VFS.BaseFileHandler constructor: (path) -> super path @ready = true meta: (f) -> f { result: true } action: (n, p, f) -> me = @ switch n when "read" _API.get @path, (d) -> f(d) , (e, s) -> _courrier.oserror __("VFS cannot read : {0}", me.path), e, s , if p then p else "text" else return _courrier.oserror __("VFS unknown action: {0}", n), (_API.throwe "OS.VFS"), n self.OS.API.VFS.register "^(http|https)$", URLFileHandler class SharedFileHandler extends self.OS.API.VFS.BaseFileHandler constructor: (path) -> super path @ready = true if @isRoot() meta: (f) -> _API.handler.fileinfo @path, f action: (n, p, f) -> me = @ switch n when "read" return _API.get "#{_API.handler.shared}/all", f, ((e, s)->) if @isRoot() #read the file return _API.handler.fileblob @path, f if p is "binary" _API.handler.readfile @path, f, if p then p else "text" when "mk" return when "write" _API.handler.write @path, p, f when "remove" _API.handler.sharefile @basename, false, f when "upload" return when "publish" return f { result: @basename } when "download" return if @info.type is "dir" _API.handler.fileblob @path, (d) -> blob = new Blob [d], { type: "octet/stream" } _API.saveblob me.basename, blob when "move" return else return _courrier.osfail __("VFS unknown action: {0}", n), (_API.throwe "OS.VFS"), n self.OS.API.VFS.register "^shared$", SharedFileHandler