ftp.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /**
  2. * Module dependencies.
  3. */
  4. var FTP = require('ftp');
  5. var path = require('path');
  6. var NotFoundError = require('./notfound');
  7. var NotModifiedError = require('./notmodified');
  8. var debug = require('debug')('get-uri:ftp');
  9. /**
  10. * Module exports.
  11. */
  12. module.exports = get;
  13. /**
  14. * Returns a Readable stream from an "ftp:" URI.
  15. *
  16. * @api protected
  17. */
  18. function get (parsed, opts, fn) {
  19. var cache = opts.cache;
  20. var client = new FTP();
  21. var filepath = parsed.pathname;
  22. var lastModified;
  23. client.once('ready', onready);
  24. client.once('greeting', ongreeting);
  25. function onready () {
  26. // first we have to figure out the Last Modified date.
  27. // try the MDTM command first, which is an optional extension command.
  28. client.lastMod(filepath, onlastmod);
  29. }
  30. function ongreeting (greeting) {
  31. debug('FTP greeting: %o', greeting);
  32. }
  33. function onerror (err) {
  34. client.end();
  35. fn(err);
  36. }
  37. function onfile (err, stream) {
  38. if (err) return onerror(err);
  39. stream.once('end', onend);
  40. stream.lastModified = lastModified;
  41. fn(null, stream);
  42. }
  43. function onend () {
  44. // close the FTP client socket connection
  45. client.end();
  46. }
  47. function getFile () {
  48. client.get(filepath, onfile);
  49. }
  50. function onlastmod (err, lastmod) {
  51. // handle the "file not found" error code
  52. if (err) {
  53. if (550 == err.code) {
  54. onerror(new NotFoundError());
  55. }
  56. // any other error then we'll try the LIST command instead
  57. }
  58. if (lastmod) {
  59. setLastMod(lastmod);
  60. } else {
  61. // try to get the last modified date via the LIST command (uses
  62. // more bandwidth, but is more compatible with older FTP servers
  63. var dir = path.dirname(filepath);
  64. client.list(dir, onlist);
  65. }
  66. }
  67. function setLastMod (lastmod) {
  68. lastModified = lastmod;
  69. if (cache && isNotModified()) {
  70. // file is the same as in the "cache", return a not modified error
  71. onerror(new NotModifiedError());
  72. } else {
  73. // XXX: a small timeout seemed necessary otherwise FTP servers
  74. // were returning empty sockets for the file occasionally
  75. setTimeout(client.get.bind(client, filepath, onfile), 10);
  76. }
  77. }
  78. function onlist (err, list) {
  79. if (err) return onerror(err);
  80. var name = path.basename(filepath);
  81. // attempt to find the "entry" with a matching "name"
  82. var entry;
  83. for (var i = 0; i < list.length; i++) {
  84. entry = list[i];
  85. debug('file %o: %o', i, entry.name);
  86. if (entry.name == name) {
  87. break;
  88. }
  89. entry = null;
  90. }
  91. if (entry) {
  92. setLastMod(entry.date);
  93. } else {
  94. onerror(new NotFoundError());
  95. }
  96. }
  97. // called when `lastModified` is set, and a "cache" stream was provided
  98. function isNotModified () {
  99. return +cache.lastModified == +lastModified;
  100. }
  101. opts.host = parsed.hostname || parsed.host || 'localhost';
  102. opts.port = parseInt(parsed.port, 10) || 21;
  103. if (debug.enabled) opts.debug = debug;
  104. // TODO: add auth
  105. client.connect(opts);
  106. }