129 lines
3.0 KiB
JavaScript
129 lines
3.0 KiB
JavaScript
|
|
||
|
/**
|
||
|
* Module dependencies.
|
||
|
*/
|
||
|
|
||
|
var FTP = require('ftp');
|
||
|
var path = require('path');
|
||
|
var NotFoundError = require('./notfound');
|
||
|
var NotModifiedError = require('./notmodified');
|
||
|
var debug = require('debug')('get-uri:ftp');
|
||
|
|
||
|
/**
|
||
|
* Module exports.
|
||
|
*/
|
||
|
|
||
|
module.exports = get;
|
||
|
|
||
|
/**
|
||
|
* Returns a Readable stream from an "ftp:" URI.
|
||
|
*
|
||
|
* @api protected
|
||
|
*/
|
||
|
|
||
|
function get (parsed, opts, fn) {
|
||
|
var cache = opts.cache;
|
||
|
var client = new FTP();
|
||
|
var filepath = parsed.pathname;
|
||
|
var lastModified;
|
||
|
|
||
|
client.once('error', onerror);
|
||
|
client.once('ready', onready);
|
||
|
client.once('greeting', ongreeting);
|
||
|
|
||
|
function onready () {
|
||
|
// first we have to figure out the Last Modified date.
|
||
|
// try the MDTM command first, which is an optional extension command.
|
||
|
client.lastMod(filepath, onlastmod);
|
||
|
}
|
||
|
|
||
|
function ongreeting (greeting) {
|
||
|
debug('FTP greeting: %o', greeting);
|
||
|
}
|
||
|
|
||
|
function onerror (err) {
|
||
|
client.end();
|
||
|
fn(err);
|
||
|
}
|
||
|
|
||
|
function onfile (err, stream) {
|
||
|
if (err) return onerror(err);
|
||
|
stream.once('end', onend);
|
||
|
stream.lastModified = lastModified;
|
||
|
fn(null, stream);
|
||
|
}
|
||
|
|
||
|
function onend () {
|
||
|
// close the FTP client socket connection
|
||
|
client.end();
|
||
|
}
|
||
|
|
||
|
function getFile () {
|
||
|
client.get(filepath, onfile);
|
||
|
}
|
||
|
|
||
|
function onlastmod (err, lastmod) {
|
||
|
// handle the "file not found" error code
|
||
|
if (err) {
|
||
|
if (550 == err.code) {
|
||
|
onerror(new NotFoundError());
|
||
|
}
|
||
|
// any other error then we'll try the LIST command instead
|
||
|
}
|
||
|
if (lastmod) {
|
||
|
setLastMod(lastmod);
|
||
|
} else {
|
||
|
// try to get the last modified date via the LIST command (uses
|
||
|
// more bandwidth, but is more compatible with older FTP servers
|
||
|
var dir = path.dirname(filepath);
|
||
|
client.list(dir, onlist);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function setLastMod (lastmod) {
|
||
|
lastModified = lastmod;
|
||
|
if (cache && isNotModified()) {
|
||
|
// file is the same as in the "cache", return a not modified error
|
||
|
onerror(new NotModifiedError());
|
||
|
} else {
|
||
|
// XXX: a small timeout seemed necessary otherwise FTP servers
|
||
|
// were returning empty sockets for the file occasionally
|
||
|
setTimeout(client.get.bind(client, filepath, onfile), 10);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function onlist (err, list) {
|
||
|
if (err) return onerror(err);
|
||
|
var name = path.basename(filepath);
|
||
|
|
||
|
// attempt to find the "entry" with a matching "name"
|
||
|
var entry;
|
||
|
for (var i = 0; i < list.length; i++) {
|
||
|
entry = list[i];
|
||
|
debug('file %o: %o', i, entry.name);
|
||
|
if (entry.name == name) {
|
||
|
break;
|
||
|
}
|
||
|
entry = null;
|
||
|
}
|
||
|
|
||
|
if (entry) {
|
||
|
setLastMod(entry.date);
|
||
|
} else {
|
||
|
onerror(new NotFoundError());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// called when `lastModified` is set, and a "cache" stream was provided
|
||
|
function isNotModified () {
|
||
|
return +cache.lastModified == +lastModified;
|
||
|
}
|
||
|
|
||
|
opts.host = parsed.hostname || parsed.host || 'localhost';
|
||
|
opts.port = parseInt(parsed.port, 10) || 21;
|
||
|
if (debug.enabled) opts.debug = debug;
|
||
|
|
||
|
// TODO: add auth
|
||
|
client.connect(opts);
|
||
|
}
|