UNPKG

jsdav-ext

Version:

jsDAV allows you to easily add WebDAV support to a NodeJS application. jsDAV is meant to cover the entire standard, and attempts to allow integration using an easy to understand API.

264 lines (243 loc) 8.93 kB
/* * @package jsDAV * @subpackage DAV * @copyright Copyright(c) 2011 Ajax.org B.V. <info AT ajax DOT org> * @author Mike de Boer <info AT mikedeboer DOT nl> * @license http://github.com/mikedeboer/jsDAV/blob/master/LICENSE MIT License */ "use strict"; var Base = require("./../shared/base"); // interfaces to check for: var jsDAV_iFile = require("./interfaces/iFile"); var jsDAV_iCollection = require("./interfaces/iCollection"); var jsDAV_iProperties = require("./interfaces/iProperties"); var Exc = require("./../shared/exceptions"); var Util = require("./../shared/util"); var Path = require("path"); /** * Abstract tree object */ var jsDAV_Tree = module.exports = Base.extend({ /** * Chunk of the path that is part of the sandbox * * @var string */ sandbox: null, /** * Set the path that needs to be stripped from the real path when presented * to the user/ client and to check if a path from a request is within this * path to prevent operations to files and directories that are outside of * this sandbox * * @param {string} path * @return void */ setSandbox: function(path) { this.sandbox = path.replace(/[\/]+$/, ""); }, /** * Strips the sandbox part from the path. * * @var {string} path * @return string */ stripSandbox: function(path) { if (!this.sandbox) return path; var idx = path.indexOf(this.sandbox); if (idx === -1) return path; // remove the sandbox path from full path, usually at the start of the string return path.substr(idx + this.sandbox.length); }, /** * Check whether the provided path is located inside the sandbox * * @param {string} path * @return boolean */ insideSandbox: function(path) { if (!this.sandbox) return true; // if the relative path FROM the sandbox directory TO the requested path // is outside of the sandbox directory, the result of Path.relative() will // start with '../', which we can trap and use: return Path.relative(this.sandbox, path).indexOf("../") !== 0; }, /** * This function must return an iNode object for a path * If a Path doesn't exist, thrown an Exc.FileNotFound * * @param {String} path * @throws Exception_FileNotFound * @return jsDAV_iNode */ getNodeForPath: function(path, cbgetnodefp) {}, /** * Copies a file from path to another * * @param {string} sourcePath The source location * @param {string} destinationPath The full destination path * @return void */ copy: function(sourcePath, destinationPath, cbcopytree) { var self = this; if (!this.insideSandbox(destinationPath)) { return cbcopytree(new Exc.Forbidden("You are not allowed to copy to " + this.stripSandbox(destinationPath))); } this.getNodeForPath(sourcePath, function(err, sourceNode) { if (err) return cbcopytree(err); // grab the dirname and basename components var parts = Util.splitPath(destinationPath); var destinationDir = parts[0]; var destinationName = parts[1]; self.getNodeForPath(destinationDir, function(err, destinationParent) { if (err) return cbcopytree(err); self.copyNode(sourceNode, destinationParent, destinationName, function(err) { if (err) return cbcopytree(err); cbcopytree(); }); }); }); }, /** * Moves a file from one location to another * * @param {string} sourcePath The path to the file which should be moved * @param {string} destinationPath The full destination path, so not just the destination parent node * @return int */ move: function(sourcePath, destinationPath, cbmovetree) { var parts = Util.splitPath(sourcePath); var sourceDir = parts[0]; var sourceName = parts[1]; parts = Util.splitPath(destinationPath); if (!this.insideSandbox(destinationPath)) { return cbmovetree(new Exc.Forbidden("You are not allowed to move to " + this.stripSandbox(destinationPath))); } var destinationDir = parts[0]; var destinationName = parts[1]; if (sourceDir === destinationDir) { this.getNodeForPath(sourcePath, function(err, renameable) { if (err) return cbmovetree(err); renameable.setName(destinationName, onDone); }); } else { var self = this; this.copy(sourcePath, destinationPath, function(err) { if (err) return cbmovetree(err); self.getNodeForPath(sourcePath, function(err, sourceNode) { if (err) return cbmovetree(err); sourceNode.delete(onDone); }); }); } function onDone(err) { if (err) return cbmovetree(err); cbmovetree(); } }, /** * Deletes a node from the tree * * @param {String} path * @return void */ "delete": function(path, cbtreedelete) { var self = this; this.getNodeForPath(path, function(err, node) { if (err) return cbtreedelete(err); node.delete(function(err) { if (err) return cbtreedelete(err); self.markDirty(Util.splitPath(path)[0]); cbtreedelete(); }); }); }, /** * Returns a list of childnodes for a given path. * * @param {String} path * @return array */ getChildren: function(path, cbtreechildren) { this.getNodeForPath(path, function(err, node) { if (err) return cbtreechildren(err); node.getChildren(cbtreechildren); }); }, /** * copyNode * * @param {jsDAV_iNode} source * @param {jsDAV_iCollection} destination * @return void */ copyNode: function(source, destinationParent, destinationName, cbcopytreenode) { if (!destinationName) destinationName = source.getName(); var destination; var self = this; if (source.hasFeature(jsDAV_iFile)) { source.get(function(err, data) { if (err) return cbcopytreenode(err); destinationParent.createFile(destinationName, data, "binary", function(err) { if (err) return cbcopytreenode(err); destinationParent.getChild(destinationName, function(err, destination) { if (err) return cbcopytreenode(err); afterCopy(destination); }); }); }); } else if (source.hasFeature(jsDAV_iCollection)) { destinationParent.createDirectory(destinationName, function(err) { if (err) return cbcopytreenode(err); destinationParent.getChild(destinationName, function(err, destination) { if (err) return cbcopytreenode(err); var child; var c = source.getChildren(); var i = 0; var l = c.length; var error = null; var onError = function(err) { if (err) error = err; }; for (; i < l && !error; ++i) { child = c[i]; self.copyNode(child, destination, onError); } if (error) return cbcopytreenode(error); afterCopy(destination); }); }); } function afterCopy(destination) { if (source.hasFeature(jsDAV_iProperties) && destination.hasFeature(jsDAV_iProperties)) destination.updateProperties(source.getProperties({}), cbcopytreenode); else cbcopytreenode(); } } });