Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

basic-ftp

Package Overview
Dependencies
Maintainers
1
Versions
112
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

basic-ftp - npm Package Compare versions

Comparing version 4.6.6 to 5.0.0

3

dist/Client.d.ts
/// <reference types="node" />
/// <reference types="node" />
import { Readable, Writable } from "stream";

@@ -306,3 +307,3 @@ import { ConnectionOptions as TLSConnectionOptions } from "tls";

*/
protected _enterFirstCompatibleMode(transferModes: TransferStrategy[]): TransferStrategy;
protected _enterFirstCompatibleMode(): TransferStrategy;
/**

@@ -309,0 +310,0 @@ * DEPRECATED, use `uploadFrom`.

@@ -17,8 +17,8 @@ "use strict";

// Use promisify to keep the library compatible with Node 8.
const fsReadDir = util_1.promisify(fs_1.readdir);
const fsMkDir = util_1.promisify(fs_1.mkdir);
const fsStat = util_1.promisify(fs_1.stat);
const fsOpen = util_1.promisify(fs_1.open);
const fsClose = util_1.promisify(fs_1.close);
const fsUnlink = util_1.promisify(fs_1.unlink);
const fsReadDir = (0, util_1.promisify)(fs_1.readdir);
const fsMkDir = (0, util_1.promisify)(fs_1.mkdir);
const fsStat = (0, util_1.promisify)(fs_1.stat);
const fsOpen = (0, util_1.promisify)(fs_1.open);
const fsClose = (0, util_1.promisify)(fs_1.close);
const fsUnlink = (0, util_1.promisify)(fs_1.unlink);
const LIST_COMMANDS_DEFAULT = ["LIST -a", "LIST"];

@@ -38,3 +38,3 @@ const LIST_COMMANDS_MLSD = ["MLSD", "LIST -a", "LIST"];

this.ftp = new FtpContext_1.FTPContext(timeout);
this.prepareTransfer = this._enterFirstCompatibleMode([transfer_1.enterPassiveModeIPv6, transfer_1.enterPassiveModeIPv4]);
this.prepareTransfer = this._enterFirstCompatibleMode();
this.parseList = parseList_1.parseList;

@@ -77,3 +77,3 @@ this._progressTracker = new ProgressTracker_1.ProgressTracker();

family: this.ftp.ipFamily
}, () => this.ftp.log(`Connected to ${netUtils_1.describeAddress(this.ftp.socket)} (${netUtils_1.describeTLS(this.ftp.socket)})`));
}, () => this.ftp.log(`Connected to ${(0, netUtils_1.describeAddress)(this.ftp.socket)} (${(0, netUtils_1.describeTLS)(this.ftp.socket)})`));
return this._handleConnectResponse();

@@ -87,3 +87,3 @@ }

this.ftp.reset();
this.ftp.socket = tls_1.connect(port, host, tlsOptions, () => this.ftp.log(`Connected to ${netUtils_1.describeAddress(this.ftp.socket)} (${netUtils_1.describeTLS(this.ftp.socket)})`));
this.ftp.socket = (0, tls_1.connect)(port, host, tlsOptions, () => this.ftp.log(`Connected to ${(0, netUtils_1.describeAddress)(this.ftp.socket)} (${(0, netUtils_1.describeTLS)(this.ftp.socket)})`));
this.ftp.tlsOptions = tlsOptions;

@@ -101,3 +101,3 @@ return this._handleConnectResponse();

}
else if (parseControlResponse_1.positiveCompletion(res.code)) {
else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) {
task.resolve(res);

@@ -109,3 +109,2 @@ }

// so the user can inspect properties of this instance.
this.ftp.socket.destroy();
task.reject(new FtpContext_1.FTPError(res));

@@ -151,5 +150,5 @@ }

const ret = await this.send(command);
this.ftp.socket = await netUtils_1.upgradeSocket(this.ftp.socket, options);
this.ftp.socket = await (0, netUtils_1.upgradeSocket)(this.ftp.socket, options);
this.ftp.tlsOptions = options; // Keep the TLS options for later data connections that should use the same options.
this.ftp.log(`Control socket is using: ${netUtils_1.describeTLS(this.ftp.socket)}`);
this.ftp.log(`Control socket is using: ${(0, netUtils_1.describeTLS)(this.ftp.socket)}`);
return ret;

@@ -164,3 +163,3 @@ }

login(user = "anonymous", password = "guest") {
this.ftp.log(`Login security: ${netUtils_1.describeTLS(this.ftp.socket)}`);
this.ftp.log(`Login security: ${(0, netUtils_1.describeTLS)(this.ftp.socket)}`);
return this.ftp.handle("USER " + user, (res, task) => {

@@ -170,3 +169,3 @@ if (res instanceof Error) {

}
else if (parseControlResponse_1.positiveCompletion(res.code)) { // User logged in proceed OR Command superfluous
else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) { // User logged in proceed OR Command superfluous
task.resolve(res);

@@ -261,3 +260,3 @@ }

// Not supporting any special features will be reported with a single line.
if (res.code < 400 && parseControlResponse_1.isMultiline(res.message)) {
if (res.code < 400 && (0, parseControlResponse_1.isMultiline)(res.message)) {
// The first and last line wrap the multiline response, ignore them.

@@ -294,3 +293,3 @@ res.message.split("\n").slice(1, -1).forEach(line => {

const date = res.message.slice(4);
return parseListMLSD_1.parseMLSxDate(date);
return (0, parseListMLSD_1.parseMLSxDate)(date);
}

@@ -379,3 +378,3 @@ /**

const fd = await fsOpen(localPath, "r");
const source = fs_1.createReadStream("", {
const source = (0, fs_1.createReadStream)("", {
fd,

@@ -404,3 +403,3 @@ start: options.localStart,

// and removes the event listener for the source stream too early.
return await transfer_1.uploadFrom(source, {
return await (0, transfer_1.uploadFrom)(source, {
ftp: this.ftp,

@@ -443,3 +442,3 @@ tracker: this._progressTracker,

const fd = await fsOpen(localPath, fileSystemFlags);
const destination = fs_1.createWriteStream("", {
const destination = (0, fs_1.createWriteStream)("", {
fd,

@@ -476,3 +475,3 @@ start: startAt,

// and removes the event listener for the source stream too early.
return await transfer_1.downloadTo(destination, {
return await (0, transfer_1.downloadTo)(destination, {
ftp: this.ftp,

@@ -522,3 +521,3 @@ tracker: this._progressTracker,

const buffer = new StringWriter_1.StringWriter();
await transfer_1.downloadTo(buffer, {
await (0, transfer_1.downloadTo)(buffer, {
ftp: this.ftp,

@@ -596,3 +595,3 @@ tracker: this._progressTracker,

for (const file of files) {
const fullPath = path_1.join(localDirPath, file);
const fullPath = (0, path_1.join)(localDirPath, file);
const stats = await fsStat(fullPath);

@@ -629,3 +628,3 @@ if (stats.isFile()) {

for (const file of await this.list()) {
const localPath = path_1.join(localDirPath, file.name);
const localPath = (0, path_1.join)(localDirPath, file.name);
if (file.isDirectory) {

@@ -703,19 +702,7 @@ await this.cd(file.name);

*/
_enterFirstCompatibleMode(transferModes) {
_enterFirstCompatibleMode() {
return async (ftp) => {
ftp.log("Trying to find optimal transfer mode...");
for (const transferMode of transferModes) {
try {
const res = await transferMode(ftp);
ftp.log("Optimal transfer mode found.");
this.prepareTransfer = transferMode; // eslint-disable-line require-atomic-updates
return res;
}
catch (err) {
// Try the next candidate no matter the exact error. It's possible that a server
// answered incorrectly to a strategy, for example a PASV answer to an EPSV.
ftp.log(`Transfer mode failed: "${err.message}", will try next.`);
}
}
throw new Error("None of the available transfer modes work.");
const features = await this.features();
this.prepareTransfer = features.has("EPSV") ? transfer_1.enterPassiveModeIPv6 : transfer_1.enterPassiveModeIPv4;
return this.prepareTransfer(ftp);
};

@@ -722,0 +709,0 @@ }

/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
import { Socket } from "net";

@@ -3,0 +5,0 @@ import { ConnectionOptions as TLSConnectionOptions, TLSSocket } from "tls";

@@ -69,2 +69,3 @@ "use strict";

this._closingError = err;
this.send("QUIT"); // Don't wait for an answer
// Before giving the user's task a chance to react, make sure we won't be bothered with any inputs.

@@ -110,3 +111,3 @@ this._closeSocket(this._socket);

if (!isUpgrade) {
this._socket.destroy();
this._socket.end();
}

@@ -277,3 +278,3 @@ this._removeSocketListeners(this._socket);

const completeResponse = this._partialResponse + chunk;
const parsed = parseControlResponse_1.parseControlResponse(completeResponse);
const parsed = (0, parseControlResponse_1.parseControlResponse)(completeResponse);
// Remember any incomplete remainder.

@@ -327,4 +328,6 @@ this._partialResponse = parsed.rest;

if (socket) {
socket.destroy();
this._removeSocketListeners(socket);
socket.on("error", () => { });
socket.setTimeout(0);
socket.end();
}

@@ -331,0 +334,0 @@ }

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k;

