Permalink
Browse files

prelim NLST support

  • Loading branch information...
1 parent 085c58e commit 94c464e77aab75621ba057e93c74180b4055fde4 @alanszlosek committed Apr 10, 2012
Showing with 173 additions and 2 deletions.
  1. +50 −2 ftpd.js
  2. +123 −0 glob.js
View
52 ftpd.js
@@ -1,8 +1,9 @@
var net = require("net");
var sys = require("sys");
var fs = require("fs");
-var path = require("path");
+var PathModule = require("path");
var dummyfs = require("./dummyfs");
+var glob = require('./glob');
require('./date-format');
/*
@@ -361,7 +362,54 @@ function createServer(host, sandbox) {
break;
case "NLST":
// Returns a list of file names in a specified directory.
- socket.write("202 Not supported\r\n");
+ if (!authenticated()) break;
+
+ /*
+ Normally the server responds with a mark using code 150. It then stops accepting new connections, attempts to send the contents of the directory over the data connection, and closes the data connection. Finally it
+
+ accepts the LIST or NLST request with code 226 if the entire directory was successfully transmitted;
+ rejects the LIST or NLST request with code 425 if no TCP connection was established;
+ rejects the LIST or NLST request with code 426 if the TCP connection was established but then broken by the client or by network failure; or
+ rejects the LIST or NLST request with code 451 if the server had trouble reading the directory from disk.
+
+ The server may reject the LIST or NLST request (with code 450 or 550) without first responding with a mark. In this case the server does not touch the data connection.
+ */
+
+ whenDataWritable( function(pasvconn) {
+ // This will be called once data has ACTUALLY written out ... socket.write() is async!
+ var success = function() {
+ socket.write("226 Transfer OK\r\n");
+ pasvconn.end();
+ };
+ // Use temporary filesystem path maker since a path might be sent with NLST
+ var temp = '';
+ if (commandArg) {
+ // Remove double slashes or "up directory"
+ commandArg = commandArg.replace(/\/{2,}|\.{2}/g, '');
+ if (commandArg.substr(0, 1) == '/') {
+ temp = PathModule.join(socket.sandbox, commandArg);
+ } else {
+ temp = PathModule.join(socket.sandbox, socket.fs.cwd(), commandArg);
+ }
+ } else temp = PathModule.join(socket.sandbox, socket.fs.cwd());
+ if (pasvconn.readable) pasvconn.resume();
+ logIf(3, "Sending file list", socket);
+
+ glob.glob(temp, function(err, files) {
+ //fs.readdir(socket.sandbox + temp.cwd(), function(err, files) {
+ if (err) {
+ logIf(0, "During NLST, error globbing files: " + err, socket);
+ socket.write("451 Read error\r\n");
+ pasvconn.write("", success);
+ return;
+ }
+ // Wait until acknowledged!
+ socket.write("150 Here comes the directory listing\r\n", function() {
+ logIf(3, "Directory has " + files.length + " files", socket);
+ pasvconn.write( files.map(PathModule.basename).join("\015\012") + "\015\012", success);
+ });
+ });
+ });
break;
case "NOOP":
// No operation (dummy packet; used mostly on keepalives).
View
123 glob.js
@@ -0,0 +1,123 @@
+var PathModule = require('path'),
+ fs = require('fs'),
+ events = require("events");
+
+// Wildcard directory listing
+
+
+// Should create using "new WildcardList"
+function glob(path, callback) {
+
+ var traversing = 0;
+
+ // Use relatve path if doesn't start with slash, riight?
+
+ // Preparation
+ var parts = path.split('/');
+ var base = '/';
+ var results = [];
+ // Internal events
+ var ev = new events.EventEmitter;
+
+ var init = function() {
+ //console.log('got: ' + path);
+ var pair = skipNonWildcards(base, parts);
+ parts = pair.pop();
+ base = pair.pop();
+ // Emitted when we start looping over items in a directory
+ /*
+ ev.on('begin', function() {
+ });
+ */
+ // After we're done looping over a directory
+ // Check whether we're REALLY done
+ ev.on('end', function() {
+ // Are we done traversing?
+ if (traversing) {
+ //console.log('still traversing');
+ return;
+ }
+ // Looks like we're done
+ callback(false, results);
+ });
+ // For debugging
+ //console.log('Prepared path and wildcard: ' + base + ' ' + parts);
+ walky(base, parts);
+ };
+
+ // Skip the prefix of folders that have no wildcards
+ var skipNonWildcards = function(folder, patterns) {
+ var basePath = folder;
+ var parts = patterns.slice(0); // Make a copy so we can modify
+ while (parts.length) {
+ var part = parts.shift();
+ if (!part.length) continue;
+ if (part.indexOf('*') == -1) {
+ // wildcard not found
+ basePath = PathModule.join(basePath, part);
+ } else {
+ parts.unshift( part );
+ break;
+ }
+ }
+ return [ basePath, parts ];
+ };
+
+
+ var walky = function(folder, patterns) {
+ var part;
+ var pair = skipNonWildcards(folder, patterns);
+ var parts = pair.pop();
+ folder = pair.pop();
+ part = (parts.length ? parts.shift() : null);
+
+ traversing++;
+
+ //console.log('Reading ' + folder);
+ PathModule.exists(folder, function(exists) {
+ if (!exists) {
+ traversing--;
+ ev.emit('end');
+ return;
+ }
+
+ fs.readdir(folder, function(err, files) {
+ if (err) {
+ console.trace('Shit: ' + err);
+ return;
+ }
+ for (var i = 0; i < files.length; i++) {
+ var file = files[i];
+ var full = PathModule.join(folder, file);
+ //console.log('Found: ' + full);
+ if (part && part.indexOf('*') > -1) { // pattern to check
+ // protect periods and replace asterisks
+ var pattern = '^' + part.replace(/\./g, '\\.').replace(/\*/g, '.+') + '$';
+ console.log('usng pattern: ' + pattern);
+ var pattern = new RegExp(pattern);
+ if (!pattern.test( file )) continue;
+ }
+ var s = fs.statSync(full);
+ if (s.isDirectory()) {
+ if (parts.length) {
+ //console.log('recursing into ' + full);
+ walky(full, parts, results, callback);
+ }
+ } else {
+ //console.log('file: ' + full);
+ if (!parts.length) results.push(full);
+ }
+ }
+ traversing--;
+ ev.emit('end');
+ });
+ });
+ };
+
+ init();
+ return this;
+};
+
+
+// Boom
+exports.glob = glob;

0 comments on commit 94c464e

Please sign in to comment.