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.
236 lines (206 loc) • 7.76 kB
JavaScript
/*
* @package jsDAV
* @subpackage DAV
* @copyright Copyright(c) 2011 Ajax.org B.V. <info AT ajax DOT org>
* @author Sergi Mansilla <sergi AT ajax DOT org>
* @license http://github.com/mikedeboer/jsDAV/blob/master/LICENSE MIT License
*/
"use strict";
var jsDAV_Tree = require("./../../tree");
var jsDAV_Ftp_Directory = require("./directory");
var jsDAV_Ftp_File = require("./file");
var jsDav_iCollection = require("./../../interfaces/iCollection");
var Path = require("path");
var Ftp = require("jsftp");
var Exc = require("./../../../shared/exceptions");
var Util = require("./../../../shared/util");
/**
* jsDAV_Tree_Ftp
*
* Creates the FTP tree.
*
* @param {Object} options
* @contructor
*/
Ftp.debugMode = require("../../jsdav").debugMode;
var jsDAV_Tree_Ftp = module.exports = jsDAV_Tree.extend({
initialize: function(options) {
this.ftpCmdListeners = [];
this.options = options.ftp;
if (this.options.path) {
if (this.options.path.charAt(0) !== "/")
this.options.path = "/" + this.options.path;
}
else {
this.options.path = "/";
}
this.basePath = this.options.path;
this.setup();
},
setup: function() {
var ftp = this.ftp = new Ftp(this.options);
this.ftp.$cache = {};
this.ftpCmdListeners.forEach(function(listener) {
ftp.addCmdListener(listener);
});
},
addFtpCmdListener: function(listener) {
if (this.ftp)
this.ftp.addCmdListener(listener);
else
this.ftpCmdListeners.push(listener);
},
/**
* Returns a new node for the given path
*
* @param {String} path
* @return void
*/
getNodeForPath: function(path, next) {
if (!path || path.match(/^\s+$/) || path.match(/^[\/]+$/))
path = this.basePath;
else if (Path.dirname(path) === ".") // It is a file in the root
path = Path.join(this.basePath, path);
if (!this.ftp)
this.setup();
var ftp = this.ftp;
if (ftp.$cache[path]) {
return next(null, ftp.$cache[path]);
}
// Root node requires special treatment because it will not be listed
if (path === this.basePath) {
var baseDir = jsDAV_Ftp_Directory.new(path, ftp);
baseDir.isRoot = true;
return next(null, ftp.$cache[path] = baseDir);
}
var self = this;
var baseName = Path.basename(path).replace("/", "");
path = this.getRealPath(path);
var parentDir = Path.resolve(path + "/..");
ftp.ls(parentDir, function(err, res) {
if (err) {
if (res && res.code === 530) // Not logged in
ftp.auth(self.options.user, self.options.pass, function(err, res) {
if (!err)
self.getNodeForPath(path, next);
else
Util.log(err, "error");
});
else
return next(new Exc.FileNotFound(err));
}
else {
var file;
for (var i = 0; i < res.length; i++) {
var stat = res[i];
if (stat.name === baseName) {
file = stat;
break;
}
}
if (file) {
if (file.type === 1) // Is it a directory?
ftp.$cache[path] = jsDAV_Ftp_Directory.new(path, ftp);
else if (file.type === 0)
ftp.$cache[path] = jsDAV_Ftp_File.new(path, ftp);
if (ftp.$cache[path]) {
ftp.$cache[path].$stat = file;
next(null, ftp.$cache[path]);
}
else {
next(new Error("Unrecognized type of file: " + path));
}
}
else {
next(new Exc.FileNotFound(err));
}
}
});
},
/**
* Returns the real filesystem path for a webdav url.
*
* @param {String} publicPath
* @return {String}
*/
getRealPath: function(path) {
var _basePath = Util.rtrim(this.basePath);
var _path = Util.trim(path).replace(/[\/]+$/, "");
var re = new RegExp("^" + Util.escapeRegExp(_basePath));
if (_path.match(re)) {
return _path;
}
else {
return Path.normalize(Path.join(_basePath, _path));
}
},
/**
* Copies a file or directory.
*
* This method must work recursively and delete the destination
* if it exists
*
* @param {String} source
* @param {String} destination
* @return void
*/
copy: function(source, destination, next) {
next(new Exc.NotImplemented(
"Could not copy from " + source + " to " + destination +
". COPY/DUPLICATE not implemented.")
);
},
/**
* Moves a file or directory recursively.
* In case the ide crashed, the nodes cache was cleared and the source's parent will have to be pre-cached
* and so will have its children using $getParentNodeRecall(), this way we will be able to come back to this method
* to rename the source effectively.
* Once the MOVE has been executed, the node need's to be updated in the cache, and if it's a Directory type its
* children will have to be updated in the cache as well, so the new keys correspond to the new path.
*
* If the destination exists, delete it first.
*
* @param {String} source
* @param {String} destination
* @return void
*/
move: function(source, destination, next) {
source = this.getRealPath(source);
destination = this.getRealPath(destination);
var node = this.ftp.$cache[source];
var ftp = this.ftp;
if (!node)
return next(new Error("Node not found for path " + source));
node.setName(destination, function(err) {
if (err)
return next(err);
ftp.$cache[destination] = node;
delete ftp.$cache[source];
if (node.hasFeature(jsDav_iCollection))
repathChildren(source, destination);
next(null, source, destination);
});
function repathChildren(oldParentPath, newParentPath) {
var paths = Object.keys(ftp.$cache);
var re = new RegExp("^" + oldParentPath.replace(/\//g, "\\/") + ".+$");
paths.forEach(function(path) {
if (ftp.$cache[path] && re.test(path)) {
var newPath = newParentPath + path.substring(oldParentPath.length);
if (!ftp.$cache[newPath])
ftp.$cache[newPath] = ftp.$cache[path];
else
// Not sure why the line below is useful for
ftp.$cache[newPath] && (ftp.$cache[newPath].path = newPath);
delete ftp.$cache[path];
}
});
}
},
unmount: function() {
Util.log("Closed connection to the server. Unmounting FTP tree.");
if (this.ftp) {
this.ftp.destroy();
this.ftp = null;
}
}
});