Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MLSD #160

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ Methods
* group - _string_ - An empty string or any combination of 'r', 'w', 'x'.

* other - _string_ - An empty string or any combination of 'r', 'w', 'x'.

* owner - _string_ - The user name or ID that this entry belongs to **(*NIX only)**.

* group - _string_ - The group name or ID that this entry belongs to **(*NIX only)**.
Expand Down Expand Up @@ -193,3 +193,5 @@ Methods
* **lastMod**(< _string_ >path, < _function_ >callback) - _(void)_ - Retrieves the last modified date and time for `path`. `callback` has 2 parameters: < _Error_ >err, < _Date_ >lastModified.

* **restart**(< _integer_ >byteOffset, < _function_ >callback) - _(void)_ - Sets the file byte offset for the next file transfer action (get/put) to `byteOffset`. `callback` has 1 parameter: < _Error_ >err.

* **mlsd**([< _string_ >path, ][< _boolean_ >useCompression, ]< _function_ >callback) - _(void)_ - Retrieves the directory listing of `path`. `path` defaults to the current working directory. `useCompression` defaults to false. `callback` has 2 parameters: < _Error_ >err, < _array_ >list. See the `list` command for a list of properties. Also see https://tools.ietf.org/html/rfc3659 7.2.
31 changes: 19 additions & 12 deletions lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -403,28 +403,27 @@ FTP.prototype.listSafe = function(path, zcomp, cb) {
this.list(path, zcomp, cb);
};

FTP.prototype.list = function(path, zcomp, cb) {
var self = this, cmd;
FTP.prototype.listCmd = function(path, zcomp, cmd, cb) {
var self = this;
var parse = cmd === 'MLSD' ? Parser.parseMlsdEntry : Parser.parseListEntry;

if (typeof path === 'function') {
// list(function() {})
cb = path;
path = undefined;
cmd = 'LIST';
zcomp = false;
} else if (typeof path === 'boolean') {
// list(true, function() {})
cb = zcomp;
zcomp = path;
path = undefined;
cmd = 'LIST';
} else if (typeof zcomp === 'function') {
// list('/foo', function() {})
cb = zcomp;
cmd = 'LIST ' + path;
cmd += ' ' + path;
zcomp = false;
} else
cmd = 'LIST ' + path;
cmd += ' ' + path;

this._pasv(function(err, sock) {
if (err)
Expand Down Expand Up @@ -474,7 +473,7 @@ FTP.prototype.list = function(path, zcomp, cb) {
entries.pop(); // ending EOL
var parsed = [];
for (var i = 0, len = entries.length; i < len; ++i) {
var parsedVal = Parser.parseListEntry(entries[i]);
var parsedVal = parse(entries[i]);
if (parsedVal !== null)
parsed.push(parsedVal);
}
Expand Down Expand Up @@ -527,6 +526,14 @@ FTP.prototype.list = function(path, zcomp, cb) {
});
};

FTP.prototype.list = function(path, zcomp, cb) {
return this.listCmd(path, zcomp, 'LIST', cb);
};

FTP.prototype.mlsd = function(path, zcomp, cb) {
return this.listCmd(path, zcomp, 'MLSD', cb);
};

FTP.prototype.get = function(path, zcomp, cb) {
var self = this;
if (typeof zcomp === 'function') {
Expand Down Expand Up @@ -743,12 +750,12 @@ FTP.prototype.rmdir = function(path, recursive, cb) { // RMD is optional
if (!recursive) {
return this._send('RMD ' + path, cb);
}

var self = this;
this.list(path, function(err, list) {
if (err) return cb(err);
var idx = 0;

// this function will be called once per listing entry
var deleteNextEntry;
deleteNextEntry = function(err) {
Expand All @@ -760,9 +767,9 @@ FTP.prototype.rmdir = function(path, recursive, cb) { // RMD is optional
return self.rmdir(path, cb);
}
}

var entry = list[idx++];

// get the path to the file
var subpath = null;
if (entry.name[0] === '/') {
Expand All @@ -776,7 +783,7 @@ FTP.prototype.rmdir = function(path, recursive, cb) { // RMD is optional
subpath = path + '/' + entry.name
}
}

// delete the entry (recursively) according to its type
if (entry.type === 'd') {
if (entry.name === "." || entry.name === "..") {
Expand Down
36 changes: 32 additions & 4 deletions lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ var REX_LISTUNIX = XRegExp.cache('^(?<type>[\\-ld])(?<permission>([\\-r][\\-w][\
RE_ENTRY_TOTAL = /^total/,
RE_RES_END = /(?:^|\r?\n)(\d{3}) [^\r\n]*\r?\n/,
RE_EOL = /\r?\n/g,
RE_DASH = /\-/g;
RE_DASH = /\-/g,
RE_SEP = /;/g,
RE_EQ = /=/;

var MONTHS = {
jan: 1, feb: 2, mar: 3, apr: 4, may: 5, jun: 6,
Expand Down Expand Up @@ -129,13 +131,13 @@ Parser.parseListEntry = function(line) {
+ mins);
// If the date is in the past but no more than 6 months old, year
// isn't displayed and doesn't have to be the current year.
//
//
// If the date is in the future (less than an hour from now), year
// isn't displayed and doesn't have to be the current year.
// That second case is much more rare than the first and less annoying.
// It's impossible to fix without knowing about the server's timezone,
// so we just don't do anything about it.
//
//
// If we're here with a time that is more than 28 hours into the
// future (1 hour + maximum timezone offset which is 27 hours),
// there is a problem -- we should be in the second conditional block
Expand All @@ -153,7 +155,7 @@ Parser.parseListEntry = function(line) {
// ahead of local)
// For instance, remote is in 2014 while local is still in 2013. In
// this case, a date like 01/01/13 02:23 could be detected instead of
// 01/01/14 02:23
// 01/01/14 02:23
// Our trigger point will be 3600*24*31*6 (since we already use 31
// as an upper bound, no need to add the 27 hours timezone offset)
if (Date.now() - info.date.getTime() > 16070400000) {
Expand Down Expand Up @@ -213,4 +215,30 @@ Parser.parseListEntry = function(line) {
return ret;
};

Parser.parseMlsdEntry = function(entry) {
var kvs = entry.split(RE_SEP);
var obj = { name: kvs.pop().substring(1) };
kvs.forEach(function(kv) {
kv = kv.split( RE_EQ );
obj[kv[0].toLowerCase()] = kv[1];
});

obj.size = parseInt(obj.size, 10);

var modify = obj.modify;
if (modify) {
var year = modify.substr(0, 4);
var month = modify.substr(4, 2);
var date = modify.substr(6, 2);
var hour = modify.substr(8, 2);
var minute = modify.substr(10, 2);
var second = modify.substr(12, 2);
obj.date = new Date(
year + '-' + month + '-' + date + 'T' + hour + ':' +minute + ':' + second
);
}

return obj;
};

module.exports = Parser;