Skip to content

Commit 0e0a944

Browse files
committed
Add MLSD
mscdex#160 Thanks @morris
1 parent 3cb3674 commit 0e0a944

3 files changed

Lines changed: 49 additions & 12 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ Methods
139139
* group - _string_ - An empty string or any combination of 'r', 'w', 'x'.
140140

141141
* other - _string_ - An empty string or any combination of 'r', 'w', 'x'.
142-
142+
143143
* owner - _string_ - The user name or ID that this entry belongs to **(*NIX only)**.
144144

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

195195
* **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.
196+
197+
* **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.

lib/connection.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -416,28 +416,27 @@ FTP.prototype.listSafe = function(path, zcomp, cb) {
416416
this.list(path, zcomp, cb);
417417
};
418418

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

422423
if (typeof path === 'function') {
423424
// list(function() {})
424425
cb = path;
425426
path = undefined;
426-
cmd = 'LIST';
427427
zcomp = false;
428428
} else if (typeof path === 'boolean') {
429429
// list(true, function() {})
430430
cb = zcomp;
431431
zcomp = path;
432432
path = undefined;
433-
cmd = 'LIST';
434433
} else if (typeof zcomp === 'function') {
435434
// list('/foo', function() {})
436435
cb = zcomp;
437-
cmd = 'LIST ' + path;
436+
cmd += ' ' + path;
438437
zcomp = false;
439438
} else
440-
cmd = 'LIST ' + path;
439+
cmd += ' ' + path;
441440

442441
this._pasv(function(err, sock) {
443442
if (err)
@@ -487,7 +486,7 @@ FTP.prototype.list = function(path, zcomp, cb) {
487486
entries.pop(); // ending EOL
488487
var parsed = [];
489488
for (var i = 0, len = entries.length; i < len; ++i) {
490-
var parsedVal = Parser.parseListEntry(entries[i]);
489+
var parsedVal = parse(entries[i]);
491490
if (parsedVal !== null)
492491
parsed.push(parsedVal);
493492
}
@@ -540,6 +539,14 @@ FTP.prototype.list = function(path, zcomp, cb) {
540539
});
541540
};
542541

542+
FTP.prototype.list = function(path, zcomp, cb) {
543+
return this.listCmd(path, zcomp, 'LIST', cb);
544+
};
545+
546+
FTP.prototype.mlsd = function(path, zcomp, cb) {
547+
return this.listCmd(path, zcomp, 'MLSD', cb);
548+
};
549+
543550
FTP.prototype.get = function(path, zcomp, cb) {
544551
var self = this;
545552
if (typeof zcomp === 'function') {

lib/parser.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ var REX_LISTUNIX = XRegExp.cache('^(?<type>[\\-ld])(?<permission>([\\-r][\\-w][\
1010
RE_ENTRY_TOTAL = /^total/,
1111
RE_RES_END = /(?:^|\r?\n)(\d{3}) [^\r\n]*\r?\n/,
1212
RE_EOL = /\r?\n/g,
13-
RE_DASH = /\-/g;
13+
RE_DASH = /\-/g,
14+
RE_SEP = /;/g,
15+
RE_EQ = /=/;
1416

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

218+
Parser.parseMlsdEntry = function(entry) {
219+
var kvs = entry.split(RE_SEP);
220+
var obj = { name: kvs.pop().substring(1) };
221+
kvs.forEach(function(kv) {
222+
kv = kv.split( RE_EQ );
223+
obj[kv[0].toLowerCase()] = kv[1];
224+
});
225+
226+
obj.size = parseInt(obj.size, 10);
227+
228+
var modify = obj.modify;
229+
if (modify) {
230+
var year = modify.substr(0, 4);
231+
var month = modify.substr(4, 2);
232+
var date = modify.substr(6, 2);
233+
var hour = modify.substr(8, 2);
234+
var minute = modify.substr(10, 2);
235+
var second = modify.substr(12, 2);
236+
obj.date = new Date(
237+
year + '-' + month + '-' + date + 'T' + hour + ':' +minute + ':' + second
238+
);
239+
}
240+
241+
return obj;
242+
};
243+
216244
module.exports = Parser;

0 commit comments

Comments
 (0)