basic-ftp
Advanced tools
Comparing version 3.2.2 to 3.3.0
# Changelog | ||
## 3.3.0 | ||
- Added: Support for leading whitespace in file and directory names. | ||
## 3.2.2 | ||
@@ -4,0 +8,0 @@ |
@@ -97,6 +97,2 @@ /// <reference types="node" /> | ||
/** | ||
* Set the working directory. | ||
*/ | ||
cd(path: string): Promise<FTPResponse>; | ||
/** | ||
* Get the current working directory. | ||
@@ -106,6 +102,2 @@ */ | ||
/** | ||
* Get the last modified time of a file. Not supported by every FTP server, method might throw exception. | ||
*/ | ||
lastMod(filename: string): Promise<Date>; | ||
/** | ||
* Get a description of supported features. | ||
@@ -119,5 +111,18 @@ * | ||
/** | ||
* Set the working directory. | ||
*/ | ||
cd(path: string): Promise<FTPResponse>; | ||
/** | ||
* Switch to the parent directory of the working directory. | ||
*/ | ||
cdup(): Promise<FTPResponse>; | ||
/** | ||
* Get the last modified time of a file. This is not supported by every FTP server, in which case | ||
* calling this method will throw an exception. | ||
*/ | ||
lastMod(path: string): Promise<Date>; | ||
/** | ||
* Get the size of a file. | ||
*/ | ||
size(filename: String): Promise<number>; | ||
size(path: string): Promise<number>; | ||
/** | ||
@@ -129,3 +134,3 @@ * Rename a file. | ||
*/ | ||
rename(path: string, newPath: string): Promise<FTPResponse>; | ||
rename(srcPath: string, destPath: string): Promise<FTPResponse>; | ||
/** | ||
@@ -137,3 +142,3 @@ * Remove a file from the current working directory. | ||
*/ | ||
remove(filename: string, ignoreErrorCodes?: boolean): Promise<FTPResponse>; | ||
remove(path: string, ignoreErrorCodes?: boolean): Promise<FTPResponse>; | ||
/** | ||
@@ -152,5 +157,5 @@ * Report transfer progress for any upload or download to a given handler. | ||
* @param source The stream to read from. | ||
* @param remoteFilename The filename of the remote file to write to. | ||
* @param remotePath The path of the remote file to write to. | ||
*/ | ||
upload(source: Readable, remoteFilename: string): Promise<FTPResponse>; | ||
upload(source: Readable, remotePath: string): Promise<FTPResponse>; | ||
/** | ||
@@ -162,6 +167,6 @@ * Download a file with a given filename from the current working directory | ||
* @param destination The stream to write to. | ||
* @param remoteFilename The name of the remote file to read from. | ||
* @param remotePath The name of the remote file to read from. | ||
* @param startAt The offset to start at. | ||
*/ | ||
download(destination: Writable, remoteFilename: string, startAt?: number): Promise<FTPResponse>; | ||
download(destination: Writable, remotePath: string, startAt?: number): Promise<FTPResponse>; | ||
/** | ||
@@ -211,2 +216,11 @@ * List files and directories in the current working directory. | ||
ensureDir(remoteDirPath: string): Promise<void>; | ||
/** | ||
* Remove an empty directory, will fail if not empty. | ||
*/ | ||
protected removeEmptyDir(path: string): Promise<FTPResponse>; | ||
/** | ||
* FTP servers can't handle filenames that have leading whitespace. This method transforms | ||
* a given path to fix that issue for most cases. | ||
*/ | ||
protected protectWhitespace(path: string): Promise<string>; | ||
} |
@@ -164,8 +164,2 @@ "use strict"; | ||
/** | ||
* Set the working directory. | ||
*/ | ||
cd(path) { | ||
return this.send("CWD " + path); | ||
} | ||
/** | ||
* Get the current working directory. | ||
@@ -184,15 +178,2 @@ */ | ||
/** | ||
* Get the last modified time of a file. Not supported by every FTP server, method might throw exception. | ||
*/ | ||
async lastMod(filename) { | ||
const res = await this.send("MDTM " + filename); | ||
// Message contains response code and modified time in the format: YYYYMMDDHHMMSS[.sss] | ||
// For example `213 19991005213102` or `213 19980615100045.014`. | ||
const msg = res.message; | ||
const date = new Date(); | ||
date.setUTCFullYear(+msg.slice(4, 8), +msg.slice(8, 10) - 1, +msg.slice(10, 12)); | ||
date.setUTCHours(+msg.slice(12, 14), +msg.slice(14, 16), +msg.slice(16, 18), +msg.slice(19, 22)); | ||
return date; | ||
} | ||
/** | ||
* Get a description of supported features. | ||
@@ -220,6 +201,36 @@ * | ||
/** | ||
* Set the working directory. | ||
*/ | ||
async cd(path) { | ||
const validPath = await this.protectWhitespace(path); | ||
return this.send("CWD " + validPath); | ||
} | ||
/** | ||
* Switch to the parent directory of the working directory. | ||
*/ | ||
async cdup() { | ||
return this.send("CDUP"); | ||
} | ||
/** | ||
* Get the last modified time of a file. This is not supported by every FTP server, in which case | ||
* calling this method will throw an exception. | ||
*/ | ||
async lastMod(path) { | ||
const validPath = await this.protectWhitespace(path); | ||
const res = await this.send(`MDTM ${validPath}`); | ||
// Message contains response code and modified time in the format: YYYYMMDDHHMMSS[.sss] | ||
// For example `213 19991005213102` or `213 19980615100045.014`. | ||
const msg = res.message; | ||
const date = new Date(); | ||
date.setUTCFullYear(+msg.slice(4, 8), +msg.slice(8, 10) - 1, +msg.slice(10, 12)); | ||
date.setUTCHours(+msg.slice(12, 14), +msg.slice(14, 16), +msg.slice(16, 18), +msg.slice(19, 22)); | ||
return date; | ||
} | ||
/** | ||
* Get the size of a file. | ||
*/ | ||
async size(filename) { | ||
const res = await this.send("SIZE " + filename); | ||
async size(path) { | ||
const validPath = await this.protectWhitespace(path); | ||
const command = `SIZE ${validPath}`; | ||
const res = await this.send(command); | ||
// The size is part of the response message, for example: "213 555555". It's | ||
@@ -229,3 +240,3 @@ // possible that there is a commmentary appended like "213 5555, some commentary". | ||
if (Number.isNaN(size)) { | ||
throw new Error(`Can't parse response to command 'SIZE ${filename}' as a numerical value: ${res.message}`); | ||
throw new Error(`Can't parse response to command '${command}' as a numerical value: ${res.message}`); | ||
} | ||
@@ -240,5 +251,7 @@ return size; | ||
*/ | ||
async rename(path, newPath) { | ||
await this.send("RNFR " + path); | ||
return this.send("RNTO " + newPath); | ||
async rename(srcPath, destPath) { | ||
const validSrc = await this.protectWhitespace(srcPath); | ||
const validDest = await this.protectWhitespace(destPath); | ||
await this.send("RNFR " + validSrc); | ||
return this.send("RNTO " + validDest); | ||
} | ||
@@ -251,4 +264,5 @@ /** | ||
*/ | ||
remove(filename, ignoreErrorCodes = false) { | ||
return this.send("DELE " + filename, ignoreErrorCodes); | ||
async remove(path, ignoreErrorCodes = false) { | ||
const validPath = await this.protectWhitespace(path); | ||
return this.send(`DELE ${validPath}`, ignoreErrorCodes); | ||
} | ||
@@ -271,7 +285,8 @@ /** | ||
* @param source The stream to read from. | ||
* @param remoteFilename The filename of the remote file to write to. | ||
* @param remotePath The path of the remote file to write to. | ||
*/ | ||
async upload(source, remoteFilename) { | ||
async upload(source, remotePath) { | ||
const validPath = await this.protectWhitespace(remotePath); | ||
await this.prepareTransfer(this); | ||
return upload(this.ftp, this.progressTracker, source, remoteFilename); | ||
return upload(this.ftp, this.progressTracker, source, validPath); | ||
} | ||
@@ -284,9 +299,10 @@ /** | ||
* @param destination The stream to write to. | ||
* @param remoteFilename The name of the remote file to read from. | ||
* @param remotePath The name of the remote file to read from. | ||
* @param startAt The offset to start at. | ||
*/ | ||
async download(destination, remoteFilename, startAt = 0) { | ||
async download(destination, remotePath, startAt = 0) { | ||
const validPath = await this.protectWhitespace(remotePath); | ||
await this.prepareTransfer(this); | ||
const command = startAt > 0 ? `REST ${startAt}` : `RETR ${remoteFilename}`; | ||
return download(this.ftp, this.progressTracker, destination, command, remoteFilename); | ||
const command = startAt > 0 ? `REST ${startAt}` : `RETR ${validPath}`; | ||
return download(this.ftp, this.progressTracker, destination, command, validPath); | ||
} | ||
@@ -323,4 +339,4 @@ /** | ||
if (workingDir !== "/") { | ||
await this.send("CDUP"); | ||
await this.send("RMD " + remoteDirPath); | ||
await this.cdup(); | ||
await this.removeEmptyDir(remoteDirPath); | ||
} | ||
@@ -337,7 +353,7 @@ } | ||
await this.clearWorkingDir(); | ||
await this.send("CDUP"); | ||
await this.send("RMD " + file.name); | ||
await this.cdup(); | ||
await this.removeEmptyDir(file.name); | ||
} | ||
else { | ||
await this.send("DELE " + file.name); | ||
await this.remove(file.name); | ||
} | ||
@@ -367,3 +383,3 @@ } | ||
if (remoteDirName !== undefined) { | ||
await this.send("CDUP"); | ||
await this.cdup(); | ||
} | ||
@@ -383,3 +399,3 @@ } | ||
await this.downloadDir(localPath); | ||
await this.send("CDUP"); | ||
await this.cdup(); | ||
} | ||
@@ -406,2 +422,23 @@ else { | ||
} | ||
/** | ||
* Remove an empty directory, will fail if not empty. | ||
*/ | ||
async removeEmptyDir(path) { | ||
const validPath = await this.protectWhitespace(path); | ||
return this.send(`RMD ${validPath}`); | ||
} | ||
/** | ||
* FTP servers can't handle filenames that have leading whitespace. This method transforms | ||
* a given path to fix that issue for most cases. | ||
*/ | ||
async protectWhitespace(path) { | ||
if (!path.startsWith(" ")) { | ||
return path; | ||
} | ||
// Handle leading whitespace by prepending the absolute path: | ||
// " test.txt" while being in the root directory becomes "/ test.txt". | ||
const pwd = await this.pwd(); | ||
const absolutePathPrefix = pwd.endsWith("/") ? pwd : pwd + "/"; | ||
return absolutePathPrefix + path; | ||
} | ||
} | ||
@@ -812,3 +849,3 @@ exports.Client = Client; | ||
await uploadDirContents(client, fullPath); | ||
await client.send("CDUP"); | ||
await client.cdup(); | ||
} | ||
@@ -815,0 +852,0 @@ } |
@@ -30,3 +30,3 @@ "use strict"; | ||
* + file has extended security attributes (e.g. ACL) | ||
* Note: local listings on MacOSX also use '@'; | ||
* Note: local listings on MacOSX also use '@' | ||
* this is not allowed for here as does not appear to be shown by FTP servers | ||
@@ -70,3 +70,2 @@ * {@code @} file has extended attributes | ||
exports.testLine = testLine; | ||
; | ||
function parseLine(line) { | ||
@@ -76,3 +75,3 @@ const groups = line.match(RE_LINE); | ||
// Ignore parent directory links | ||
const name = groups[21].trim(); | ||
const name = groups[21]; | ||
if (name === "." || name === "..") { | ||
@@ -129,3 +128,2 @@ return undefined; | ||
exports.parseLine = parseLine; | ||
; | ||
function parseMode(r, w, x) { | ||
@@ -132,0 +130,0 @@ let value = 0; |
{ | ||
"name": "basic-ftp", | ||
"version": "3.2.2", | ||
"version": "3.3.0", | ||
"description": "FTP client for Node.js, supports explicit FTPS over TLS, IPv6, Async/Await, and Typescript.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index", |
102171
2034