piclaw 0.0.19 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/defult-D5RLDUrI.js +1 -0
- package/.output/public/assets/{dist-CMBqBOCp.js → dist-BH_oa-kv.js} +1 -1
- package/.output/public/assets/index-7JvURuHy.js +204 -0
- package/.output/public/assets/index-K43slwjJ.css +1 -0
- package/.output/public/index.html +11 -2
- package/.output/server/_...path_.get.mjs +16 -0
- package/.output/server/_chunks/app.mjs +261 -181
- package/.output/server/_chunks/browser.mjs +4 -1
- package/.output/server/_chunks/config.mjs +4 -0
- package/.output/server/_chunks/db.mjs +32 -28
- package/.output/server/_chunks/device-bus.mjs +123 -0
- package/.output/server/_chunks/dummy.mjs +1 -1
- package/.output/server/_chunks/logger.mjs +23 -0
- package/.output/server/_chunks/login.mjs +1 -1
- package/.output/server/_chunks/notes.mjs +1 -3
- package/.output/server/_chunks/renderer-template.mjs +1 -1
- package/.output/server/_chunks/sandbox.mjs +217 -0
- package/.output/server/_chunks/server.mjs +2302 -122
- package/.output/server/_chunks/terminal.mjs +63 -8
- package/.output/server/_chunks/uploads.mjs +60 -0
- package/.output/server/_chunks/virtual.mjs +192 -54
- package/.output/server/_id_.delete.mjs +5 -2
- package/.output/server/_id_.patch.mjs +2 -0
- package/.output/server/_id_2.delete.mjs +8 -0
- package/.output/server/_jid_.delete.mjs +5 -2
- package/.output/server/_jid_.patch.mjs +37 -4
- package/.output/server/_jid_2.delete.mjs +5 -2
- package/.output/server/_libs/@acemir/cssom+[...].mjs +2269 -1137
- package/.output/server/_libs/@google/genai.mjs +337 -273
- package/.output/server/_libs/@mariozechner/pi-agent-core+[...].mjs +381 -2073
- package/.output/server/_libs/@mariozechner/pi-coding-agent+[...].mjs +231 -131
- package/.output/server/_libs/_.mjs +3 -2
- package/.output/server/_libs/_10.mjs +2 -4
- package/.output/server/_libs/_11.mjs +2 -4
- package/.output/server/_libs/_12.mjs +2 -3
- package/.output/server/_libs/_13.mjs +2 -3
- package/.output/server/_libs/_14.mjs +2 -4
- package/.output/server/_libs/_15.mjs +2 -4
- package/.output/server/_libs/_16.mjs +2 -3
- package/.output/server/_libs/_17.mjs +2 -4
- package/.output/server/_libs/_18.mjs +2 -2
- package/.output/server/_libs/_19.mjs +2 -2
- package/.output/server/_libs/_2.mjs +3 -3
- package/.output/server/_libs/_20.mjs +2 -2
- package/.output/server/_libs/_21.mjs +2 -2
- package/.output/server/_libs/_22.mjs +2 -2
- package/.output/server/_libs/_23.mjs +2 -2
- package/.output/server/_libs/_24.mjs +2 -2
- package/.output/server/_libs/_25.mjs +2 -2
- package/.output/server/_libs/_26.mjs +2 -2
- package/.output/server/_libs/_27.mjs +2 -2
- package/.output/server/_libs/_28.mjs +2 -2
- package/.output/server/_libs/_29.mjs +2 -2
- package/.output/server/_libs/_3.mjs +3 -3
- package/.output/server/_libs/_30.mjs +2 -2
- package/.output/server/_libs/_31.mjs +2 -2
- package/.output/server/_libs/_32.mjs +2 -2
- package/.output/server/_libs/_33.mjs +2 -2
- package/.output/server/_libs/_34.mjs +2 -2
- package/.output/server/_libs/_35.mjs +2 -2
- package/.output/server/_libs/_36.mjs +2 -2
- package/.output/server/_libs/_37.mjs +2 -2
- package/.output/server/_libs/_38.mjs +2 -2
- package/.output/server/_libs/_39.mjs +2 -2
- package/.output/server/_libs/_4.mjs +4 -3
- package/.output/server/_libs/_40.mjs +2 -2
- package/.output/server/_libs/_41.mjs +2 -2
- package/.output/server/_libs/_42.mjs +2 -2
- package/.output/server/_libs/_43.mjs +2 -2
- package/.output/server/_libs/_44.mjs +2 -2
- package/.output/server/_libs/_45.mjs +2 -2
- package/.output/server/_libs/_46.mjs +2 -2
- package/.output/server/_libs/_47.mjs +2 -2
- package/.output/server/_libs/_48.mjs +2 -2
- package/.output/server/_libs/_49.mjs +2 -2
- package/.output/server/_libs/_5.mjs +2 -3
- package/.output/server/_libs/_50.mjs +2 -2
- package/.output/server/_libs/_51.mjs +2 -2
- package/.output/server/_libs/_52.mjs +2 -2
- package/.output/server/_libs/_53.mjs +2 -2
- package/.output/server/_libs/_54.mjs +2 -2
- package/.output/server/_libs/_55.mjs +2 -2
- package/.output/server/_libs/_56.mjs +2 -2
- package/.output/server/_libs/_57.mjs +2 -2
- package/.output/server/_libs/_58.mjs +2 -2
- package/.output/server/_libs/_59.mjs +2 -2
- package/.output/server/_libs/_6.mjs +2 -3
- package/.output/server/_libs/_60.mjs +2 -2
- package/.output/server/_libs/_61.mjs +2 -2
- package/.output/server/_libs/_62.mjs +2 -2
- package/.output/server/_libs/_63.mjs +2 -2
- package/.output/server/_libs/_64.mjs +2 -2
- package/.output/server/_libs/_65.mjs +2 -2
- package/.output/server/_libs/_66.mjs +2 -2
- package/.output/server/_libs/_67.mjs +2 -2
- package/.output/server/_libs/_68.mjs +2 -2
- package/.output/server/_libs/_69.mjs +2 -2
- package/.output/server/_libs/_7.mjs +2 -5
- package/.output/server/_libs/_70.mjs +2 -2
- package/.output/server/_libs/_71.mjs +2 -2
- package/.output/server/_libs/_72.mjs +2 -2
- package/.output/server/_libs/_73.mjs +2 -2
- package/.output/server/_libs/_74.mjs +2 -2
- package/.output/server/_libs/_75.mjs +2 -2
- package/.output/server/_libs/_76.mjs +2 -2
- package/.output/server/_libs/_77.mjs +2 -2
- package/.output/server/_libs/_78.mjs +2 -2
- package/.output/server/_libs/_79.mjs +2 -2
- package/.output/server/_libs/_8.mjs +2 -3
- package/.output/server/_libs/_80.mjs +2 -2
- package/.output/server/_libs/_81.mjs +2 -2
- package/.output/server/_libs/_82.mjs +2 -2
- package/.output/server/_libs/_83.mjs +2 -2
- package/.output/server/_libs/_84.mjs +2 -2
- package/.output/server/_libs/_85.mjs +2 -2
- package/.output/server/_libs/_86.mjs +2 -2
- package/.output/server/_libs/_87.mjs +2 -2
- package/.output/server/_libs/_88.mjs +2 -2
- package/.output/server/_libs/_89.mjs +2 -2
- package/.output/server/_libs/_9.mjs +2 -4
- package/.output/server/_libs/_90.mjs +5 -2
- package/.output/server/_libs/_91.mjs +3 -2
- package/.output/server/_libs/_92.mjs +2 -2
- package/.output/server/_libs/_93.mjs +2 -2
- package/.output/server/_libs/_94.mjs +2 -2
- package/.output/server/_libs/agent-base.mjs +1 -1
- package/.output/server/_libs/cheerio+[...].mjs +1 -1
- package/.output/server/_libs/data-uri-to-buffer.mjs +2 -67
- package/.output/server/_libs/data-urls+[...].mjs +1 -1
- package/.output/server/_libs/diff.mjs +1 -1
- package/.output/server/_libs/exodus__bytes.mjs +99 -81
- package/.output/server/_libs/fetch-blob+node-domexception.mjs +1 -1
- package/.output/server/_libs/h3+rou3+srvx.mjs +34 -4
- package/.output/server/_libs/html-encoding-sniffer.mjs +1 -1
- package/.output/server/_libs/https-proxy-agent.mjs +2 -2
- package/.output/server/_libs/jsdom.mjs +1 -1
- package/.output/server/_libs/just-bash+[...].mjs +4676 -3916
- package/.output/server/_libs/mariozechner__jiti.mjs +1 -1
- package/.output/server/_libs/mariozechner__pi-ai.mjs +1472 -0
- package/.output/server/_libs/md4x.mjs +1 -1
- package/.output/server/_libs/mime.mjs +838 -1
- package/.output/server/_libs/node-fetch.mjs +4 -4
- package/.output/server/_libs/node-liblzma.mjs +1 -1
- package/.output/server/_libs/silvia-odwyer__photon-node.mjs +1 -1
- package/.output/server/_routes/api/auth/approve.mjs +2 -0
- package/.output/server/_routes/api/auth/revoke.mjs +2 -0
- package/.output/server/_routes/api/auth/status.mjs +25 -6
- package/.output/server/_routes/api/browser2.mjs +1 -1
- package/.output/server/_routes/api/config2.mjs +2 -0
- package/.output/server/_routes/api/device_events.mjs +36 -0
- package/.output/server/_routes/api/files/groups.mjs +1 -2
- package/.output/server/_routes/api/files/raw.mjs +1 -1
- package/.output/server/_routes/api/groups.mjs +5 -3
- package/.output/server/_routes/api/groups2.mjs +18 -6
- package/.output/server/_routes/api/health.mjs +1 -2
- package/.output/server/_routes/api/messages.mjs +7 -1
- package/.output/server/_routes/api/notes/delete.mjs +4 -1
- package/.output/server/_routes/api/notes/write.mjs +2 -0
- package/.output/server/_routes/api/ntfy/setup.mjs +8 -0
- package/.output/server/_routes/api/pi/apikey.mjs +3 -2
- package/.output/server/_routes/api/pi/apikey_providers.mjs +1 -2
- package/.output/server/_routes/api/pi/commands.mjs +13 -3
- package/.output/server/_routes/api/pi/login/events.mjs +0 -1
- package/.output/server/_routes/api/pi/login/respond.mjs +2 -1
- package/.output/server/_routes/api/pi/login.mjs +1 -2
- package/.output/server/_routes/api/pi/logout.mjs +2 -1
- package/.output/server/_routes/api/pi/models.mjs +1 -2
- package/.output/server/_routes/api/pi/models_config2.mjs +2 -0
- package/.output/server/_routes/api/pi/settings2.mjs +2 -0
- package/.output/server/_routes/api/pi/status.mjs +1 -2
- package/.output/server/_routes/api/proxy.mjs +19 -1
- package/.output/server/_routes/api/sandbox.mjs +26 -0
- package/.output/server/_routes/api/sandbox2.mjs +17 -0
- package/.output/server/_routes/api/send.mjs +26 -18
- package/.output/server/_routes/api/status.mjs +1 -3
- package/.output/server/_routes/api/stop.mjs +11 -0
- package/.output/server/_routes/api/store/plugins.mjs +75 -0
- package/.output/server/_routes/api/store/skills.mjs +11 -0
- package/.output/server/_routes/api/tasks2.mjs +3 -2
- package/.output/server/_routes/api/telegram/setup.mjs +5 -2
- package/.output/server/_routes/api/telegram/status.mjs +1 -2
- package/.output/server/_routes/api/terminal2.mjs +2 -1
- package/.output/server/_routes/api/tunnel/setup.mjs +4 -2
- package/.output/server/_runtime.mjs +1 -2
- package/.output/server/_utils.mjs +10 -2
- package/.output/server/index.mjs +1 -1
- package/.output/server/node_modules/amdefine/amdefine.js +301 -0
- package/.output/server/node_modules/amdefine/package.json +16 -0
- package/.output/server/node_modules/compressjs/lib/BWT.js +420 -0
- package/.output/server/node_modules/compressjs/lib/BWTC.js +234 -0
- package/.output/server/node_modules/compressjs/lib/BitStream.js +108 -0
- package/.output/server/node_modules/compressjs/lib/Bzip2.js +936 -0
- package/.output/server/node_modules/compressjs/lib/CRC32.js +105 -0
- package/.output/server/node_modules/compressjs/lib/Context1Model.js +56 -0
- package/.output/server/node_modules/compressjs/lib/DefSumModel.js +152 -0
- package/.output/server/node_modules/compressjs/lib/DeflateDistanceModel.js +55 -0
- package/.output/server/node_modules/compressjs/lib/Dmc.js +197 -0
- package/.output/server/node_modules/compressjs/lib/DummyRangeCoder.js +81 -0
- package/.output/server/node_modules/compressjs/lib/FenwickModel.js +194 -0
- package/.output/server/node_modules/compressjs/lib/Huffman.js +514 -0
- package/.output/server/node_modules/compressjs/lib/HuffmanAllocator.js +227 -0
- package/.output/server/node_modules/compressjs/lib/LogDistanceModel.js +46 -0
- package/.output/server/node_modules/compressjs/lib/Lzjb.js +300 -0
- package/.output/server/node_modules/compressjs/lib/LzjbR.js +241 -0
- package/.output/server/node_modules/compressjs/lib/Lzp3.js +273 -0
- package/.output/server/node_modules/compressjs/lib/MTFModel.js +208 -0
- package/.output/server/node_modules/compressjs/lib/NoModel.js +46 -0
- package/.output/server/node_modules/compressjs/lib/PPM.js +343 -0
- package/.output/server/node_modules/compressjs/lib/RangeCoder.js +238 -0
- package/.output/server/node_modules/compressjs/lib/Simple.js +111 -0
- package/.output/server/node_modules/compressjs/lib/Stream.js +53 -0
- package/.output/server/node_modules/compressjs/lib/Util.js +324 -0
- package/.output/server/node_modules/compressjs/lib/freeze.js +14 -0
- package/.output/server/node_modules/compressjs/main.js +29 -0
- package/.output/server/node_modules/compressjs/package.json +35 -0
- package/.output/server/package.json +2 -1
- package/README.md +10 -1
- package/lib/index.d.mts +1 -0
- package/lib/index.mjs +1 -0
- package/lib/piclaw.mjs +100 -0
- package/lib/utils.mjs +96 -0
- package/package.json +16 -11
- package/.output/public/assets/defult-CMO6TZ5a.js +0 -1
- package/.output/public/assets/index-jdnbJw-M.js +0 -204
- package/.output/public/assets/index-ooXrRwgl.css +0 -1
- package/.output/server/_chunks/commands.mjs +0 -282
- package/.output/server/_chunks/pi.mjs +0 -202
- package/.output/server/_chunks/session.mjs +0 -1114
- package/.output/server/_libs/@aws-crypto/crc32+[...].mjs +0 -299
- package/.output/server/_libs/@aws-sdk/client-bedrock-runtime+[...].mjs +0 -17828
- package/.output/server/_libs/@aws-sdk/credential-provider-http+[...].mjs +0 -122
- package/.output/server/_libs/@aws-sdk/credential-provider-ini+[...].mjs +0 -417
- package/.output/server/_libs/@aws-sdk/credential-provider-process+[...].mjs +0 -54
- package/.output/server/_libs/@aws-sdk/credential-provider-sso+[...].mjs +0 -1151
- package/.output/server/_libs/@aws-sdk/credential-provider-web-identity+[...].mjs +0 -50
- package/.output/server/_libs/@smithy/credential-provider-imds+[...].mjs +0 -369
- package/.output/server/_libs/@tootallnate/quickjs-emscripten+[...].mjs +0 -3011
- package/.output/server/_libs/_100.mjs +0 -2
- package/.output/server/_libs/_101.mjs +0 -2
- package/.output/server/_libs/_102.mjs +0 -5
- package/.output/server/_libs/_103.mjs +0 -3
- package/.output/server/_libs/_104.mjs +0 -2
- package/.output/server/_libs/_105.mjs +0 -3
- package/.output/server/_libs/_106.mjs +0 -2
- package/.output/server/_libs/_107.mjs +0 -2
- package/.output/server/_libs/_95.mjs +0 -2
- package/.output/server/_libs/_96.mjs +0 -2
- package/.output/server/_libs/_97.mjs +0 -2
- package/.output/server/_libs/_98.mjs +0 -2
- package/.output/server/_libs/_99.mjs +0 -2
- package/.output/server/_libs/amdefine.mjs +0 -188
- package/.output/server/_libs/ast-types.mjs +0 -2270
- package/.output/server/_libs/aws-sdk__nested-clients.mjs +0 -3141
- package/.output/server/_libs/basic-ftp.mjs +0 -1906
- package/.output/server/_libs/compressjs.mjs +0 -50
- package/.output/server/_libs/degenerator+[...].mjs +0 -9964
- package/.output/server/_libs/get-uri.mjs +0 -413
- package/.output/server/_libs/http-proxy-agent.mjs +0 -123
- package/.output/server/_libs/ip-address.mjs +0 -1423
- package/.output/server/_libs/lru-cache.mjs +0 -732
- package/.output/server/_libs/netmask.mjs +0 -139
- package/.output/server/_libs/pac-proxy-agent+[...].mjs +0 -3104
- package/.output/server/_libs/proxy-agent+proxy-from-env.mjs +0 -204
- package/.output/server/_libs/smithy__core.mjs +0 -192
- package/.output/server/node_modules/tslib/modules/index.js +0 -70
- package/.output/server/node_modules/tslib/modules/package.json +0 -3
- package/.output/server/node_modules/tslib/package.json +0 -47
- package/.output/server/node_modules/tslib/tslib.js +0 -484
- package/bin/piclaw.mjs +0 -195
|
@@ -1,1906 +0,0 @@
|
|
|
1
|
-
import { a as __require, t as __commonJSMin } from "../_runtime.mjs";
|
|
2
|
-
var require_parseControlResponse = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.parseControlResponse = parseControlResponse;
|
|
5
|
-
exports.isSingleLine = isSingleLine;
|
|
6
|
-
exports.isMultiline = isMultiline;
|
|
7
|
-
exports.positiveCompletion = positiveCompletion;
|
|
8
|
-
exports.positiveIntermediate = positiveIntermediate;
|
|
9
|
-
var LF = "\n";
|
|
10
|
-
/**
|
|
11
|
-
* Parse an FTP control response as a collection of messages. A message is a complete
|
|
12
|
-
* single- or multiline response. A response can also contain multiple multiline responses
|
|
13
|
-
* that will each be represented by a message. A response can also be incomplete
|
|
14
|
-
* and be completed on the next incoming data chunk for which case this function also
|
|
15
|
-
* describes a `rest`. This function converts all CRLF to LF.
|
|
16
|
-
*/
|
|
17
|
-
function parseControlResponse(text) {
|
|
18
|
-
const lines = text.split(/\r?\n/).filter(isNotBlank);
|
|
19
|
-
const messages = [];
|
|
20
|
-
let startAt = 0;
|
|
21
|
-
let tokenRegex;
|
|
22
|
-
for (let i = 0; i < lines.length; i++) {
|
|
23
|
-
const line = lines[i];
|
|
24
|
-
if (!tokenRegex) {
|
|
25
|
-
if (isMultiline(line)) {
|
|
26
|
-
const token = line.substr(0, 3);
|
|
27
|
-
tokenRegex = new RegExp(`^${token}(?:$| )`);
|
|
28
|
-
startAt = i;
|
|
29
|
-
} else if (isSingleLine(line)) messages.push(line);
|
|
30
|
-
} else if (tokenRegex.test(line)) {
|
|
31
|
-
tokenRegex = void 0;
|
|
32
|
-
messages.push(lines.slice(startAt, i + 1).join(LF));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
messages,
|
|
37
|
-
rest: tokenRegex ? lines.slice(startAt).join(LF) + LF : ""
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function isSingleLine(line) {
|
|
41
|
-
return /^\d\d\d(?:$| )/.test(line);
|
|
42
|
-
}
|
|
43
|
-
function isMultiline(line) {
|
|
44
|
-
return /^\d\d\d-/.test(line);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Return true if an FTP return code describes a positive completion.
|
|
48
|
-
*/
|
|
49
|
-
function positiveCompletion(code) {
|
|
50
|
-
return code >= 200 && code < 300;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Return true if an FTP return code describes a positive intermediate response.
|
|
54
|
-
*/
|
|
55
|
-
function positiveIntermediate(code) {
|
|
56
|
-
return code >= 300 && code < 400;
|
|
57
|
-
}
|
|
58
|
-
function isNotBlank(str) {
|
|
59
|
-
return str.trim() !== "";
|
|
60
|
-
}
|
|
61
|
-
}));
|
|
62
|
-
var require_FtpContext = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
63
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
-
exports.FTPContext = exports.FTPError = void 0;
|
|
65
|
-
var net_1 = __require("net");
|
|
66
|
-
var parseControlResponse_1 = require_parseControlResponse();
|
|
67
|
-
/**
|
|
68
|
-
* Describes an FTP server error response including the FTP response code.
|
|
69
|
-
*/
|
|
70
|
-
var FTPError = class extends Error {
|
|
71
|
-
constructor(res) {
|
|
72
|
-
super(res.message);
|
|
73
|
-
this.name = this.constructor.name;
|
|
74
|
-
this.code = res.code;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
exports.FTPError = FTPError;
|
|
78
|
-
function doNothing() {
|
|
79
|
-
/** Do nothing */
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* FTPContext holds the control and data sockets of an FTP connection and provides a
|
|
83
|
-
* simplified way to interact with an FTP server, handle responses, errors and timeouts.
|
|
84
|
-
*
|
|
85
|
-
* It doesn't implement or use any FTP commands. It's only a foundation to make writing an FTP
|
|
86
|
-
* client as easy as possible. You won't usually instantiate this, but use `Client`.
|
|
87
|
-
*/
|
|
88
|
-
var FTPContext = class {
|
|
89
|
-
/**
|
|
90
|
-
* Instantiate an FTP context.
|
|
91
|
-
*
|
|
92
|
-
* @param timeout - Timeout in milliseconds to apply to control and data connections. Use 0 for no timeout.
|
|
93
|
-
* @param encoding - Encoding to use for control connection. UTF-8 by default. Use "latin1" for older servers.
|
|
94
|
-
*/
|
|
95
|
-
constructor(timeout = 0, encoding = "utf8") {
|
|
96
|
-
this.timeout = timeout;
|
|
97
|
-
/** Debug-level logging of all socket communication. */
|
|
98
|
-
this.verbose = false;
|
|
99
|
-
/** IP version to prefer (4: IPv4, 6: IPv6, undefined: automatic). */
|
|
100
|
-
this.ipFamily = void 0;
|
|
101
|
-
/** Options for TLS connections. */
|
|
102
|
-
this.tlsOptions = {};
|
|
103
|
-
/** A multiline response might be received as multiple chunks. */
|
|
104
|
-
this._partialResponse = "";
|
|
105
|
-
this._encoding = encoding;
|
|
106
|
-
this._socket = this.socket = this._newSocket();
|
|
107
|
-
this._dataSocket = void 0;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Close the context.
|
|
111
|
-
*/
|
|
112
|
-
close() {
|
|
113
|
-
const message = this._task ? "User closed client during task" : "User closed client";
|
|
114
|
-
const err = new Error(message);
|
|
115
|
-
this.closeWithError(err);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Close the context with an error.
|
|
119
|
-
*/
|
|
120
|
-
closeWithError(err) {
|
|
121
|
-
if (this._closingError) return;
|
|
122
|
-
this._closingError = err;
|
|
123
|
-
this._closeControlSocket();
|
|
124
|
-
this._closeSocket(this._dataSocket);
|
|
125
|
-
this._passToHandler(err);
|
|
126
|
-
this._stopTrackingTask();
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Returns true if this context has been closed or hasn't been connected yet. You can reopen it with `access`.
|
|
130
|
-
*/
|
|
131
|
-
get closed() {
|
|
132
|
-
return this.socket.remoteAddress === void 0 || this._closingError !== void 0;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Reset this contex and all of its state.
|
|
136
|
-
*/
|
|
137
|
-
reset() {
|
|
138
|
-
this.socket = this._newSocket();
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Get the FTP control socket.
|
|
142
|
-
*/
|
|
143
|
-
get socket() {
|
|
144
|
-
return this._socket;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Set the socket for the control connection. This will only close the current control socket
|
|
148
|
-
* if the new one is not an upgrade to the current one.
|
|
149
|
-
*/
|
|
150
|
-
set socket(socket) {
|
|
151
|
-
this.dataSocket = void 0;
|
|
152
|
-
this.tlsOptions = {};
|
|
153
|
-
this._partialResponse = "";
|
|
154
|
-
if (this._socket) if (socket.localPort === this._socket.localPort) this._removeSocketListeners(this.socket);
|
|
155
|
-
else this._closeControlSocket();
|
|
156
|
-
if (socket) {
|
|
157
|
-
this._closingError = void 0;
|
|
158
|
-
socket.setTimeout(0);
|
|
159
|
-
socket.setEncoding(this._encoding);
|
|
160
|
-
socket.setKeepAlive(true);
|
|
161
|
-
socket.on("data", (data) => this._onControlSocketData(data));
|
|
162
|
-
socket.on("end", () => this.closeWithError(/* @__PURE__ */ new Error("Server sent FIN packet unexpectedly, closing connection.")));
|
|
163
|
-
socket.on("close", (hadError) => {
|
|
164
|
-
if (!hadError) this.closeWithError(/* @__PURE__ */ new Error("Server closed connection unexpectedly."));
|
|
165
|
-
});
|
|
166
|
-
this._setupDefaultErrorHandlers(socket, "control socket");
|
|
167
|
-
}
|
|
168
|
-
this._socket = socket;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Get the current FTP data connection if present.
|
|
172
|
-
*/
|
|
173
|
-
get dataSocket() {
|
|
174
|
-
return this._dataSocket;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Set the socket for the data connection. This will automatically close the former data socket.
|
|
178
|
-
*/
|
|
179
|
-
set dataSocket(socket) {
|
|
180
|
-
this._closeSocket(this._dataSocket);
|
|
181
|
-
if (socket) {
|
|
182
|
-
socket.setTimeout(0);
|
|
183
|
-
this._setupDefaultErrorHandlers(socket, "data socket");
|
|
184
|
-
}
|
|
185
|
-
this._dataSocket = socket;
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Get the currently used encoding.
|
|
189
|
-
*/
|
|
190
|
-
get encoding() {
|
|
191
|
-
return this._encoding;
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Set the encoding used for the control socket.
|
|
195
|
-
*
|
|
196
|
-
* See https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings for what encodings
|
|
197
|
-
* are supported by Node.
|
|
198
|
-
*/
|
|
199
|
-
set encoding(encoding) {
|
|
200
|
-
this._encoding = encoding;
|
|
201
|
-
if (this.socket) this.socket.setEncoding(encoding);
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Send an FTP command without waiting for or handling the result.
|
|
205
|
-
*/
|
|
206
|
-
send(command) {
|
|
207
|
-
const message = command.startsWith("PASS") ? "> PASS ###" : `> ${command}`;
|
|
208
|
-
this.log(message);
|
|
209
|
-
this._socket.write(command + "\r\n", this.encoding);
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Send an FTP command and handle the first response. Use this if you have a simple
|
|
213
|
-
* request-response situation.
|
|
214
|
-
*/
|
|
215
|
-
request(command) {
|
|
216
|
-
return this.handle(command, (res, task) => {
|
|
217
|
-
if (res instanceof Error) task.reject(res);
|
|
218
|
-
else task.resolve(res);
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Send an FTP command and handle any response until you resolve/reject. Use this if you expect multiple responses
|
|
223
|
-
* to a request. This returns a Promise that will hold whatever the response handler passed on when resolving/rejecting its task.
|
|
224
|
-
*/
|
|
225
|
-
handle(command, responseHandler) {
|
|
226
|
-
if (this._task) {
|
|
227
|
-
const err = /* @__PURE__ */ new Error("User launched a task while another one is still running. Forgot to use 'await' or '.then()'?");
|
|
228
|
-
err.stack += `\nRunning task launched at: ${this._task.stack}`;
|
|
229
|
-
this.closeWithError(err);
|
|
230
|
-
}
|
|
231
|
-
return new Promise((resolveTask, rejectTask) => {
|
|
232
|
-
this._task = {
|
|
233
|
-
stack: (/* @__PURE__ */ new Error()).stack || "Unknown call stack",
|
|
234
|
-
responseHandler,
|
|
235
|
-
resolver: {
|
|
236
|
-
resolve: (arg) => {
|
|
237
|
-
this._stopTrackingTask();
|
|
238
|
-
resolveTask(arg);
|
|
239
|
-
},
|
|
240
|
-
reject: (err) => {
|
|
241
|
-
this._stopTrackingTask();
|
|
242
|
-
rejectTask(err);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
if (this._closingError) {
|
|
247
|
-
const err = /* @__PURE__ */ new Error(`Client is closed because ${this._closingError.message}`);
|
|
248
|
-
err.stack += `\nClosing reason: ${this._closingError.stack}`;
|
|
249
|
-
err.code = this._closingError.code !== void 0 ? this._closingError.code : "0";
|
|
250
|
-
this._passToHandler(err);
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
this.socket.setTimeout(this.timeout);
|
|
254
|
-
if (command) this.send(command);
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Log message if set to be verbose.
|
|
259
|
-
*/
|
|
260
|
-
log(message) {
|
|
261
|
-
if (this.verbose) console.log(message);
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Return true if the control socket is using TLS. This does not mean that a session
|
|
265
|
-
* has already been negotiated.
|
|
266
|
-
*/
|
|
267
|
-
get hasTLS() {
|
|
268
|
-
return "encrypted" in this._socket;
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Removes reference to current task and handler. This won't resolve or reject the task.
|
|
272
|
-
* @protected
|
|
273
|
-
*/
|
|
274
|
-
_stopTrackingTask() {
|
|
275
|
-
this.socket.setTimeout(0);
|
|
276
|
-
this._task = void 0;
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Handle incoming data on the control socket. The chunk is going to be of type `string`
|
|
280
|
-
* because we let `socket` handle encoding with `setEncoding`.
|
|
281
|
-
* @protected
|
|
282
|
-
*/
|
|
283
|
-
_onControlSocketData(chunk) {
|
|
284
|
-
this.log(`< ${chunk}`);
|
|
285
|
-
const completeResponse = this._partialResponse + chunk;
|
|
286
|
-
const parsed = (0, parseControlResponse_1.parseControlResponse)(completeResponse);
|
|
287
|
-
this._partialResponse = parsed.rest;
|
|
288
|
-
for (const message of parsed.messages) {
|
|
289
|
-
const code = parseInt(message.substr(0, 3), 10);
|
|
290
|
-
const response = {
|
|
291
|
-
code,
|
|
292
|
-
message
|
|
293
|
-
};
|
|
294
|
-
const err = code >= 400 ? new FTPError(response) : void 0;
|
|
295
|
-
this._passToHandler(err ? err : response);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Send the current handler a response. This is usually a control socket response
|
|
300
|
-
* or a socket event, like an error or timeout.
|
|
301
|
-
* @protected
|
|
302
|
-
*/
|
|
303
|
-
_passToHandler(response) {
|
|
304
|
-
if (this._task) this._task.responseHandler(response, this._task.resolver);
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Setup all error handlers for a socket.
|
|
308
|
-
* @protected
|
|
309
|
-
*/
|
|
310
|
-
_setupDefaultErrorHandlers(socket, identifier) {
|
|
311
|
-
socket.once("error", (error) => {
|
|
312
|
-
error.message += ` (${identifier})`;
|
|
313
|
-
this.closeWithError(error);
|
|
314
|
-
});
|
|
315
|
-
socket.once("close", (hadError) => {
|
|
316
|
-
if (hadError) this.closeWithError(/* @__PURE__ */ new Error(`Socket closed due to transmission error (${identifier})`));
|
|
317
|
-
});
|
|
318
|
-
socket.once("timeout", () => {
|
|
319
|
-
socket.destroy();
|
|
320
|
-
this.closeWithError(/* @__PURE__ */ new Error(`Timeout (${identifier})`));
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Close the control socket. Sends QUIT, then FIN, and ignores any response or error.
|
|
325
|
-
*/
|
|
326
|
-
_closeControlSocket() {
|
|
327
|
-
this._removeSocketListeners(this._socket);
|
|
328
|
-
this._socket.on("error", doNothing);
|
|
329
|
-
this.send("QUIT");
|
|
330
|
-
this._closeSocket(this._socket);
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Close a socket, ignores any error.
|
|
334
|
-
* @protected
|
|
335
|
-
*/
|
|
336
|
-
_closeSocket(socket) {
|
|
337
|
-
if (socket) {
|
|
338
|
-
this._removeSocketListeners(socket);
|
|
339
|
-
socket.on("error", doNothing);
|
|
340
|
-
socket.destroy();
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* Remove all default listeners for socket.
|
|
345
|
-
* @protected
|
|
346
|
-
*/
|
|
347
|
-
_removeSocketListeners(socket) {
|
|
348
|
-
socket.removeAllListeners();
|
|
349
|
-
socket.removeAllListeners("timeout");
|
|
350
|
-
socket.removeAllListeners("data");
|
|
351
|
-
socket.removeAllListeners("end");
|
|
352
|
-
socket.removeAllListeners("error");
|
|
353
|
-
socket.removeAllListeners("close");
|
|
354
|
-
socket.removeAllListeners("connect");
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Provide a new socket instance.
|
|
358
|
-
*
|
|
359
|
-
* Internal use only, replaced for unit tests.
|
|
360
|
-
*/
|
|
361
|
-
_newSocket() {
|
|
362
|
-
return new net_1.Socket();
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
exports.FTPContext = FTPContext;
|
|
366
|
-
}));
|
|
367
|
-
var require_FileInfo = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
368
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
369
|
-
exports.FileInfo = exports.FileType = void 0;
|
|
370
|
-
var FileType;
|
|
371
|
-
(function(FileType) {
|
|
372
|
-
FileType[FileType["Unknown"] = 0] = "Unknown";
|
|
373
|
-
FileType[FileType["File"] = 1] = "File";
|
|
374
|
-
FileType[FileType["Directory"] = 2] = "Directory";
|
|
375
|
-
FileType[FileType["SymbolicLink"] = 3] = "SymbolicLink";
|
|
376
|
-
})(FileType || (exports.FileType = FileType = {}));
|
|
377
|
-
/**
|
|
378
|
-
* Describes a file, directory or symbolic link.
|
|
379
|
-
*/
|
|
380
|
-
var FileInfo = class {
|
|
381
|
-
constructor(name) {
|
|
382
|
-
this.name = name;
|
|
383
|
-
this.type = FileType.Unknown;
|
|
384
|
-
this.size = 0;
|
|
385
|
-
/**
|
|
386
|
-
* Unparsed, raw modification date as a string.
|
|
387
|
-
*
|
|
388
|
-
* If `modifiedAt` is undefined, the FTP server you're connected to doesn't support the more modern
|
|
389
|
-
* MLSD command for machine-readable directory listings. The older command LIST is then used returning
|
|
390
|
-
* results that vary a lot between servers as the format hasn't been standardized. Here, directory listings
|
|
391
|
-
* and especially modification dates were meant to be human-readable first.
|
|
392
|
-
*
|
|
393
|
-
* Be careful when still trying to parse this by yourself. Parsing dates from listings using LIST is
|
|
394
|
-
* unreliable. This library decides to offer parsed dates only when they're absolutely reliable and safe to
|
|
395
|
-
* use e.g. for comparisons.
|
|
396
|
-
*/
|
|
397
|
-
this.rawModifiedAt = "";
|
|
398
|
-
/**
|
|
399
|
-
* Parsed modification date.
|
|
400
|
-
*
|
|
401
|
-
* Available if the FTP server supports the MLSD command. Only MLSD guarantees dates than can be reliably
|
|
402
|
-
* parsed with the correct timezone and a resolution down to seconds. See `rawModifiedAt` property for the unparsed
|
|
403
|
-
* date that is always available.
|
|
404
|
-
*/
|
|
405
|
-
this.modifiedAt = void 0;
|
|
406
|
-
/**
|
|
407
|
-
* Unix permissions if present. If the underlying FTP server is not running on Unix this will be undefined.
|
|
408
|
-
* If set, you might be able to edit permissions with the FTP command `SITE CHMOD`.
|
|
409
|
-
*/
|
|
410
|
-
this.permissions = void 0;
|
|
411
|
-
/**
|
|
412
|
-
* Hard link count if available.
|
|
413
|
-
*/
|
|
414
|
-
this.hardLinkCount = void 0;
|
|
415
|
-
/**
|
|
416
|
-
* Link name for symbolic links if available.
|
|
417
|
-
*/
|
|
418
|
-
this.link = void 0;
|
|
419
|
-
/**
|
|
420
|
-
* Unix group if available.
|
|
421
|
-
*/
|
|
422
|
-
this.group = void 0;
|
|
423
|
-
/**
|
|
424
|
-
* Unix user if available.
|
|
425
|
-
*/
|
|
426
|
-
this.user = void 0;
|
|
427
|
-
/**
|
|
428
|
-
* Unique ID if available.
|
|
429
|
-
*/
|
|
430
|
-
this.uniqueID = void 0;
|
|
431
|
-
this.name = name;
|
|
432
|
-
}
|
|
433
|
-
get isDirectory() {
|
|
434
|
-
return this.type === FileType.Directory;
|
|
435
|
-
}
|
|
436
|
-
get isSymbolicLink() {
|
|
437
|
-
return this.type === FileType.SymbolicLink;
|
|
438
|
-
}
|
|
439
|
-
get isFile() {
|
|
440
|
-
return this.type === FileType.File;
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* Deprecated, legacy API. Use `rawModifiedAt` instead.
|
|
444
|
-
* @deprecated
|
|
445
|
-
*/
|
|
446
|
-
get date() {
|
|
447
|
-
return this.rawModifiedAt;
|
|
448
|
-
}
|
|
449
|
-
set date(rawModifiedAt) {
|
|
450
|
-
this.rawModifiedAt = rawModifiedAt;
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
exports.FileInfo = FileInfo;
|
|
454
|
-
FileInfo.UnixPermission = {
|
|
455
|
-
Read: 4,
|
|
456
|
-
Write: 2,
|
|
457
|
-
Execute: 1
|
|
458
|
-
};
|
|
459
|
-
}));
|
|
460
|
-
var require_parseListDOS = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
461
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
462
|
-
exports.testLine = testLine;
|
|
463
|
-
exports.parseLine = parseLine;
|
|
464
|
-
exports.transformList = transformList;
|
|
465
|
-
var FileInfo_1 = require_FileInfo();
|
|
466
|
-
/**
|
|
467
|
-
* This parser is based on the FTP client library source code in Apache Commons Net provided
|
|
468
|
-
* under the Apache 2.0 license. It has been simplified and rewritten to better fit the Javascript language.
|
|
469
|
-
*
|
|
470
|
-
* https://github.com/apache/commons-net/blob/master/src/main/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java
|
|
471
|
-
*/
|
|
472
|
-
var RE_LINE = /* @__PURE__ */ new RegExp("(\\S+)\\s+(\\S+)\\s+(?:(<DIR>)|([0-9]+))\\s+(\\S.*)");
|
|
473
|
-
/**
|
|
474
|
-
* Returns true if a given line might be a DOS-style listing.
|
|
475
|
-
*
|
|
476
|
-
* - Example: `12-05-96 05:03PM <DIR> myDir`
|
|
477
|
-
*/
|
|
478
|
-
function testLine(line) {
|
|
479
|
-
return /^\d{2}/.test(line) && RE_LINE.test(line);
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Parse a single line of a DOS-style directory listing.
|
|
483
|
-
*/
|
|
484
|
-
function parseLine(line) {
|
|
485
|
-
const groups = line.match(RE_LINE);
|
|
486
|
-
if (groups === null) return;
|
|
487
|
-
const name = groups[5];
|
|
488
|
-
if (name === "." || name === "..") return;
|
|
489
|
-
const file = new FileInfo_1.FileInfo(name);
|
|
490
|
-
if (groups[3] === "<DIR>") {
|
|
491
|
-
file.type = FileInfo_1.FileType.Directory;
|
|
492
|
-
file.size = 0;
|
|
493
|
-
} else {
|
|
494
|
-
file.type = FileInfo_1.FileType.File;
|
|
495
|
-
file.size = parseInt(groups[4], 10);
|
|
496
|
-
}
|
|
497
|
-
file.rawModifiedAt = groups[1] + " " + groups[2];
|
|
498
|
-
return file;
|
|
499
|
-
}
|
|
500
|
-
function transformList(files) {
|
|
501
|
-
return files;
|
|
502
|
-
}
|
|
503
|
-
}));
|
|
504
|
-
var require_parseListUnix = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
505
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
506
|
-
exports.testLine = testLine;
|
|
507
|
-
exports.parseLine = parseLine;
|
|
508
|
-
exports.transformList = transformList;
|
|
509
|
-
var FileInfo_1 = require_FileInfo();
|
|
510
|
-
/**
|
|
511
|
-
* This parser is based on the FTP client library source code in Apache Commons Net provided
|
|
512
|
-
* under the Apache 2.0 license. It has been simplified and rewritten to better fit the Javascript language.
|
|
513
|
-
*
|
|
514
|
-
* https://github.com/apache/commons-net/blob/master/src/main/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java
|
|
515
|
-
*
|
|
516
|
-
* Below is the regular expression used by this parser.
|
|
517
|
-
*
|
|
518
|
-
* Permissions:
|
|
519
|
-
* r the file is readable
|
|
520
|
-
* w the file is writable
|
|
521
|
-
* x the file is executable
|
|
522
|
-
* - the indicated permission is not granted
|
|
523
|
-
* L mandatory locking occurs during access (the set-group-ID bit is
|
|
524
|
-
* on and the group execution bit is off)
|
|
525
|
-
* s the set-user-ID or set-group-ID bit is on, and the corresponding
|
|
526
|
-
* user or group execution bit is also on
|
|
527
|
-
* S undefined bit-state (the set-user-ID bit is on and the user
|
|
528
|
-
* execution bit is off)
|
|
529
|
-
* t the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
|
|
530
|
-
* execution is on
|
|
531
|
-
* T the 1000 bit is turned on, and execution is off (undefined bit-
|
|
532
|
-
* state)
|
|
533
|
-
* e z/OS external link bit
|
|
534
|
-
* Final letter may be appended:
|
|
535
|
-
* + file has extended security attributes (e.g. ACL)
|
|
536
|
-
* Note: local listings on MacOSX also use '@'
|
|
537
|
-
* this is not allowed for here as does not appear to be shown by FTP servers
|
|
538
|
-
* {@code @} file has extended attributes
|
|
539
|
-
*/
|
|
540
|
-
var RE_LINE = /* @__PURE__ */ new RegExp("([bcdelfmpSs-])(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]?)))\\+?\\s*(\\d+)\\s+(?:(\\S+(?:\\s\\S+)*?)\\s+)?(?:(\\S+(?:\\s\\S+)*)\\s+)?(\\d+(?:,\\s*\\d+)?)\\s+((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S{3}\\s+\\d{1,2})|(?:\\d{1,2}\\s+\\S{3})|(?:\\d{1,2}月\\s+\\d{1,2}日))\\s+((?:\\d+(?::\\d+)?)|(?:\\d{4}年))\\s(.*)");
|
|
541
|
-
/**
|
|
542
|
-
* Returns true if a given line might be a Unix-style listing.
|
|
543
|
-
*
|
|
544
|
-
* - Example: `-rw-r--r--+ 1 patrick staff 1057 Dec 11 14:35 test.txt`
|
|
545
|
-
*/
|
|
546
|
-
function testLine(line) {
|
|
547
|
-
return RE_LINE.test(line);
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* Parse a single line of a Unix-style directory listing.
|
|
551
|
-
*/
|
|
552
|
-
function parseLine(line) {
|
|
553
|
-
const groups = line.match(RE_LINE);
|
|
554
|
-
if (groups === null) return;
|
|
555
|
-
const name = groups[21];
|
|
556
|
-
if (name === "." || name === "..") return;
|
|
557
|
-
const file = new FileInfo_1.FileInfo(name);
|
|
558
|
-
file.size = parseInt(groups[18], 10);
|
|
559
|
-
file.user = groups[16];
|
|
560
|
-
file.group = groups[17];
|
|
561
|
-
file.hardLinkCount = parseInt(groups[15], 10);
|
|
562
|
-
file.rawModifiedAt = groups[19] + " " + groups[20];
|
|
563
|
-
file.permissions = {
|
|
564
|
-
user: parseMode(groups[4], groups[5], groups[6]),
|
|
565
|
-
group: parseMode(groups[8], groups[9], groups[10]),
|
|
566
|
-
world: parseMode(groups[12], groups[13], groups[14])
|
|
567
|
-
};
|
|
568
|
-
switch (groups[1].charAt(0)) {
|
|
569
|
-
case "d":
|
|
570
|
-
file.type = FileInfo_1.FileType.Directory;
|
|
571
|
-
break;
|
|
572
|
-
case "e":
|
|
573
|
-
file.type = FileInfo_1.FileType.SymbolicLink;
|
|
574
|
-
break;
|
|
575
|
-
case "l":
|
|
576
|
-
file.type = FileInfo_1.FileType.SymbolicLink;
|
|
577
|
-
break;
|
|
578
|
-
case "b":
|
|
579
|
-
case "c":
|
|
580
|
-
file.type = FileInfo_1.FileType.File;
|
|
581
|
-
break;
|
|
582
|
-
case "f":
|
|
583
|
-
case "-":
|
|
584
|
-
file.type = FileInfo_1.FileType.File;
|
|
585
|
-
break;
|
|
586
|
-
default: file.type = FileInfo_1.FileType.Unknown;
|
|
587
|
-
}
|
|
588
|
-
if (file.isSymbolicLink) {
|
|
589
|
-
const end = name.indexOf(" -> ");
|
|
590
|
-
if (end !== -1) {
|
|
591
|
-
file.name = name.substring(0, end);
|
|
592
|
-
file.link = name.substring(end + 4);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
return file;
|
|
596
|
-
}
|
|
597
|
-
function transformList(files) {
|
|
598
|
-
return files;
|
|
599
|
-
}
|
|
600
|
-
function parseMode(r, w, x) {
|
|
601
|
-
let value = 0;
|
|
602
|
-
if (r !== "-") value += FileInfo_1.FileInfo.UnixPermission.Read;
|
|
603
|
-
if (w !== "-") value += FileInfo_1.FileInfo.UnixPermission.Write;
|
|
604
|
-
const execToken = x.charAt(0);
|
|
605
|
-
if (execToken !== "-" && execToken.toUpperCase() !== execToken) value += FileInfo_1.FileInfo.UnixPermission.Execute;
|
|
606
|
-
return value;
|
|
607
|
-
}
|
|
608
|
-
}));
|
|
609
|
-
var require_parseListMLSD = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
610
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
611
|
-
exports.testLine = testLine;
|
|
612
|
-
exports.parseLine = parseLine;
|
|
613
|
-
exports.transformList = transformList;
|
|
614
|
-
exports.parseMLSxDate = parseMLSxDate;
|
|
615
|
-
var FileInfo_1 = require_FileInfo();
|
|
616
|
-
function parseSize(value, info) {
|
|
617
|
-
info.size = parseInt(value, 10);
|
|
618
|
-
}
|
|
619
|
-
/**
|
|
620
|
-
* Parsers for MLSD facts.
|
|
621
|
-
*/
|
|
622
|
-
var factHandlersByName = {
|
|
623
|
-
"size": parseSize,
|
|
624
|
-
"sizd": parseSize,
|
|
625
|
-
"unique": (value, info) => {
|
|
626
|
-
info.uniqueID = value;
|
|
627
|
-
},
|
|
628
|
-
"modify": (value, info) => {
|
|
629
|
-
info.modifiedAt = parseMLSxDate(value);
|
|
630
|
-
info.rawModifiedAt = info.modifiedAt.toISOString();
|
|
631
|
-
},
|
|
632
|
-
"type": (value, info) => {
|
|
633
|
-
if (value.startsWith("OS.unix=slink")) {
|
|
634
|
-
info.type = FileInfo_1.FileType.SymbolicLink;
|
|
635
|
-
info.link = value.substr(value.indexOf(":") + 1);
|
|
636
|
-
return 1;
|
|
637
|
-
}
|
|
638
|
-
switch (value) {
|
|
639
|
-
case "file":
|
|
640
|
-
info.type = FileInfo_1.FileType.File;
|
|
641
|
-
break;
|
|
642
|
-
case "dir":
|
|
643
|
-
info.type = FileInfo_1.FileType.Directory;
|
|
644
|
-
break;
|
|
645
|
-
case "OS.unix=symlink":
|
|
646
|
-
info.type = FileInfo_1.FileType.SymbolicLink;
|
|
647
|
-
break;
|
|
648
|
-
case "cdir":
|
|
649
|
-
case "pdir": return 2;
|
|
650
|
-
default: info.type = FileInfo_1.FileType.Unknown;
|
|
651
|
-
}
|
|
652
|
-
return 1;
|
|
653
|
-
},
|
|
654
|
-
"unix.mode": (value, info) => {
|
|
655
|
-
const digits = value.substr(-3);
|
|
656
|
-
info.permissions = {
|
|
657
|
-
user: parseInt(digits[0], 10),
|
|
658
|
-
group: parseInt(digits[1], 10),
|
|
659
|
-
world: parseInt(digits[2], 10)
|
|
660
|
-
};
|
|
661
|
-
},
|
|
662
|
-
"unix.ownername": (value, info) => {
|
|
663
|
-
info.user = value;
|
|
664
|
-
},
|
|
665
|
-
"unix.owner": (value, info) => {
|
|
666
|
-
if (info.user === void 0) info.user = value;
|
|
667
|
-
},
|
|
668
|
-
get "unix.uid"() {
|
|
669
|
-
return this["unix.owner"];
|
|
670
|
-
},
|
|
671
|
-
"unix.groupname": (value, info) => {
|
|
672
|
-
info.group = value;
|
|
673
|
-
},
|
|
674
|
-
"unix.group": (value, info) => {
|
|
675
|
-
if (info.group === void 0) info.group = value;
|
|
676
|
-
},
|
|
677
|
-
get "unix.gid"() {
|
|
678
|
-
return this["unix.group"];
|
|
679
|
-
}
|
|
680
|
-
};
|
|
681
|
-
/**
|
|
682
|
-
* Split a string once at the first position of a delimiter. For example
|
|
683
|
-
* `splitStringOnce("a b c d", " ")` returns `["a", "b c d"]`.
|
|
684
|
-
*/
|
|
685
|
-
function splitStringOnce(str, delimiter) {
|
|
686
|
-
const pos = str.indexOf(delimiter);
|
|
687
|
-
return [str.substr(0, pos), str.substr(pos + delimiter.length)];
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Returns true if a given line might be part of an MLSD listing.
|
|
691
|
-
*
|
|
692
|
-
* - Example 1: `size=15227;type=dir;perm=el;modify=20190419065730; test one`
|
|
693
|
-
* - Example 2: ` file name` (leading space)
|
|
694
|
-
*/
|
|
695
|
-
function testLine(line) {
|
|
696
|
-
return /^\S+=\S+;/.test(line) || line.startsWith(" ");
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Parse single line as MLSD listing, see specification at https://tools.ietf.org/html/rfc3659#section-7.
|
|
700
|
-
*/
|
|
701
|
-
function parseLine(line) {
|
|
702
|
-
const [packedFacts, name] = splitStringOnce(line, " ");
|
|
703
|
-
if (name === "" || name === "." || name === "..") return;
|
|
704
|
-
const info = new FileInfo_1.FileInfo(name);
|
|
705
|
-
const facts = packedFacts.split(";");
|
|
706
|
-
for (const fact of facts) {
|
|
707
|
-
const [factName, factValue] = splitStringOnce(fact, "=");
|
|
708
|
-
if (!factValue) continue;
|
|
709
|
-
const factHandler = factHandlersByName[factName.toLowerCase()];
|
|
710
|
-
if (!factHandler) continue;
|
|
711
|
-
if (factHandler(factValue, info) === 2) return;
|
|
712
|
-
}
|
|
713
|
-
return info;
|
|
714
|
-
}
|
|
715
|
-
function transformList(files) {
|
|
716
|
-
const nonLinksByID = /* @__PURE__ */ new Map();
|
|
717
|
-
for (const file of files) if (!file.isSymbolicLink && file.uniqueID !== void 0) nonLinksByID.set(file.uniqueID, file);
|
|
718
|
-
const resolvedFiles = [];
|
|
719
|
-
for (const file of files) {
|
|
720
|
-
if (file.isSymbolicLink && file.uniqueID !== void 0 && file.link === void 0) {
|
|
721
|
-
const target = nonLinksByID.get(file.uniqueID);
|
|
722
|
-
if (target !== void 0) file.link = target.name;
|
|
723
|
-
}
|
|
724
|
-
if (!file.name.includes("/")) resolvedFiles.push(file);
|
|
725
|
-
}
|
|
726
|
-
return resolvedFiles;
|
|
727
|
-
}
|
|
728
|
-
/**
|
|
729
|
-
* Parse date as specified in https://tools.ietf.org/html/rfc3659#section-2.3.
|
|
730
|
-
*
|
|
731
|
-
* Message contains response code and modified time in the format: YYYYMMDDHHMMSS[.sss]
|
|
732
|
-
* For example `19991005213102` or `19980615100045.014`.
|
|
733
|
-
*/
|
|
734
|
-
function parseMLSxDate(fact) {
|
|
735
|
-
return new Date(Date.UTC(+fact.slice(0, 4), +fact.slice(4, 6) - 1, +fact.slice(6, 8), +fact.slice(8, 10), +fact.slice(10, 12), +fact.slice(12, 14), +fact.slice(15, 18)));
|
|
736
|
-
}
|
|
737
|
-
}));
|
|
738
|
-
var require_parseList = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
739
|
-
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
740
|
-
if (k2 === void 0) k2 = k;
|
|
741
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
742
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) desc = {
|
|
743
|
-
enumerable: true,
|
|
744
|
-
get: function() {
|
|
745
|
-
return m[k];
|
|
746
|
-
}
|
|
747
|
-
};
|
|
748
|
-
Object.defineProperty(o, k2, desc);
|
|
749
|
-
}) : (function(o, m, k, k2) {
|
|
750
|
-
if (k2 === void 0) k2 = k;
|
|
751
|
-
o[k2] = m[k];
|
|
752
|
-
}));
|
|
753
|
-
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
754
|
-
Object.defineProperty(o, "default", {
|
|
755
|
-
enumerable: true,
|
|
756
|
-
value: v
|
|
757
|
-
});
|
|
758
|
-
}) : function(o, v) {
|
|
759
|
-
o["default"] = v;
|
|
760
|
-
});
|
|
761
|
-
var __importStar = exports && exports.__importStar || (function() {
|
|
762
|
-
var ownKeys = function(o) {
|
|
763
|
-
ownKeys = Object.getOwnPropertyNames || function(o) {
|
|
764
|
-
var ar = [];
|
|
765
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
766
|
-
return ar;
|
|
767
|
-
};
|
|
768
|
-
return ownKeys(o);
|
|
769
|
-
};
|
|
770
|
-
return function(mod) {
|
|
771
|
-
if (mod && mod.__esModule) return mod;
|
|
772
|
-
var result = {};
|
|
773
|
-
if (mod != null) {
|
|
774
|
-
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
775
|
-
}
|
|
776
|
-
__setModuleDefault(result, mod);
|
|
777
|
-
return result;
|
|
778
|
-
};
|
|
779
|
-
})();
|
|
780
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
781
|
-
exports.parseList = parseList;
|
|
782
|
-
/**
|
|
783
|
-
* Available directory listing parsers. These are candidates that will be tested
|
|
784
|
-
* in the order presented. The first candidate will be used to parse the whole list.
|
|
785
|
-
*/
|
|
786
|
-
var availableParsers = [
|
|
787
|
-
__importStar(require_parseListDOS()),
|
|
788
|
-
__importStar(require_parseListUnix()),
|
|
789
|
-
__importStar(require_parseListMLSD())
|
|
790
|
-
];
|
|
791
|
-
function firstCompatibleParser(line, parsers) {
|
|
792
|
-
return parsers.find((parser) => parser.testLine(line) === true);
|
|
793
|
-
}
|
|
794
|
-
function isNotBlank(str) {
|
|
795
|
-
return str.trim() !== "";
|
|
796
|
-
}
|
|
797
|
-
function isNotMeta(str) {
|
|
798
|
-
return !str.startsWith("total");
|
|
799
|
-
}
|
|
800
|
-
var REGEX_NEWLINE = /\r?\n/;
|
|
801
|
-
/**
|
|
802
|
-
* Parse raw directory listing.
|
|
803
|
-
*/
|
|
804
|
-
function parseList(rawList) {
|
|
805
|
-
const lines = rawList.split(REGEX_NEWLINE).filter(isNotBlank).filter(isNotMeta);
|
|
806
|
-
if (lines.length === 0) return [];
|
|
807
|
-
const testLine = lines[lines.length - 1];
|
|
808
|
-
const parser = firstCompatibleParser(testLine, availableParsers);
|
|
809
|
-
if (!parser) throw new Error("This library only supports MLSD, Unix- or DOS-style directory listing. Your FTP server seems to be using another format. You can see the transmitted listing when setting `client.ftp.verbose = true`. You can then provide a custom parser to `client.parseList`, see the documentation for details.");
|
|
810
|
-
const files = lines.map(parser.parseLine).filter((info) => info !== void 0);
|
|
811
|
-
return parser.transformList(files);
|
|
812
|
-
}
|
|
813
|
-
}));
|
|
814
|
-
var require_ProgressTracker = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
815
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
816
|
-
exports.ProgressTracker = void 0;
|
|
817
|
-
/**
|
|
818
|
-
* Tracks progress of one socket data transfer at a time.
|
|
819
|
-
*/
|
|
820
|
-
var ProgressTracker = class {
|
|
821
|
-
constructor() {
|
|
822
|
-
this.bytesOverall = 0;
|
|
823
|
-
this.intervalMs = 500;
|
|
824
|
-
this.onStop = noop;
|
|
825
|
-
this.onHandle = noop;
|
|
826
|
-
}
|
|
827
|
-
/**
|
|
828
|
-
* Register a new handler for progress info. Use `undefined` to disable reporting.
|
|
829
|
-
*/
|
|
830
|
-
reportTo(onHandle = noop) {
|
|
831
|
-
this.onHandle = onHandle;
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Start tracking transfer progress of a socket.
|
|
835
|
-
*
|
|
836
|
-
* @param socket The socket to observe.
|
|
837
|
-
* @param name A name associated with this progress tracking, e.g. a filename.
|
|
838
|
-
* @param type The type of the transfer, typically "upload" or "download".
|
|
839
|
-
*/
|
|
840
|
-
start(socket, name, type) {
|
|
841
|
-
let lastBytes = 0;
|
|
842
|
-
this.onStop = poll(this.intervalMs, () => {
|
|
843
|
-
const bytes = socket.bytesRead + socket.bytesWritten;
|
|
844
|
-
this.bytesOverall += bytes - lastBytes;
|
|
845
|
-
lastBytes = bytes;
|
|
846
|
-
this.onHandle({
|
|
847
|
-
name,
|
|
848
|
-
type,
|
|
849
|
-
bytes,
|
|
850
|
-
bytesOverall: this.bytesOverall
|
|
851
|
-
});
|
|
852
|
-
});
|
|
853
|
-
}
|
|
854
|
-
/**
|
|
855
|
-
* Stop tracking transfer progress.
|
|
856
|
-
*/
|
|
857
|
-
stop() {
|
|
858
|
-
this.onStop(false);
|
|
859
|
-
}
|
|
860
|
-
/**
|
|
861
|
-
* Call the progress handler one more time, then stop tracking.
|
|
862
|
-
*/
|
|
863
|
-
updateAndStop() {
|
|
864
|
-
this.onStop(true);
|
|
865
|
-
}
|
|
866
|
-
};
|
|
867
|
-
exports.ProgressTracker = ProgressTracker;
|
|
868
|
-
/**
|
|
869
|
-
* Starts calling a callback function at a regular interval. The first call will go out
|
|
870
|
-
* immediately. The function returns a function to stop the polling.
|
|
871
|
-
*/
|
|
872
|
-
function poll(intervalMs, updateFunc) {
|
|
873
|
-
const id = setInterval(updateFunc, intervalMs);
|
|
874
|
-
const stopFunc = (stopWithUpdate) => {
|
|
875
|
-
clearInterval(id);
|
|
876
|
-
if (stopWithUpdate) updateFunc();
|
|
877
|
-
updateFunc = noop;
|
|
878
|
-
};
|
|
879
|
-
updateFunc();
|
|
880
|
-
return stopFunc;
|
|
881
|
-
}
|
|
882
|
-
function noop() {}
|
|
883
|
-
}));
|
|
884
|
-
var require_StringWriter = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
885
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
886
|
-
exports.StringWriter = void 0;
|
|
887
|
-
var stream_1$1 = __require("stream");
|
|
888
|
-
var StringWriter = class extends stream_1$1.Writable {
|
|
889
|
-
constructor() {
|
|
890
|
-
super(...arguments);
|
|
891
|
-
this.buf = Buffer.alloc(0);
|
|
892
|
-
}
|
|
893
|
-
_write(chunk, _, callback) {
|
|
894
|
-
if (chunk instanceof Buffer) {
|
|
895
|
-
this.buf = Buffer.concat([this.buf, chunk]);
|
|
896
|
-
callback(null);
|
|
897
|
-
} else callback(/* @__PURE__ */ new Error("StringWriter expects chunks of type 'Buffer'."));
|
|
898
|
-
}
|
|
899
|
-
getText(encoding) {
|
|
900
|
-
return this.buf.toString(encoding);
|
|
901
|
-
}
|
|
902
|
-
};
|
|
903
|
-
exports.StringWriter = StringWriter;
|
|
904
|
-
}));
|
|
905
|
-
var require_netUtils = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
906
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
907
|
-
exports.describeTLS = describeTLS;
|
|
908
|
-
exports.describeAddress = describeAddress;
|
|
909
|
-
exports.upgradeSocket = upgradeSocket;
|
|
910
|
-
exports.ipIsPrivateV4Address = ipIsPrivateV4Address;
|
|
911
|
-
var tls_1$2 = __require("tls");
|
|
912
|
-
/**
|
|
913
|
-
* Returns a string describing the encryption on a given socket instance.
|
|
914
|
-
*/
|
|
915
|
-
function describeTLS(socket) {
|
|
916
|
-
if (socket instanceof tls_1$2.TLSSocket) {
|
|
917
|
-
const protocol = socket.getProtocol();
|
|
918
|
-
return protocol ? protocol : "Server socket or disconnected client socket";
|
|
919
|
-
}
|
|
920
|
-
return "No encryption";
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* Returns a string describing the remote address of a socket.
|
|
924
|
-
*/
|
|
925
|
-
function describeAddress(socket) {
|
|
926
|
-
if (socket.remoteFamily === "IPv6") return `[${socket.remoteAddress}]:${socket.remotePort}`;
|
|
927
|
-
return `${socket.remoteAddress}:${socket.remotePort}`;
|
|
928
|
-
}
|
|
929
|
-
/**
|
|
930
|
-
* Upgrade a socket connection with TLS.
|
|
931
|
-
*/
|
|
932
|
-
function upgradeSocket(socket, options) {
|
|
933
|
-
return new Promise((resolve, reject) => {
|
|
934
|
-
const tlsOptions = Object.assign({}, options, { socket });
|
|
935
|
-
const tlsSocket = (0, tls_1$2.connect)(tlsOptions, () => {
|
|
936
|
-
if (tlsOptions.rejectUnauthorized !== false && !tlsSocket.authorized) reject(tlsSocket.authorizationError);
|
|
937
|
-
else {
|
|
938
|
-
tlsSocket.removeAllListeners("error");
|
|
939
|
-
resolve(tlsSocket);
|
|
940
|
-
}
|
|
941
|
-
}).once("error", (error) => {
|
|
942
|
-
reject(error);
|
|
943
|
-
});
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
/**
|
|
947
|
-
* Returns true if an IP is a private address according to https://tools.ietf.org/html/rfc1918#section-3.
|
|
948
|
-
* This will handle IPv4-mapped IPv6 addresses correctly but return false for all other IPv6 addresses.
|
|
949
|
-
*
|
|
950
|
-
* @param ip The IP as a string, e.g. "192.168.0.1"
|
|
951
|
-
*/
|
|
952
|
-
function ipIsPrivateV4Address(ip = "") {
|
|
953
|
-
if (ip.startsWith("::ffff:")) ip = ip.substr(7);
|
|
954
|
-
const octets = ip.split(".").map((o) => parseInt(o, 10));
|
|
955
|
-
return octets[0] === 10 || octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31 || octets[0] === 192 && octets[1] === 168 || ip === "127.0.0.1";
|
|
956
|
-
}
|
|
957
|
-
}));
|
|
958
|
-
var require_transfer = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
959
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
960
|
-
exports.enterPassiveModeIPv6 = enterPassiveModeIPv6;
|
|
961
|
-
exports.parseEpsvResponse = parseEpsvResponse;
|
|
962
|
-
exports.enterPassiveModeIPv4 = enterPassiveModeIPv4;
|
|
963
|
-
exports.enterPassiveModeIPv4_forceControlHostIP = enterPassiveModeIPv4_forceControlHostIP;
|
|
964
|
-
exports.parsePasvResponse = parsePasvResponse;
|
|
965
|
-
exports.connectForPassiveTransfer = connectForPassiveTransfer;
|
|
966
|
-
exports.uploadFrom = uploadFrom;
|
|
967
|
-
exports.downloadTo = downloadTo;
|
|
968
|
-
var netUtils_1 = require_netUtils();
|
|
969
|
-
var stream_1 = __require("stream");
|
|
970
|
-
var tls_1$1 = __require("tls");
|
|
971
|
-
var parseControlResponse_1 = require_parseControlResponse();
|
|
972
|
-
/**
|
|
973
|
-
* Prepare a data socket using passive mode over IPv6.
|
|
974
|
-
*/
|
|
975
|
-
async function enterPassiveModeIPv6(ftp) {
|
|
976
|
-
const res = await ftp.request("EPSV");
|
|
977
|
-
const port = parseEpsvResponse(res.message);
|
|
978
|
-
if (!port) throw new Error("Can't parse EPSV response: " + res.message);
|
|
979
|
-
const controlHost = ftp.socket.remoteAddress;
|
|
980
|
-
if (controlHost === void 0) throw new Error("Control socket is disconnected, can't get remote address.");
|
|
981
|
-
await connectForPassiveTransfer(controlHost, port, ftp);
|
|
982
|
-
return res;
|
|
983
|
-
}
|
|
984
|
-
/**
|
|
985
|
-
* Parse an EPSV response. Returns only the port as in EPSV the host of the control connection is used.
|
|
986
|
-
*/
|
|
987
|
-
function parseEpsvResponse(message) {
|
|
988
|
-
const groups = message.match(/[|!]{3}(.+)[|!]/);
|
|
989
|
-
if (groups === null || groups[1] === void 0) throw new Error(`Can't parse response to 'EPSV': ${message}`);
|
|
990
|
-
const port = parseInt(groups[1], 10);
|
|
991
|
-
if (Number.isNaN(port)) throw new Error(`Can't parse response to 'EPSV', port is not a number: ${message}`);
|
|
992
|
-
return port;
|
|
993
|
-
}
|
|
994
|
-
/**
|
|
995
|
-
* Prepare a data socket using passive mode over IPv4.
|
|
996
|
-
*/
|
|
997
|
-
async function enterPassiveModeIPv4(ftp) {
|
|
998
|
-
const res = await ftp.request("PASV");
|
|
999
|
-
const target = parsePasvResponse(res.message);
|
|
1000
|
-
if (!target) throw new Error("Can't parse PASV response: " + res.message);
|
|
1001
|
-
const controlHost = ftp.socket.remoteAddress;
|
|
1002
|
-
if ((0, netUtils_1.ipIsPrivateV4Address)(target.host) && controlHost && !(0, netUtils_1.ipIsPrivateV4Address)(controlHost)) target.host = controlHost;
|
|
1003
|
-
await connectForPassiveTransfer(target.host, target.port, ftp);
|
|
1004
|
-
return res;
|
|
1005
|
-
}
|
|
1006
|
-
/**
|
|
1007
|
-
* Prepare a data socket using passive mode over IPv4. Ignore the IP provided by the PASV response,
|
|
1008
|
-
* and use the control host IP. This is the same behaviour as with the more modern variant EPSV. Use
|
|
1009
|
-
* this to fix issues around NAT or provide more security by preventing FTP bounce attacks.
|
|
1010
|
-
*/
|
|
1011
|
-
async function enterPassiveModeIPv4_forceControlHostIP(ftp) {
|
|
1012
|
-
const res = await ftp.request("PASV");
|
|
1013
|
-
const target = parsePasvResponse(res.message);
|
|
1014
|
-
if (!target) throw new Error("Can't parse PASV response: " + res.message);
|
|
1015
|
-
const controlHost = ftp.socket.remoteAddress;
|
|
1016
|
-
if (controlHost === void 0) throw new Error("Control socket is disconnected, can't get remote address.");
|
|
1017
|
-
await connectForPassiveTransfer(controlHost, target.port, ftp);
|
|
1018
|
-
return res;
|
|
1019
|
-
}
|
|
1020
|
-
/**
|
|
1021
|
-
* Parse a PASV response.
|
|
1022
|
-
*/
|
|
1023
|
-
function parsePasvResponse(message) {
|
|
1024
|
-
const groups = message.match(/([-\d]+,[-\d]+,[-\d]+,[-\d]+),([-\d]+),([-\d]+)/);
|
|
1025
|
-
if (groups === null || groups.length !== 4) throw new Error(`Can't parse response to 'PASV': ${message}`);
|
|
1026
|
-
return {
|
|
1027
|
-
host: groups[1].replace(/,/g, "."),
|
|
1028
|
-
port: (parseInt(groups[2], 10) & 255) * 256 + (parseInt(groups[3], 10) & 255)
|
|
1029
|
-
};
|
|
1030
|
-
}
|
|
1031
|
-
function connectForPassiveTransfer(host, port, ftp) {
|
|
1032
|
-
return new Promise((resolve, reject) => {
|
|
1033
|
-
let socket = ftp._newSocket();
|
|
1034
|
-
const handleConnErr = function(err) {
|
|
1035
|
-
err.message = "Can't open data connection in passive mode: " + err.message;
|
|
1036
|
-
reject(err);
|
|
1037
|
-
};
|
|
1038
|
-
const handleTimeout = function() {
|
|
1039
|
-
socket.destroy();
|
|
1040
|
-
reject(/* @__PURE__ */ new Error(`Timeout when trying to open data connection to ${host}:${port}`));
|
|
1041
|
-
};
|
|
1042
|
-
socket.setTimeout(ftp.timeout);
|
|
1043
|
-
socket.on("error", handleConnErr);
|
|
1044
|
-
socket.on("timeout", handleTimeout);
|
|
1045
|
-
socket.connect({
|
|
1046
|
-
port,
|
|
1047
|
-
host,
|
|
1048
|
-
family: ftp.ipFamily
|
|
1049
|
-
}, () => {
|
|
1050
|
-
if (ftp.socket instanceof tls_1$1.TLSSocket) socket = (0, tls_1$1.connect)(Object.assign({}, ftp.tlsOptions, {
|
|
1051
|
-
socket,
|
|
1052
|
-
session: ftp.socket.getSession()
|
|
1053
|
-
}));
|
|
1054
|
-
socket.removeListener("error", handleConnErr);
|
|
1055
|
-
socket.removeListener("timeout", handleTimeout);
|
|
1056
|
-
ftp.dataSocket = socket;
|
|
1057
|
-
resolve();
|
|
1058
|
-
});
|
|
1059
|
-
});
|
|
1060
|
-
}
|
|
1061
|
-
/**
|
|
1062
|
-
* Helps resolving/rejecting transfers.
|
|
1063
|
-
*
|
|
1064
|
-
* This is used internally for all FTP transfers. For example when downloading, the server might confirm
|
|
1065
|
-
* with "226 Transfer complete" when in fact the download on the data connection has not finished
|
|
1066
|
-
* yet. With all transfers we make sure that a) the result arrived and b) has been confirmed by
|
|
1067
|
-
* e.g. the control connection. We just don't know in which order this will happen.
|
|
1068
|
-
*/
|
|
1069
|
-
var TransferResolver = class {
|
|
1070
|
-
/**
|
|
1071
|
-
* Instantiate a TransferResolver
|
|
1072
|
-
*/
|
|
1073
|
-
constructor(ftp, progress) {
|
|
1074
|
-
this.ftp = ftp;
|
|
1075
|
-
this.progress = progress;
|
|
1076
|
-
this.response = void 0;
|
|
1077
|
-
this.dataTransferDone = false;
|
|
1078
|
-
}
|
|
1079
|
-
/**
|
|
1080
|
-
* Mark the beginning of a transfer.
|
|
1081
|
-
*
|
|
1082
|
-
* @param name - Name of the transfer, usually the filename.
|
|
1083
|
-
* @param type - Type of transfer, usually "upload" or "download".
|
|
1084
|
-
*/
|
|
1085
|
-
onDataStart(name, type) {
|
|
1086
|
-
if (this.ftp.dataSocket === void 0) throw new Error("Data transfer should start but there is no data connection.");
|
|
1087
|
-
this.ftp.socket.setTimeout(0);
|
|
1088
|
-
this.ftp.dataSocket.setTimeout(this.ftp.timeout);
|
|
1089
|
-
this.progress.start(this.ftp.dataSocket, name, type);
|
|
1090
|
-
}
|
|
1091
|
-
/**
|
|
1092
|
-
* The data connection has finished the transfer.
|
|
1093
|
-
*/
|
|
1094
|
-
onDataDone(task) {
|
|
1095
|
-
this.progress.updateAndStop();
|
|
1096
|
-
this.ftp.socket.setTimeout(this.ftp.timeout);
|
|
1097
|
-
if (this.ftp.dataSocket) this.ftp.dataSocket.setTimeout(0);
|
|
1098
|
-
this.dataTransferDone = true;
|
|
1099
|
-
this.tryResolve(task);
|
|
1100
|
-
}
|
|
1101
|
-
/**
|
|
1102
|
-
* The control connection reports the transfer as finished.
|
|
1103
|
-
*/
|
|
1104
|
-
onControlDone(task, response) {
|
|
1105
|
-
this.response = response;
|
|
1106
|
-
this.tryResolve(task);
|
|
1107
|
-
}
|
|
1108
|
-
/**
|
|
1109
|
-
* An error has been reported and the task should be rejected.
|
|
1110
|
-
*/
|
|
1111
|
-
onError(task, err) {
|
|
1112
|
-
this.progress.updateAndStop();
|
|
1113
|
-
this.ftp.socket.setTimeout(this.ftp.timeout);
|
|
1114
|
-
this.ftp.dataSocket = void 0;
|
|
1115
|
-
task.reject(err);
|
|
1116
|
-
}
|
|
1117
|
-
/**
|
|
1118
|
-
* Control connection sent an unexpected request requiring a response from our part. We
|
|
1119
|
-
* can't provide that (because unknown) and have to close the contrext with an error because
|
|
1120
|
-
* the FTP server is now caught up in a state we can't resolve.
|
|
1121
|
-
*/
|
|
1122
|
-
onUnexpectedRequest(response) {
|
|
1123
|
-
const err = /* @__PURE__ */ new Error(`Unexpected FTP response is requesting an answer: ${response.message}`);
|
|
1124
|
-
this.ftp.closeWithError(err);
|
|
1125
|
-
}
|
|
1126
|
-
tryResolve(task) {
|
|
1127
|
-
if (this.dataTransferDone && this.response !== void 0) {
|
|
1128
|
-
this.ftp.dataSocket = void 0;
|
|
1129
|
-
task.resolve(this.response);
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
};
|
|
1133
|
-
function uploadFrom(source, config) {
|
|
1134
|
-
const resolver = new TransferResolver(config.ftp, config.tracker);
|
|
1135
|
-
const fullCommand = `${config.command} ${config.remotePath}`;
|
|
1136
|
-
return config.ftp.handle(fullCommand, (res, task) => {
|
|
1137
|
-
if (res instanceof Error) resolver.onError(task, res);
|
|
1138
|
-
else if (res.code === 150 || res.code === 125) {
|
|
1139
|
-
const dataSocket = config.ftp.dataSocket;
|
|
1140
|
-
if (!dataSocket) {
|
|
1141
|
-
resolver.onError(task, /* @__PURE__ */ new Error("Upload should begin but no data connection is available."));
|
|
1142
|
-
return;
|
|
1143
|
-
}
|
|
1144
|
-
onConditionOrEvent("getCipher" in dataSocket ? dataSocket.getCipher() !== void 0 : true, dataSocket, "secureConnect", () => {
|
|
1145
|
-
config.ftp.log(`Uploading to ${(0, netUtils_1.describeAddress)(dataSocket)} (${(0, netUtils_1.describeTLS)(dataSocket)})`);
|
|
1146
|
-
resolver.onDataStart(config.remotePath, config.type);
|
|
1147
|
-
(0, stream_1.pipeline)(source, dataSocket, (err) => {
|
|
1148
|
-
if (err) resolver.onError(task, err);
|
|
1149
|
-
else resolver.onDataDone(task);
|
|
1150
|
-
});
|
|
1151
|
-
});
|
|
1152
|
-
} else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) resolver.onControlDone(task, res);
|
|
1153
|
-
else if ((0, parseControlResponse_1.positiveIntermediate)(res.code)) resolver.onUnexpectedRequest(res);
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
|
-
function downloadTo(destination, config) {
|
|
1157
|
-
if (!config.ftp.dataSocket) throw new Error("Download will be initiated but no data connection is available.");
|
|
1158
|
-
const resolver = new TransferResolver(config.ftp, config.tracker);
|
|
1159
|
-
return config.ftp.handle(config.command, (res, task) => {
|
|
1160
|
-
if (res instanceof Error) resolver.onError(task, res);
|
|
1161
|
-
else if (res.code === 150 || res.code === 125) {
|
|
1162
|
-
const dataSocket = config.ftp.dataSocket;
|
|
1163
|
-
if (!dataSocket) {
|
|
1164
|
-
resolver.onError(task, /* @__PURE__ */ new Error("Download should begin but no data connection is available."));
|
|
1165
|
-
return;
|
|
1166
|
-
}
|
|
1167
|
-
config.ftp.log(`Downloading from ${(0, netUtils_1.describeAddress)(dataSocket)} (${(0, netUtils_1.describeTLS)(dataSocket)})`);
|
|
1168
|
-
resolver.onDataStart(config.remotePath, config.type);
|
|
1169
|
-
(0, stream_1.pipeline)(dataSocket, destination, (err) => {
|
|
1170
|
-
if (err) resolver.onError(task, err);
|
|
1171
|
-
else resolver.onDataDone(task);
|
|
1172
|
-
});
|
|
1173
|
-
} else if (res.code === 350) config.ftp.send("RETR " + config.remotePath);
|
|
1174
|
-
else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) resolver.onControlDone(task, res);
|
|
1175
|
-
else if ((0, parseControlResponse_1.positiveIntermediate)(res.code)) resolver.onUnexpectedRequest(res);
|
|
1176
|
-
});
|
|
1177
|
-
}
|
|
1178
|
-
/**
|
|
1179
|
-
* Calls a function immediately if a condition is met or subscribes to an event and calls
|
|
1180
|
-
* it once the event is emitted.
|
|
1181
|
-
*
|
|
1182
|
-
* @param condition The condition to test.
|
|
1183
|
-
* @param emitter The emitter to use if the condition is not met.
|
|
1184
|
-
* @param eventName The event to subscribe to if the condition is not met.
|
|
1185
|
-
* @param action The function to call.
|
|
1186
|
-
*/
|
|
1187
|
-
function onConditionOrEvent(condition, emitter, eventName, action) {
|
|
1188
|
-
if (condition === true) action();
|
|
1189
|
-
else emitter.once(eventName, () => action());
|
|
1190
|
-
}
|
|
1191
|
-
}));
|
|
1192
|
-
var require_Client = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
1193
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1194
|
-
exports.Client = void 0;
|
|
1195
|
-
var fs_1 = __require("fs");
|
|
1196
|
-
var path_1 = __require("path");
|
|
1197
|
-
var tls_1 = __require("tls");
|
|
1198
|
-
var util_1 = __require("util");
|
|
1199
|
-
var FtpContext_1 = require_FtpContext();
|
|
1200
|
-
var parseList_1 = require_parseList();
|
|
1201
|
-
var ProgressTracker_1 = require_ProgressTracker();
|
|
1202
|
-
var StringWriter_1 = require_StringWriter();
|
|
1203
|
-
var parseListMLSD_1 = require_parseListMLSD();
|
|
1204
|
-
var netUtils_1 = require_netUtils();
|
|
1205
|
-
var transfer_1 = require_transfer();
|
|
1206
|
-
var parseControlResponse_1 = require_parseControlResponse();
|
|
1207
|
-
var fsReadDir = (0, util_1.promisify)(fs_1.readdir);
|
|
1208
|
-
var fsMkDir = (0, util_1.promisify)(fs_1.mkdir);
|
|
1209
|
-
var fsStat = (0, util_1.promisify)(fs_1.stat);
|
|
1210
|
-
var fsOpen = (0, util_1.promisify)(fs_1.open);
|
|
1211
|
-
var fsClose = (0, util_1.promisify)(fs_1.close);
|
|
1212
|
-
var fsUnlink = (0, util_1.promisify)(fs_1.unlink);
|
|
1213
|
-
var defaultClientOptions = { allowSeparateTransferHost: true };
|
|
1214
|
-
var LIST_COMMANDS_DEFAULT = () => ["LIST -a", "LIST"];
|
|
1215
|
-
var LIST_COMMANDS_MLSD = () => [
|
|
1216
|
-
"MLSD",
|
|
1217
|
-
"LIST -a",
|
|
1218
|
-
"LIST"
|
|
1219
|
-
];
|
|
1220
|
-
/**
|
|
1221
|
-
* High-level API to interact with an FTP server.
|
|
1222
|
-
*/
|
|
1223
|
-
var Client = class {
|
|
1224
|
-
/**
|
|
1225
|
-
* Instantiate an FTP client.
|
|
1226
|
-
*
|
|
1227
|
-
* @param timeout Timeout in milliseconds, use 0 for no timeout. Optional, default is 30 seconds.
|
|
1228
|
-
*/
|
|
1229
|
-
constructor(timeout = 3e4, options = defaultClientOptions) {
|
|
1230
|
-
this.availableListCommands = LIST_COMMANDS_DEFAULT();
|
|
1231
|
-
this.ftp = new FtpContext_1.FTPContext(timeout);
|
|
1232
|
-
this.prepareTransfer = this._enterFirstCompatibleMode([transfer_1.enterPassiveModeIPv6, options.allowSeparateTransferHost ? transfer_1.enterPassiveModeIPv4 : transfer_1.enterPassiveModeIPv4_forceControlHostIP]);
|
|
1233
|
-
this.parseList = parseList_1.parseList;
|
|
1234
|
-
this._progressTracker = new ProgressTracker_1.ProgressTracker();
|
|
1235
|
-
}
|
|
1236
|
-
/**
|
|
1237
|
-
* Close the client and all open socket connections.
|
|
1238
|
-
*
|
|
1239
|
-
* Close the client and all open socket connections. The client can’t be used anymore after calling this method,
|
|
1240
|
-
* you have to either reconnect with `access` or `connect` or instantiate a new instance to continue any work.
|
|
1241
|
-
* A client is also closed automatically if any timeout or connection error occurs.
|
|
1242
|
-
*/
|
|
1243
|
-
close() {
|
|
1244
|
-
this.ftp.close();
|
|
1245
|
-
this._progressTracker.stop();
|
|
1246
|
-
}
|
|
1247
|
-
/**
|
|
1248
|
-
* Returns true if the client is closed and can't be used anymore.
|
|
1249
|
-
*/
|
|
1250
|
-
get closed() {
|
|
1251
|
-
return this.ftp.closed;
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* Connect (or reconnect) to an FTP server.
|
|
1255
|
-
*
|
|
1256
|
-
* This is an instance method and thus can be called multiple times during the lifecycle of a `Client`
|
|
1257
|
-
* instance. Whenever you do, the client is reset with a new control connection. This also implies that
|
|
1258
|
-
* you can reopen a `Client` instance that has been closed due to an error when reconnecting with this
|
|
1259
|
-
* method. In fact, reconnecting is the only way to continue using a closed `Client`.
|
|
1260
|
-
*
|
|
1261
|
-
* @param host Host the client should connect to. Optional, default is "localhost".
|
|
1262
|
-
* @param port Port the client should connect to. Optional, default is 21.
|
|
1263
|
-
*/
|
|
1264
|
-
connect(host = "localhost", port = 21) {
|
|
1265
|
-
this.ftp.reset();
|
|
1266
|
-
this.ftp.socket.connect({
|
|
1267
|
-
host,
|
|
1268
|
-
port,
|
|
1269
|
-
family: this.ftp.ipFamily
|
|
1270
|
-
}, () => this.ftp.log(`Connected to ${(0, netUtils_1.describeAddress)(this.ftp.socket)} (${(0, netUtils_1.describeTLS)(this.ftp.socket)})`));
|
|
1271
|
-
return this._handleConnectResponse();
|
|
1272
|
-
}
|
|
1273
|
-
/**
|
|
1274
|
-
* As `connect` but using implicit TLS. Implicit TLS is not an FTP standard and has been replaced by
|
|
1275
|
-
* explicit TLS. There are still FTP servers that support only implicit TLS, though.
|
|
1276
|
-
*/
|
|
1277
|
-
connectImplicitTLS(host = "localhost", port = 21, tlsOptions = {}) {
|
|
1278
|
-
this.ftp.reset();
|
|
1279
|
-
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)})`));
|
|
1280
|
-
this.ftp.tlsOptions = tlsOptions;
|
|
1281
|
-
return this._handleConnectResponse();
|
|
1282
|
-
}
|
|
1283
|
-
/**
|
|
1284
|
-
* Handles the first reponse by an FTP server after the socket connection has been established.
|
|
1285
|
-
*/
|
|
1286
|
-
_handleConnectResponse() {
|
|
1287
|
-
return this.ftp.handle(void 0, (res, task) => {
|
|
1288
|
-
if (res instanceof Error) task.reject(res);
|
|
1289
|
-
else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) task.resolve(res);
|
|
1290
|
-
else task.reject(new FtpContext_1.FTPError(res));
|
|
1291
|
-
});
|
|
1292
|
-
}
|
|
1293
|
-
/**
|
|
1294
|
-
* Send an FTP command and handle the first response.
|
|
1295
|
-
*/
|
|
1296
|
-
send(command, ignoreErrorCodesDEPRECATED = false) {
|
|
1297
|
-
if (ignoreErrorCodesDEPRECATED) {
|
|
1298
|
-
this.ftp.log("Deprecated call using send(command, flag) with boolean flag to ignore errors. Use sendIgnoringError(command).");
|
|
1299
|
-
return this.sendIgnoringError(command);
|
|
1300
|
-
}
|
|
1301
|
-
return this.ftp.request(command);
|
|
1302
|
-
}
|
|
1303
|
-
/**
|
|
1304
|
-
* Send an FTP command and ignore an FTP error response. Any other kind of error or timeout will still reject the Promise.
|
|
1305
|
-
*
|
|
1306
|
-
* @param command
|
|
1307
|
-
*/
|
|
1308
|
-
sendIgnoringError(command) {
|
|
1309
|
-
return this.ftp.handle(command, (res, task) => {
|
|
1310
|
-
if (res instanceof FtpContext_1.FTPError) task.resolve({
|
|
1311
|
-
code: res.code,
|
|
1312
|
-
message: res.message
|
|
1313
|
-
});
|
|
1314
|
-
else if (res instanceof Error) task.reject(res);
|
|
1315
|
-
else task.resolve(res);
|
|
1316
|
-
});
|
|
1317
|
-
}
|
|
1318
|
-
/**
|
|
1319
|
-
* Upgrade the current socket connection to TLS.
|
|
1320
|
-
*
|
|
1321
|
-
* @param options TLS options as in `tls.connect(options)`, optional.
|
|
1322
|
-
* @param command Set the authentication command. Optional, default is "AUTH TLS".
|
|
1323
|
-
*/
|
|
1324
|
-
async useTLS(options = {}, command = "AUTH TLS") {
|
|
1325
|
-
const ret = await this.send(command);
|
|
1326
|
-
this.ftp.socket = await (0, netUtils_1.upgradeSocket)(this.ftp.socket, options);
|
|
1327
|
-
this.ftp.tlsOptions = options;
|
|
1328
|
-
this.ftp.log(`Control socket is using: ${(0, netUtils_1.describeTLS)(this.ftp.socket)}`);
|
|
1329
|
-
return ret;
|
|
1330
|
-
}
|
|
1331
|
-
/**
|
|
1332
|
-
* Login a user with a password.
|
|
1333
|
-
*
|
|
1334
|
-
* @param user Username to use for login. Optional, default is "anonymous".
|
|
1335
|
-
* @param password Password to use for login. Optional, default is "guest".
|
|
1336
|
-
*/
|
|
1337
|
-
login(user = "anonymous", password = "guest") {
|
|
1338
|
-
this.ftp.log(`Login security: ${(0, netUtils_1.describeTLS)(this.ftp.socket)}`);
|
|
1339
|
-
return this.ftp.handle("USER " + user, (res, task) => {
|
|
1340
|
-
if (res instanceof Error) task.reject(res);
|
|
1341
|
-
else if ((0, parseControlResponse_1.positiveCompletion)(res.code)) task.resolve(res);
|
|
1342
|
-
else if (res.code === 331) this.ftp.send("PASS " + password);
|
|
1343
|
-
else task.reject(new FtpContext_1.FTPError(res));
|
|
1344
|
-
});
|
|
1345
|
-
}
|
|
1346
|
-
/**
|
|
1347
|
-
* Set the usual default settings.
|
|
1348
|
-
*
|
|
1349
|
-
* Settings used:
|
|
1350
|
-
* * Binary mode (TYPE I)
|
|
1351
|
-
* * File structure (STRU F)
|
|
1352
|
-
* * Additional settings for FTPS (PBSZ 0, PROT P)
|
|
1353
|
-
*/
|
|
1354
|
-
async useDefaultSettings() {
|
|
1355
|
-
const supportsMLSD = (await this.features()).has("MLST");
|
|
1356
|
-
this.availableListCommands = supportsMLSD ? LIST_COMMANDS_MLSD() : LIST_COMMANDS_DEFAULT();
|
|
1357
|
-
await this.send("TYPE I");
|
|
1358
|
-
await this.sendIgnoringError("STRU F");
|
|
1359
|
-
await this.sendIgnoringError("OPTS UTF8 ON");
|
|
1360
|
-
if (supportsMLSD) await this.sendIgnoringError("OPTS MLST type;size;modify;unique;unix.mode;unix.owner;unix.group;unix.ownername;unix.groupname;");
|
|
1361
|
-
if (this.ftp.hasTLS) {
|
|
1362
|
-
await this.sendIgnoringError("PBSZ 0");
|
|
1363
|
-
await this.sendIgnoringError("PROT P");
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
/**
|
|
1367
|
-
* Convenience method that calls `connect`, `useTLS`, `login` and `useDefaultSettings`.
|
|
1368
|
-
*
|
|
1369
|
-
* This is an instance method and thus can be called multiple times during the lifecycle of a `Client`
|
|
1370
|
-
* instance. Whenever you do, the client is reset with a new control connection. This also implies that
|
|
1371
|
-
* you can reopen a `Client` instance that has been closed due to an error when reconnecting with this
|
|
1372
|
-
* method. In fact, reconnecting is the only way to continue using a closed `Client`.
|
|
1373
|
-
*/
|
|
1374
|
-
async access(options = {}) {
|
|
1375
|
-
var _a, _b;
|
|
1376
|
-
const useExplicitTLS = options.secure === true;
|
|
1377
|
-
const useImplicitTLS = options.secure === "implicit";
|
|
1378
|
-
let welcome;
|
|
1379
|
-
if (useImplicitTLS) welcome = await this.connectImplicitTLS(options.host, options.port, options.secureOptions);
|
|
1380
|
-
else welcome = await this.connect(options.host, options.port);
|
|
1381
|
-
if (useExplicitTLS) {
|
|
1382
|
-
const secureOptions = (_a = options.secureOptions) !== null && _a !== void 0 ? _a : {};
|
|
1383
|
-
secureOptions.host = (_b = secureOptions.host) !== null && _b !== void 0 ? _b : options.host;
|
|
1384
|
-
await this.useTLS(secureOptions);
|
|
1385
|
-
}
|
|
1386
|
-
await this.sendIgnoringError("OPTS UTF8 ON");
|
|
1387
|
-
await this.login(options.user, options.password);
|
|
1388
|
-
await this.useDefaultSettings();
|
|
1389
|
-
return welcome;
|
|
1390
|
-
}
|
|
1391
|
-
/**
|
|
1392
|
-
* Get the current working directory.
|
|
1393
|
-
*/
|
|
1394
|
-
async pwd() {
|
|
1395
|
-
const res = await this.send("PWD");
|
|
1396
|
-
const parsed = res.message.match(/"(.+)"/);
|
|
1397
|
-
if (parsed === null || parsed[1] === void 0) throw new Error(`Can't parse response to command 'PWD': ${res.message}`);
|
|
1398
|
-
return parsed[1];
|
|
1399
|
-
}
|
|
1400
|
-
/**
|
|
1401
|
-
* Get a description of supported features.
|
|
1402
|
-
*
|
|
1403
|
-
* This sends the FEAT command and parses the result into a Map where keys correspond to available commands
|
|
1404
|
-
* and values hold further information. Be aware that your FTP servers might not support this
|
|
1405
|
-
* command in which case this method will not throw an exception but just return an empty Map.
|
|
1406
|
-
*/
|
|
1407
|
-
async features() {
|
|
1408
|
-
const res = await this.sendIgnoringError("FEAT");
|
|
1409
|
-
const features = /* @__PURE__ */ new Map();
|
|
1410
|
-
if (res.code < 400 && (0, parseControlResponse_1.isMultiline)(res.message)) res.message.split("\n").slice(1, -1).forEach((line) => {
|
|
1411
|
-
const entry = line.trim().split(" ");
|
|
1412
|
-
features.set(entry[0], entry[1] || "");
|
|
1413
|
-
});
|
|
1414
|
-
return features;
|
|
1415
|
-
}
|
|
1416
|
-
/**
|
|
1417
|
-
* Set the working directory.
|
|
1418
|
-
*/
|
|
1419
|
-
async cd(path) {
|
|
1420
|
-
const validPath = await this.protectWhitespace(path);
|
|
1421
|
-
return this.send("CWD " + validPath);
|
|
1422
|
-
}
|
|
1423
|
-
/**
|
|
1424
|
-
* Switch to the parent directory of the working directory.
|
|
1425
|
-
*/
|
|
1426
|
-
async cdup() {
|
|
1427
|
-
return this.send("CDUP");
|
|
1428
|
-
}
|
|
1429
|
-
/**
|
|
1430
|
-
* Get the last modified time of a file. This is not supported by every FTP server, in which case
|
|
1431
|
-
* calling this method will throw an exception.
|
|
1432
|
-
*/
|
|
1433
|
-
async lastMod(path) {
|
|
1434
|
-
const validPath = await this.protectWhitespace(path);
|
|
1435
|
-
const date = (await this.send(`MDTM ${validPath}`)).message.slice(4);
|
|
1436
|
-
return (0, parseListMLSD_1.parseMLSxDate)(date);
|
|
1437
|
-
}
|
|
1438
|
-
/**
|
|
1439
|
-
* Get the size of a file.
|
|
1440
|
-
*/
|
|
1441
|
-
async size(path) {
|
|
1442
|
-
const command = `SIZE ${await this.protectWhitespace(path)}`;
|
|
1443
|
-
const res = await this.send(command);
|
|
1444
|
-
const size = parseInt(res.message.slice(4), 10);
|
|
1445
|
-
if (Number.isNaN(size)) throw new Error(`Can't parse response to command '${command}' as a numerical value: ${res.message}`);
|
|
1446
|
-
return size;
|
|
1447
|
-
}
|
|
1448
|
-
/**
|
|
1449
|
-
* Rename a file.
|
|
1450
|
-
*
|
|
1451
|
-
* Depending on the FTP server this might also be used to move a file from one
|
|
1452
|
-
* directory to another by providing full paths.
|
|
1453
|
-
*/
|
|
1454
|
-
async rename(srcPath, destPath) {
|
|
1455
|
-
const validSrc = await this.protectWhitespace(srcPath);
|
|
1456
|
-
const validDest = await this.protectWhitespace(destPath);
|
|
1457
|
-
await this.send("RNFR " + validSrc);
|
|
1458
|
-
return this.send("RNTO " + validDest);
|
|
1459
|
-
}
|
|
1460
|
-
/**
|
|
1461
|
-
* Remove a file from the current working directory.
|
|
1462
|
-
*
|
|
1463
|
-
* You can ignore FTP error return codes which won't throw an exception if e.g.
|
|
1464
|
-
* the file doesn't exist.
|
|
1465
|
-
*/
|
|
1466
|
-
async remove(path, ignoreErrorCodes = false) {
|
|
1467
|
-
const validPath = await this.protectWhitespace(path);
|
|
1468
|
-
if (ignoreErrorCodes) return this.sendIgnoringError(`DELE ${validPath}`);
|
|
1469
|
-
return this.send(`DELE ${validPath}`);
|
|
1470
|
-
}
|
|
1471
|
-
/**
|
|
1472
|
-
* Report transfer progress for any upload or download to a given handler.
|
|
1473
|
-
*
|
|
1474
|
-
* This will also reset the overall transfer counter that can be used for multiple transfers. You can
|
|
1475
|
-
* also call the function without a handler to stop reporting to an earlier one.
|
|
1476
|
-
*
|
|
1477
|
-
* @param handler Handler function to call on transfer progress.
|
|
1478
|
-
*/
|
|
1479
|
-
trackProgress(handler) {
|
|
1480
|
-
this._progressTracker.bytesOverall = 0;
|
|
1481
|
-
this._progressTracker.reportTo(handler);
|
|
1482
|
-
}
|
|
1483
|
-
/**
|
|
1484
|
-
* Upload data from a readable stream or a local file to a remote file.
|
|
1485
|
-
*
|
|
1486
|
-
* @param source Readable stream or path to a local file.
|
|
1487
|
-
* @param toRemotePath Path to a remote file to write to.
|
|
1488
|
-
*/
|
|
1489
|
-
async uploadFrom(source, toRemotePath, options = {}) {
|
|
1490
|
-
return this._uploadWithCommand(source, toRemotePath, "STOR", options);
|
|
1491
|
-
}
|
|
1492
|
-
/**
|
|
1493
|
-
* Upload data from a readable stream or a local file by appending it to an existing file. If the file doesn't
|
|
1494
|
-
* exist the FTP server should create it.
|
|
1495
|
-
*
|
|
1496
|
-
* @param source Readable stream or path to a local file.
|
|
1497
|
-
* @param toRemotePath Path to a remote file to write to.
|
|
1498
|
-
*/
|
|
1499
|
-
async appendFrom(source, toRemotePath, options = {}) {
|
|
1500
|
-
return this._uploadWithCommand(source, toRemotePath, "APPE", options);
|
|
1501
|
-
}
|
|
1502
|
-
/**
|
|
1503
|
-
* @protected
|
|
1504
|
-
*/
|
|
1505
|
-
async _uploadWithCommand(source, remotePath, command, options) {
|
|
1506
|
-
if (typeof source === "string") return this._uploadLocalFile(source, remotePath, command, options);
|
|
1507
|
-
return this._uploadFromStream(source, remotePath, command);
|
|
1508
|
-
}
|
|
1509
|
-
/**
|
|
1510
|
-
* @protected
|
|
1511
|
-
*/
|
|
1512
|
-
async _uploadLocalFile(localPath, remotePath, command, options) {
|
|
1513
|
-
const fd = await fsOpen(localPath, "r");
|
|
1514
|
-
const source = (0, fs_1.createReadStream)("", {
|
|
1515
|
-
fd,
|
|
1516
|
-
start: options.localStart,
|
|
1517
|
-
end: options.localEndInclusive,
|
|
1518
|
-
autoClose: false
|
|
1519
|
-
});
|
|
1520
|
-
try {
|
|
1521
|
-
return await this._uploadFromStream(source, remotePath, command);
|
|
1522
|
-
} finally {
|
|
1523
|
-
await ignoreError(() => fsClose(fd));
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
/**
|
|
1527
|
-
* @protected
|
|
1528
|
-
*/
|
|
1529
|
-
async _uploadFromStream(source, remotePath, command) {
|
|
1530
|
-
const onError = (err) => this.ftp.closeWithError(err);
|
|
1531
|
-
source.once("error", onError);
|
|
1532
|
-
try {
|
|
1533
|
-
const validPath = await this.protectWhitespace(remotePath);
|
|
1534
|
-
await this.prepareTransfer(this.ftp);
|
|
1535
|
-
return await (0, transfer_1.uploadFrom)(source, {
|
|
1536
|
-
ftp: this.ftp,
|
|
1537
|
-
tracker: this._progressTracker,
|
|
1538
|
-
command,
|
|
1539
|
-
remotePath: validPath,
|
|
1540
|
-
type: "upload"
|
|
1541
|
-
});
|
|
1542
|
-
} finally {
|
|
1543
|
-
source.removeListener("error", onError);
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
/**
|
|
1547
|
-
* Download a remote file and pipe its data to a writable stream or to a local file.
|
|
1548
|
-
*
|
|
1549
|
-
* You can optionally define at which position of the remote file you'd like to start
|
|
1550
|
-
* downloading. If the destination you provide is a file, the offset will be applied
|
|
1551
|
-
* to it as well. For example: To resume a failed download, you'd request the size of
|
|
1552
|
-
* the local, partially downloaded file and use that as the offset. Assuming the size
|
|
1553
|
-
* is 23, you'd download the rest using `downloadTo("local.txt", "remote.txt", 23)`.
|
|
1554
|
-
*
|
|
1555
|
-
* @param destination Stream or path for a local file to write to.
|
|
1556
|
-
* @param fromRemotePath Path of the remote file to read from.
|
|
1557
|
-
* @param startAt Position within the remote file to start downloading at. If the destination is a file, this offset is also applied to it.
|
|
1558
|
-
*/
|
|
1559
|
-
async downloadTo(destination, fromRemotePath, startAt = 0) {
|
|
1560
|
-
if (typeof destination === "string") return this._downloadToFile(destination, fromRemotePath, startAt);
|
|
1561
|
-
return this._downloadToStream(destination, fromRemotePath, startAt);
|
|
1562
|
-
}
|
|
1563
|
-
/**
|
|
1564
|
-
* @protected
|
|
1565
|
-
*/
|
|
1566
|
-
async _downloadToFile(localPath, remotePath, startAt) {
|
|
1567
|
-
const appendingToLocalFile = startAt > 0;
|
|
1568
|
-
const fd = await fsOpen(localPath, appendingToLocalFile ? "r+" : "w");
|
|
1569
|
-
const destination = (0, fs_1.createWriteStream)("", {
|
|
1570
|
-
fd,
|
|
1571
|
-
start: startAt,
|
|
1572
|
-
autoClose: false
|
|
1573
|
-
});
|
|
1574
|
-
try {
|
|
1575
|
-
return await this._downloadToStream(destination, remotePath, startAt);
|
|
1576
|
-
} catch (err) {
|
|
1577
|
-
const localFileStats = await ignoreError(() => fsStat(localPath));
|
|
1578
|
-
const hasDownloadedData = localFileStats && localFileStats.size > 0;
|
|
1579
|
-
if (!appendingToLocalFile && !hasDownloadedData) await ignoreError(() => fsUnlink(localPath));
|
|
1580
|
-
throw err;
|
|
1581
|
-
} finally {
|
|
1582
|
-
await ignoreError(() => fsClose(fd));
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
/**
|
|
1586
|
-
* @protected
|
|
1587
|
-
*/
|
|
1588
|
-
async _downloadToStream(destination, remotePath, startAt) {
|
|
1589
|
-
const onError = (err) => this.ftp.closeWithError(err);
|
|
1590
|
-
destination.once("error", onError);
|
|
1591
|
-
try {
|
|
1592
|
-
const validPath = await this.protectWhitespace(remotePath);
|
|
1593
|
-
await this.prepareTransfer(this.ftp);
|
|
1594
|
-
return await (0, transfer_1.downloadTo)(destination, {
|
|
1595
|
-
ftp: this.ftp,
|
|
1596
|
-
tracker: this._progressTracker,
|
|
1597
|
-
command: startAt > 0 ? `REST ${startAt}` : `RETR ${validPath}`,
|
|
1598
|
-
remotePath: validPath,
|
|
1599
|
-
type: "download"
|
|
1600
|
-
});
|
|
1601
|
-
} finally {
|
|
1602
|
-
destination.removeListener("error", onError);
|
|
1603
|
-
destination.end();
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
/**
|
|
1607
|
-
* List files and directories in the current working directory, or from `path` if specified.
|
|
1608
|
-
*
|
|
1609
|
-
* @param [path] Path to remote file or directory.
|
|
1610
|
-
*/
|
|
1611
|
-
async list(path = "") {
|
|
1612
|
-
const validPath = await this.protectWhitespace(path);
|
|
1613
|
-
let lastError;
|
|
1614
|
-
for (const candidate of this.availableListCommands) {
|
|
1615
|
-
const command = validPath === "" ? candidate : `${candidate} ${validPath}`;
|
|
1616
|
-
await this.prepareTransfer(this.ftp);
|
|
1617
|
-
try {
|
|
1618
|
-
const parsedList = await this._requestListWithCommand(command);
|
|
1619
|
-
this.availableListCommands = [candidate];
|
|
1620
|
-
return parsedList;
|
|
1621
|
-
} catch (err) {
|
|
1622
|
-
if (!(err instanceof FtpContext_1.FTPError)) throw err;
|
|
1623
|
-
lastError = err;
|
|
1624
|
-
}
|
|
1625
|
-
}
|
|
1626
|
-
throw lastError;
|
|
1627
|
-
}
|
|
1628
|
-
/**
|
|
1629
|
-
* @protected
|
|
1630
|
-
*/
|
|
1631
|
-
async _requestListWithCommand(command) {
|
|
1632
|
-
const buffer = new StringWriter_1.StringWriter();
|
|
1633
|
-
await (0, transfer_1.downloadTo)(buffer, {
|
|
1634
|
-
ftp: this.ftp,
|
|
1635
|
-
tracker: this._progressTracker,
|
|
1636
|
-
command,
|
|
1637
|
-
remotePath: "",
|
|
1638
|
-
type: "list"
|
|
1639
|
-
});
|
|
1640
|
-
const text = buffer.getText(this.ftp.encoding);
|
|
1641
|
-
this.ftp.log(text);
|
|
1642
|
-
return this.parseList(text);
|
|
1643
|
-
}
|
|
1644
|
-
/**
|
|
1645
|
-
* Remove a directory and all of its content.
|
|
1646
|
-
*
|
|
1647
|
-
* @param remoteDirPath The path of the remote directory to delete.
|
|
1648
|
-
* @example client.removeDir("foo") // Remove directory 'foo' using a relative path.
|
|
1649
|
-
* @example client.removeDir("foo/bar") // Remove directory 'bar' using a relative path.
|
|
1650
|
-
* @example client.removeDir("/foo/bar") // Remove directory 'bar' using an absolute path.
|
|
1651
|
-
* @example client.removeDir("/") // Remove everything.
|
|
1652
|
-
*/
|
|
1653
|
-
async removeDir(remoteDirPath) {
|
|
1654
|
-
return this._exitAtCurrentDirectory(async () => {
|
|
1655
|
-
await this.cd(remoteDirPath);
|
|
1656
|
-
const absoluteDirPath = await this.pwd();
|
|
1657
|
-
await this.clearWorkingDir();
|
|
1658
|
-
if (!(absoluteDirPath === "/")) {
|
|
1659
|
-
await this.cdup();
|
|
1660
|
-
await this.removeEmptyDir(absoluteDirPath);
|
|
1661
|
-
}
|
|
1662
|
-
});
|
|
1663
|
-
}
|
|
1664
|
-
/**
|
|
1665
|
-
* Remove all files and directories in the working directory without removing
|
|
1666
|
-
* the working directory itself.
|
|
1667
|
-
*/
|
|
1668
|
-
async clearWorkingDir() {
|
|
1669
|
-
for (const file of await this.list()) if (file.isDirectory) {
|
|
1670
|
-
await this.cd(file.name);
|
|
1671
|
-
await this.clearWorkingDir();
|
|
1672
|
-
await this.cdup();
|
|
1673
|
-
await this.removeEmptyDir(file.name);
|
|
1674
|
-
} else await this.remove(file.name);
|
|
1675
|
-
}
|
|
1676
|
-
/**
|
|
1677
|
-
* Upload the contents of a local directory to the remote working directory.
|
|
1678
|
-
*
|
|
1679
|
-
* This will overwrite existing files with the same names and reuse existing directories.
|
|
1680
|
-
* Unrelated files and directories will remain untouched. You can optionally provide a `remoteDirPath`
|
|
1681
|
-
* to put the contents inside a directory which will be created if necessary including all
|
|
1682
|
-
* intermediate directories. If you did provide a remoteDirPath the working directory will stay
|
|
1683
|
-
* the same as before calling this method.
|
|
1684
|
-
*
|
|
1685
|
-
* @param localDirPath Local path, e.g. "foo/bar" or "../test"
|
|
1686
|
-
* @param [remoteDirPath] Remote path of a directory to upload to. Working directory if undefined.
|
|
1687
|
-
*/
|
|
1688
|
-
async uploadFromDir(localDirPath, remoteDirPath) {
|
|
1689
|
-
return this._exitAtCurrentDirectory(async () => {
|
|
1690
|
-
if (remoteDirPath) await this.ensureDir(remoteDirPath);
|
|
1691
|
-
return await this._uploadToWorkingDir(localDirPath);
|
|
1692
|
-
});
|
|
1693
|
-
}
|
|
1694
|
-
/**
|
|
1695
|
-
* @protected
|
|
1696
|
-
*/
|
|
1697
|
-
async _uploadToWorkingDir(localDirPath) {
|
|
1698
|
-
const files = await fsReadDir(localDirPath);
|
|
1699
|
-
for (const file of files) {
|
|
1700
|
-
const fullPath = (0, path_1.join)(localDirPath, file);
|
|
1701
|
-
const stats = await fsStat(fullPath);
|
|
1702
|
-
if (stats.isFile()) await this.uploadFrom(fullPath, file);
|
|
1703
|
-
else if (stats.isDirectory()) {
|
|
1704
|
-
await this._openDir(file);
|
|
1705
|
-
await this._uploadToWorkingDir(fullPath);
|
|
1706
|
-
await this.cdup();
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
|
-
/**
|
|
1711
|
-
* Download all files and directories of the working directory to a local directory.
|
|
1712
|
-
*
|
|
1713
|
-
* @param localDirPath The local directory to download to.
|
|
1714
|
-
* @param remoteDirPath Remote directory to download. Current working directory if not specified.
|
|
1715
|
-
*/
|
|
1716
|
-
async downloadToDir(localDirPath, remoteDirPath) {
|
|
1717
|
-
return this._exitAtCurrentDirectory(async () => {
|
|
1718
|
-
if (remoteDirPath) await this.cd(remoteDirPath);
|
|
1719
|
-
return await this._downloadFromWorkingDir(localDirPath);
|
|
1720
|
-
});
|
|
1721
|
-
}
|
|
1722
|
-
/**
|
|
1723
|
-
* @protected
|
|
1724
|
-
*/
|
|
1725
|
-
async _downloadFromWorkingDir(localDirPath) {
|
|
1726
|
-
await ensureLocalDirectory(localDirPath);
|
|
1727
|
-
for (const file of await this.list()) {
|
|
1728
|
-
if (!file.name || (0, path_1.basename)(file.name) !== file.name) {
|
|
1729
|
-
const safeName = JSON.stringify(file.name);
|
|
1730
|
-
this.ftp.log(`Invalid filename from server listing, will skip file. (${safeName})`);
|
|
1731
|
-
continue;
|
|
1732
|
-
}
|
|
1733
|
-
const localPath = (0, path_1.join)(localDirPath, file.name);
|
|
1734
|
-
if (file.isDirectory) {
|
|
1735
|
-
await this.cd(file.name);
|
|
1736
|
-
await this._downloadFromWorkingDir(localPath);
|
|
1737
|
-
await this.cdup();
|
|
1738
|
-
} else if (file.isFile) await this.downloadTo(localPath, file.name);
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
/**
|
|
1742
|
-
* Make sure a given remote path exists, creating all directories as necessary.
|
|
1743
|
-
* This function also changes the current working directory to the given path.
|
|
1744
|
-
*/
|
|
1745
|
-
async ensureDir(remoteDirPath) {
|
|
1746
|
-
if (remoteDirPath.startsWith("/")) await this.cd("/");
|
|
1747
|
-
const names = remoteDirPath.split("/").filter((name) => name !== "");
|
|
1748
|
-
for (const name of names) await this._openDir(name);
|
|
1749
|
-
}
|
|
1750
|
-
/**
|
|
1751
|
-
* Try to create a directory and enter it. This will not raise an exception if the directory
|
|
1752
|
-
* couldn't be created if for example it already exists.
|
|
1753
|
-
* @protected
|
|
1754
|
-
*/
|
|
1755
|
-
async _openDir(dirName) {
|
|
1756
|
-
await this.sendIgnoringError("MKD " + dirName);
|
|
1757
|
-
await this.cd(dirName);
|
|
1758
|
-
}
|
|
1759
|
-
/**
|
|
1760
|
-
* Remove an empty directory, will fail if not empty.
|
|
1761
|
-
*/
|
|
1762
|
-
async removeEmptyDir(path) {
|
|
1763
|
-
const validPath = await this.protectWhitespace(path);
|
|
1764
|
-
return this.send(`RMD ${validPath}`);
|
|
1765
|
-
}
|
|
1766
|
-
/**
|
|
1767
|
-
* FTP servers can't handle filenames that have leading whitespace. This method transforms
|
|
1768
|
-
* a given path to fix that issue for most cases.
|
|
1769
|
-
*/
|
|
1770
|
-
async protectWhitespace(path) {
|
|
1771
|
-
if (!path.startsWith(" ")) return path;
|
|
1772
|
-
const pwd = await this.pwd();
|
|
1773
|
-
return (pwd.endsWith("/") ? pwd : pwd + "/") + path;
|
|
1774
|
-
}
|
|
1775
|
-
async _exitAtCurrentDirectory(func) {
|
|
1776
|
-
const userDir = await this.pwd();
|
|
1777
|
-
try {
|
|
1778
|
-
return await func();
|
|
1779
|
-
} finally {
|
|
1780
|
-
if (!this.closed) await ignoreError(() => this.cd(userDir));
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
/**
|
|
1784
|
-
* Try all available transfer strategies and pick the first one that works. Update `client` to
|
|
1785
|
-
* use the working strategy for all successive transfer requests.
|
|
1786
|
-
*
|
|
1787
|
-
* @returns a function that will try the provided strategies.
|
|
1788
|
-
*/
|
|
1789
|
-
_enterFirstCompatibleMode(strategies) {
|
|
1790
|
-
return async (ftp) => {
|
|
1791
|
-
ftp.log("Trying to find optimal transfer strategy...");
|
|
1792
|
-
let lastError = void 0;
|
|
1793
|
-
for (const strategy of strategies) try {
|
|
1794
|
-
const res = await strategy(ftp);
|
|
1795
|
-
ftp.log("Optimal transfer strategy found.");
|
|
1796
|
-
this.prepareTransfer = strategy;
|
|
1797
|
-
return res;
|
|
1798
|
-
} catch (err) {
|
|
1799
|
-
lastError = err;
|
|
1800
|
-
}
|
|
1801
|
-
throw new Error(`None of the available transfer strategies work. Last error response was '${lastError}'.`);
|
|
1802
|
-
};
|
|
1803
|
-
}
|
|
1804
|
-
/**
|
|
1805
|
-
* DEPRECATED, use `uploadFrom`.
|
|
1806
|
-
* @deprecated
|
|
1807
|
-
*/
|
|
1808
|
-
async upload(source, toRemotePath, options = {}) {
|
|
1809
|
-
this.ftp.log("Warning: upload() has been deprecated, use uploadFrom().");
|
|
1810
|
-
return this.uploadFrom(source, toRemotePath, options);
|
|
1811
|
-
}
|
|
1812
|
-
/**
|
|
1813
|
-
* DEPRECATED, use `appendFrom`.
|
|
1814
|
-
* @deprecated
|
|
1815
|
-
*/
|
|
1816
|
-
async append(source, toRemotePath, options = {}) {
|
|
1817
|
-
this.ftp.log("Warning: append() has been deprecated, use appendFrom().");
|
|
1818
|
-
return this.appendFrom(source, toRemotePath, options);
|
|
1819
|
-
}
|
|
1820
|
-
/**
|
|
1821
|
-
* DEPRECATED, use `downloadTo`.
|
|
1822
|
-
* @deprecated
|
|
1823
|
-
*/
|
|
1824
|
-
async download(destination, fromRemotePath, startAt = 0) {
|
|
1825
|
-
this.ftp.log("Warning: download() has been deprecated, use downloadTo().");
|
|
1826
|
-
return this.downloadTo(destination, fromRemotePath, startAt);
|
|
1827
|
-
}
|
|
1828
|
-
/**
|
|
1829
|
-
* DEPRECATED, use `uploadFromDir`.
|
|
1830
|
-
* @deprecated
|
|
1831
|
-
*/
|
|
1832
|
-
async uploadDir(localDirPath, remoteDirPath) {
|
|
1833
|
-
this.ftp.log("Warning: uploadDir() has been deprecated, use uploadFromDir().");
|
|
1834
|
-
return this.uploadFromDir(localDirPath, remoteDirPath);
|
|
1835
|
-
}
|
|
1836
|
-
/**
|
|
1837
|
-
* DEPRECATED, use `downloadToDir`.
|
|
1838
|
-
* @deprecated
|
|
1839
|
-
*/
|
|
1840
|
-
async downloadDir(localDirPath) {
|
|
1841
|
-
this.ftp.log("Warning: downloadDir() has been deprecated, use downloadToDir().");
|
|
1842
|
-
return this.downloadToDir(localDirPath);
|
|
1843
|
-
}
|
|
1844
|
-
};
|
|
1845
|
-
exports.Client = Client;
|
|
1846
|
-
async function ensureLocalDirectory(path) {
|
|
1847
|
-
try {
|
|
1848
|
-
await fsStat(path);
|
|
1849
|
-
} catch (_a) {
|
|
1850
|
-
await fsMkDir(path, { recursive: true });
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
async function ignoreError(func) {
|
|
1854
|
-
try {
|
|
1855
|
-
return await func();
|
|
1856
|
-
} catch (_a) {
|
|
1857
|
-
return;
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
}));
|
|
1861
|
-
var require_StringEncoding = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
1862
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1863
|
-
}));
|
|
1864
|
-
var require_dist = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
1865
|
-
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
1866
|
-
if (k2 === void 0) k2 = k;
|
|
1867
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1868
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) desc = {
|
|
1869
|
-
enumerable: true,
|
|
1870
|
-
get: function() {
|
|
1871
|
-
return m[k];
|
|
1872
|
-
}
|
|
1873
|
-
};
|
|
1874
|
-
Object.defineProperty(o, k2, desc);
|
|
1875
|
-
}) : (function(o, m, k, k2) {
|
|
1876
|
-
if (k2 === void 0) k2 = k;
|
|
1877
|
-
o[k2] = m[k];
|
|
1878
|
-
}));
|
|
1879
|
-
var __exportStar = exports && exports.__exportStar || function(m, exports$1) {
|
|
1880
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
|
|
1881
|
-
};
|
|
1882
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1883
|
-
exports.enterPassiveModeIPv6 = exports.enterPassiveModeIPv4 = void 0;
|
|
1884
|
-
/**
|
|
1885
|
-
* Public API
|
|
1886
|
-
*/
|
|
1887
|
-
__exportStar(require_Client(), exports);
|
|
1888
|
-
__exportStar(require_FtpContext(), exports);
|
|
1889
|
-
__exportStar(require_FileInfo(), exports);
|
|
1890
|
-
__exportStar(require_parseList(), exports);
|
|
1891
|
-
__exportStar(require_StringEncoding(), exports);
|
|
1892
|
-
var transfer_1 = require_transfer();
|
|
1893
|
-
Object.defineProperty(exports, "enterPassiveModeIPv4", {
|
|
1894
|
-
enumerable: true,
|
|
1895
|
-
get: function() {
|
|
1896
|
-
return transfer_1.enterPassiveModeIPv4;
|
|
1897
|
-
}
|
|
1898
|
-
});
|
|
1899
|
-
Object.defineProperty(exports, "enterPassiveModeIPv6", {
|
|
1900
|
-
enumerable: true,
|
|
1901
|
-
get: function() {
|
|
1902
|
-
return transfer_1.enterPassiveModeIPv6;
|
|
1903
|
-
}
|
|
1904
|
-
});
|
|
1905
|
-
}));
|
|
1906
|
-
export { require_dist as t };
|