/// <reference types="node" />
/// <reference types="node" />
import { Socket } from "net";

@@ -3,0 +4,0 @@ import { ConnectionOptions, TLSSocket } from "tls";

@@ -34,3 +34,3 @@ "use strict";

});
const tlsSocket = tls_1.connect(tlsOptions, () => {
const tlsSocket = (0, tls_1.connect)(tlsOptions, () => {
const expectCertificate = tlsOptions.rejectUnauthorized !== false;

@@ -65,4 +65,5 @@ if (expectCertificate && !tlsSocket.authorized) {

|| (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) // 172.16.0.0 - 172.31.255.255
|| (octets[0] === 192 && octets[1] === 168); // 192.168.0.0 - 192.168.255.255
|| (octets[0] === 192 && octets[1] === 168) // 192.168.0.0 - 192.168.255.255
|| ip === "127.0.0.1";
}
exports.ipIsPrivateV4Address = ipIsPrivateV4Address;
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -38,5 +42,8 @@ if (k2 === undefined) k2 = k;

}
function stringIsNotBlank(str) {
function isNotBlank(str) {
return str.trim() !== "";
}
function isNotMeta(str) {
return !str.startsWith("total");
}
const REGEX_NEWLINE = /\r?\n/;

@@ -49,3 +56,4 @@ /**

.split(REGEX_NEWLINE)
.filter(stringIsNotBlank);
.filter(isNotBlank)
.filter(isNotMeta);
if (lines.length === 0) {

@@ -52,0 +60,0 @@ return [];

@@ -36,3 +36,3 @@ "use strict";

info.link = value.substr(value.indexOf(":") + 1);
return 1 /* Continue */;
return 1 /* FactHandlerResult.Continue */;
}

@@ -53,7 +53,7 @@ switch (value) {

case "pdir": // Parent directory
return 2 /* IgnoreFile */; // Don't include these entries in the listing
return 2 /* FactHandlerResult.IgnoreFile */; // Don't include these entries in the listing
default:
info.type = FileInfo_1.FileType.Unknown;
}
return 1 /* Continue */;
return 1 /* FactHandlerResult.Continue */;
},

@@ -139,3 +139,3 @@ "unix.mode": (value, info) => {

const result = factHandler(factValue, info);
if (result === 2 /* IgnoreFile */) {
if (result === 2 /* FactHandlerResult.IgnoreFile */) {
return undefined;

@@ -142,0 +142,0 @@ }

@@ -39,3 +39,3 @@ "use strict";

const RE_LINE = new RegExp("([bcdelfmpSs-])" // file type
+ "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?" // permissions
+ "(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]?)))\\+?" // permissions
+ "\\s*" // separator TODO why allow it to be omitted??

@@ -42,0 +42,0 @@ + "(\\d+)" // link count

/// <reference types="node" />
/// <reference types="node" />
import { Writable } from "stream";

@@ -3,0 +4,0 @@ import { StringEncoding } from "./StringEncoding";

@@ -5,2 +5,3 @@ "use strict";

const netUtils_1 = require("./netUtils");
const stream_1 = require("stream");
const tls_1 = require("tls");

@@ -56,3 +57,3 @@ const parseControlResponse_1 = require("./parseControlResponse");

const controlHost = ftp.socket.remoteAddress;
if (netUtils_1.ipIsPrivateV4Address(target.host) && controlHost && !netUtils_1.ipIsPrivateV4Address(controlHost)) {
if ((0, netUtils_1.ipIsPrivateV4Address)(target.host) && controlHost && !(0, netUtils_1.ipIsPrivateV4Address)(controlHost)) {
target.host = controlHost;

@@ -81,2 +82,3 @@ }

return new Promise((resolve, reject) => {
let socket = ftp._newSocket();
const handleConnErr = function (err) {

@@ -86,7 +88,12 @@ err.message = "Can't open data connection in passive mode: " + err.message;

};
let socket = ftp._newSocket();
const handleTimeout = function () {
socket.destroy();
reject(new Error(`Timeout when trying to open data connection to ${host}:${port}`));
};
socket.setTimeout(ftp.timeout);
socket.on("error", handleConnErr);
socket.on("timeout", handleTimeout);
socket.connect({ port, host, family: ftp.ipFamily }, () => {
if (ftp.socket instanceof tls_1.TLSSocket) {
socket = tls_1.connect(Object.assign({}, ftp.tlsOptions, {
socket = (0, tls_1.connect)(Object.assign({}, ftp.tlsOptions, {
socket,

@@ -110,2 +117,3 @@ // Reuse the TLS session negotiated earlier when the control connection

socket.removeListener("error", handleConnErr);
socket.removeListener("timeout", handleTimeout);
ftp.dataSocket = socket;

@@ -220,14 +228,18 @@ resolve();

onConditionOrEvent(canUpload, dataSocket, "secureConnect", () => {
config.ftp.log(`Uploading to ${netUtils_1.describeAddress(dataSocket)} (${netUtils_1.describeTLS(dataSocket)})`);
config.ftp.log(`Uploading to ${(0, netUtils_1.describeAddress)(dataSocket)} (${(0, netUtils_1.describeTLS)(dataSocket)})`);
resolver.onDataStart(config.remotePath, config.type);
source.pipe(dataSocket).once("finish", () => {
dataSocket.destroy(); // Explicitly close/destroy the socket to signal the end.
resolver.onDataDone(task);
(0, stream_1.pipeline)(source, dataSocket, err => {
if (err) {
resolver.onError(task, err);
}
else {
resolver.onDataDone(task);
}
});
});
}
else if (parseControlResponse_1.positiveCompletion(res.code)) { // Transfer complete
else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) { // Transfer complete
resolver.onControlDone(task, res);
}
else if (parseControlResponse_1.positiveIntermediate(res.code)) {
else if ((0, parseControlResponse_1.positiveIntermediate)(res.code)) {
resolver.onUnexpectedRequest(res);

@@ -243,5 +255,2 @@ }

}
// It's possible that data transmission begins before the control socket
// receives the announcement. Start listening for data immediately.
config.ftp.dataSocket.pipe(destination);
const resolver = new TransferResolver(config.ftp, config.tracker);

@@ -258,5 +267,12 @@ return config.ftp.handle(config.command, (res, task) => {

}
config.ftp.log(`Downloading from ${netUtils_1.describeAddress(dataSocket)} (${netUtils_1.describeTLS(dataSocket)})`);
config.ftp.log(`Downloading from ${(0, netUtils_1.describeAddress)(dataSocket)} (${(0, netUtils_1.describeTLS)(dataSocket)})`);
resolver.onDataStart(config.remotePath, config.type);
onConditionOrEvent(isWritableFinished(destination), destination, "finish", () => resolver.onDataDone(task));
(0, stream_1.pipeline)(dataSocket, destination, err => {
if (err) {
resolver.onError(task, err);
}
else {
resolver.onDataDone(task);
}
});
}

@@ -266,6 +282,6 @@ else if (res.code === 350) { // Restarting at startAt.

}
else if (parseControlResponse_1.positiveCompletion(res.code)) { // Transfer complete
else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) { // Transfer complete
resolver.onControlDone(task, res);
}
else if (parseControlResponse_1.positiveIntermediate(res.code)) {
else if ((0, parseControlResponse_1.positiveIntermediate)(res.code)) {
resolver.onUnexpectedRequest(res);

@@ -294,13 +310,1 @@ }

}
/**
* Detect whether a writable stream is finished, supporting Node 8.
* From https://github.com/nodejs/node/blob/3e2a3007107b7a100794f4e4adbde19263fc7464/lib/internal/streams/end-of-stream.js#L28-L33
*/
function isWritableFinished(stream) {
if (stream.writableFinished)
return true;
const wState = stream._writableState;
if (!wState || wState.errored)
return false;
return wState.finished || (wState.ended && wState.length === 0);
}
{
"name": "basic-ftp",
"version": "4.6.6",
"version": "5.0.0",
"description": "FTP client for Node.js, supports FTPS over TLS, IPv6, Async/Await, and Typescript.",

@@ -37,15 +37,15 @@ "main": "dist/index",

"engines": {
"node": ">=8.0.0"
"node": ">=10.0.0"
},
"devDependencies": {
"@types/mocha": "8.2.2",
"@types/node": "14.14.37",
"@typescript-eslint/eslint-plugin": "4.19.0",
"@typescript-eslint/parser": "4.19.0",
"eslint": "7.23.0",
"mocha": "8.3.2",
"mock-fs": "4.13.0",
"@types/mocha": "9.1.1",
"@types/node": "18.0.5",
"@typescript-eslint/eslint-plugin": "5.30.6",
"@typescript-eslint/parser": "5.30.6",
"eslint": "8.19.0",
"mocha": "10.0.0",
"mock-fs": "5.1.2",
"rimraf": "3.0.2",
"typescript": "4.2.3"
"typescript": "4.7.4"
}
}
# Basic FTP
[![Build Status](https://travis-ci.org/patrickjuchli/basic-ftp.svg?branch=master)](https://travis-ci.org/patrickjuchli/basic-ftp) [![dependencies](https://img.shields.io/david/patrickjuchli/basic-ftp)](https://david-dm.org/patrickjuchli/basic-ftp) [![npm version](https://img.shields.io/npm/v/basic-ftp.svg)](https://www.npmjs.com/package/basic-ftp)
[![npm version](https://img.shields.io/npm/v/basic-ftp.svg)](https://www.npmjs.com/package/basic-ftp)
[![npm downloads](https://img.shields.io/npm/dm/basic-ftp)](https://www.npmjs.com/package/basic-ftp)

@@ -13,10 +14,15 @@ This is an FTP client for Node.js. It supports FTPS over TLS, Passive Mode over IPv6, has a Promise-based API, and offers methods to operate on whole directories.

Node 8.0 or later is the only dependency.
Node 10.0 or later is the only dependency.
## Introduction
## Installation
`npm install basic-ftp`
## Usage
The first example will connect to an FTP server using TLS, get a directory listing, upload a file and download it as a copy. Note that the FTP protocol doesn't allow multiple requests running in parallel.
```js
const ftp = require("basic-ftp")
const ftp = require("basic-ftp")
// ESM: import * as ftp from "basic-ftp"

@@ -127,9 +133,9 @@ example()

`uploadFrom(readableStream | localPath, remotePath): Promise<FTPResponse>`
`uploadFrom(readableStream | localPath, remotePath, [options]): Promise<FTPResponse>`
Upload data from a readable stream or a local file to a remote file. If such a file already exists it will be overwritten.
Upload data from a readable stream or a local file to a remote file. If such a file already exists it will be overwritten. If a file is being uploaded, additional options offer `localStart` and `localEndInclusive` to only upload parts of it.
`appendFrom(readableStream | localPath, remotePath): Promise<FTPResponse>`
`appendFrom(readableStream | localPath, remotePath, [options]): Promise<FTPResponse>`
Upload data from a readable stream or a local file by appending it to an existing file. If the file doesn't exist the FTP server should create it.
Upload data from a readable stream or a local file by appending it to an existing file. If the file doesn't exist the FTP server should create it. If a file is being uploaded, additional options offer `localStart` and `localEndInclusive` to only upload parts of it.

@@ -136,0 +142,0 @@ `downloadTo(writableStream | localPath, remotePath, startAt = 0): Promise<FTPResponse>`

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc