blob: 453efef3fd876df69dbfc44c29fc281a68599be0 [file] [log] [blame]
/**
* 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);
}