trzsz2 1.0.0
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/LICENSE +21 -0
- package/README.cn.md +64 -0
- package/README.md +70 -0
- package/dist/browser/buffer.d.ts +76 -0
- package/dist/browser/comm.d.ts +162 -0
- package/dist/browser/escape.d.ts +33 -0
- package/dist/browser/index.d.ts +17 -0
- package/dist/browser/options.d.ts +57 -0
- package/dist/browser/progress.d.ts +100 -0
- package/dist/browser/transfer.d.ts +259 -0
- package/dist/browser/trzsz2.js +6147 -0
- package/dist/browser/trzsz2.js.map +1 -0
- package/dist/browser/trzsz2.min.js +19 -0
- package/dist/buffer.d.ts +76 -0
- package/dist/cjs/buffer.cjs +252 -0
- package/dist/cjs/buffer.cjs.map +1 -0
- package/dist/cjs/buffer.d.ts +76 -0
- package/dist/cjs/comm.cjs +292 -0
- package/dist/cjs/comm.cjs.map +1 -0
- package/dist/cjs/comm.d.ts +162 -0
- package/dist/cjs/escape.cjs +86 -0
- package/dist/cjs/escape.cjs.map +1 -0
- package/dist/cjs/escape.d.ts +33 -0
- package/dist/cjs/index.cjs +30 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.ts +17 -0
- package/dist/cjs/node_modules/pako/dist/pako.esm.cjs +4177 -0
- package/dist/cjs/node_modules/pako/dist/pako.esm.cjs.map +1 -0
- package/dist/cjs/node_modules/ts-md5/dist/esm/md5.cjs +353 -0
- package/dist/cjs/node_modules/ts-md5/dist/esm/md5.cjs.map +1 -0
- package/dist/cjs/options.d.ts +57 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/progress.cjs +313 -0
- package/dist/cjs/progress.cjs.map +1 -0
- package/dist/cjs/progress.d.ts +100 -0
- package/dist/cjs/transfer.cjs +706 -0
- package/dist/cjs/transfer.cjs.map +1 -0
- package/dist/cjs/transfer.d.ts +259 -0
- package/dist/cjs-full/buffer.d.ts +76 -0
- package/dist/cjs-full/comm.d.ts +162 -0
- package/dist/cjs-full/escape.d.ts +33 -0
- package/dist/cjs-full/index.cjs +6144 -0
- package/dist/cjs-full/index.cjs.map +1 -0
- package/dist/cjs-full/index.d.ts +17 -0
- package/dist/cjs-full/options.d.ts +57 -0
- package/dist/cjs-full/package.json +3 -0
- package/dist/cjs-full/progress.d.ts +100 -0
- package/dist/cjs-full/transfer.d.ts +259 -0
- package/dist/comm.d.ts +162 -0
- package/dist/escape.d.ts +33 -0
- package/dist/esm/buffer.d.ts +76 -0
- package/dist/esm/buffer.js +252 -0
- package/dist/esm/buffer.js.map +1 -0
- package/dist/esm/comm.d.ts +162 -0
- package/dist/esm/comm.js +292 -0
- package/dist/esm/comm.js.map +1 -0
- package/dist/esm/escape.d.ts +33 -0
- package/dist/esm/escape.js +86 -0
- package/dist/esm/escape.js.map +1 -0
- package/dist/esm/index.d.ts +17 -0
- package/dist/esm/index.js +30 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/node_modules/pako/dist/pako.esm.js +4177 -0
- package/dist/esm/node_modules/pako/dist/pako.esm.js.map +1 -0
- package/dist/esm/node_modules/ts-md5/dist/esm/md5.js +353 -0
- package/dist/esm/node_modules/ts-md5/dist/esm/md5.js.map +1 -0
- package/dist/esm/options.d.ts +57 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/progress.d.ts +100 -0
- package/dist/esm/progress.js +313 -0
- package/dist/esm/progress.js.map +1 -0
- package/dist/esm/transfer.d.ts +259 -0
- package/dist/esm/transfer.js +706 -0
- package/dist/esm/transfer.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/options.d.ts +57 -0
- package/dist/progress.d.ts +100 -0
- package/dist/transfer.d.ts +259 -0
- package/package.json +108 -0
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
import { Md5 } from "./node_modules/ts-md5/dist/esm/md5.js";
|
|
2
|
+
import { TrzszBuffer } from "./buffer.js";
|
|
3
|
+
import { escapeData, unescapeData, escapeCharsToCodes } from "./escape.js";
|
|
4
|
+
import { TrzszError, stripTmuxStatusLine, encodeBuffer, uint8ToStr, decodeBuffer, trzszVersion, TmuxMode } from "./comm.js";
|
|
5
|
+
class TrzszTransfer {
|
|
6
|
+
/**
|
|
7
|
+
* Create a TrzszTransfer.
|
|
8
|
+
* @param writer - The output writer function.
|
|
9
|
+
* @param isWindowsShell - Whether the shell is Windows.
|
|
10
|
+
*/
|
|
11
|
+
constructor(writer, isWindowsShell = false) {
|
|
12
|
+
this.buffer = new TrzszBuffer();
|
|
13
|
+
this.remoteIsWindows = false;
|
|
14
|
+
this.lastInputTime = 0;
|
|
15
|
+
this.openedFiles = [];
|
|
16
|
+
this.createdFiles = [];
|
|
17
|
+
this.tmuxOutputJunk = false;
|
|
18
|
+
this.cleanTimeoutInMilliseconds = 100;
|
|
19
|
+
this.transferConfig = {};
|
|
20
|
+
this.stopped = false;
|
|
21
|
+
this.maxChunkTimeInMilliseconds = 0;
|
|
22
|
+
this.protocolNewline = "\n";
|
|
23
|
+
this.writer = writer;
|
|
24
|
+
this.isWindowsShell = isWindowsShell;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Cleanup resources.
|
|
28
|
+
*/
|
|
29
|
+
cleanup() {
|
|
30
|
+
for (const file of this.openedFiles) {
|
|
31
|
+
file.closeFile();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Add received data to the buffer.
|
|
36
|
+
* @param data - The received data.
|
|
37
|
+
*/
|
|
38
|
+
addReceivedData(data) {
|
|
39
|
+
if (!this.stopped) {
|
|
40
|
+
this.buffer.addBuffer(data);
|
|
41
|
+
}
|
|
42
|
+
this.lastInputTime = Date.now();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Stop transferring.
|
|
46
|
+
*/
|
|
47
|
+
async stopTransferring() {
|
|
48
|
+
this.cleanTimeoutInMilliseconds = Math.max(this.maxChunkTimeInMilliseconds * 2, 500);
|
|
49
|
+
this.stopped = true;
|
|
50
|
+
this.buffer.stopBuffer();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Clean input buffer.
|
|
54
|
+
* @param timeoutInMilliseconds - The timeout in milliseconds.
|
|
55
|
+
*/
|
|
56
|
+
async cleanInput(timeoutInMilliseconds) {
|
|
57
|
+
this.stopped = true;
|
|
58
|
+
this.buffer.drainBuffer();
|
|
59
|
+
this.lastInputTime = Date.now();
|
|
60
|
+
while (true) {
|
|
61
|
+
const sleepTime = timeoutInMilliseconds - (Date.now() - this.lastInputTime);
|
|
62
|
+
if (sleepTime <= 0) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
await new Promise((resolve) => setTimeout(resolve, sleepTime));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Send a line.
|
|
70
|
+
* @param typ - The type.
|
|
71
|
+
* @param buf - The buffer.
|
|
72
|
+
*/
|
|
73
|
+
async sendLine(typ, buf) {
|
|
74
|
+
this.writer(`#${typ}:${buf}${this.protocolNewline}`);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Receive a line.
|
|
78
|
+
* @param expectType - The expected type.
|
|
79
|
+
* @param mayHasJunk - Whether there may be junk.
|
|
80
|
+
*/
|
|
81
|
+
async recvLine(expectType, mayHasJunk = false) {
|
|
82
|
+
if (this.stopped) {
|
|
83
|
+
throw new TrzszError("Stopped");
|
|
84
|
+
}
|
|
85
|
+
if (this.isWindowsShell || this.remoteIsWindows) {
|
|
86
|
+
let line2 = await this.buffer.readLineOnWindows();
|
|
87
|
+
const idx = line2.lastIndexOf("#" + expectType + ":");
|
|
88
|
+
if (idx >= 0) {
|
|
89
|
+
line2 = line2.substring(idx);
|
|
90
|
+
} else {
|
|
91
|
+
const idx2 = line2.lastIndexOf("#");
|
|
92
|
+
if (idx2 > 0) {
|
|
93
|
+
line2 = line2.substring(idx2);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return line2;
|
|
97
|
+
}
|
|
98
|
+
let line = await this.buffer.readLine();
|
|
99
|
+
if (this.tmuxOutputJunk || mayHasJunk) {
|
|
100
|
+
if (line.length > 0) {
|
|
101
|
+
while (line[line.length - 1] === "\r") {
|
|
102
|
+
line = line.substring(0, line.length - 1) + await this.buffer.readLine();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const idx = line.lastIndexOf("#" + expectType + ":");
|
|
106
|
+
if (idx >= 0) {
|
|
107
|
+
line = line.substring(idx);
|
|
108
|
+
} else {
|
|
109
|
+
const idx2 = line.lastIndexOf("#");
|
|
110
|
+
if (idx2 > 0) {
|
|
111
|
+
line = line.substring(idx2);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
line = stripTmuxStatusLine(line);
|
|
115
|
+
}
|
|
116
|
+
return line;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Receive and check a line.
|
|
120
|
+
* @param expectType - The expected type.
|
|
121
|
+
* @param mayHasJunk - Whether there may be junk.
|
|
122
|
+
*/
|
|
123
|
+
async recvCheck(expectType, mayHasJunk = false) {
|
|
124
|
+
const line = await this.recvLine(expectType, mayHasJunk);
|
|
125
|
+
const idx = line.indexOf(":");
|
|
126
|
+
if (idx < 1) {
|
|
127
|
+
throw new TrzszError(encodeBuffer(line), "colon", true);
|
|
128
|
+
}
|
|
129
|
+
const typ = line.substring(1, idx);
|
|
130
|
+
const buf = line.substring(idx + 1);
|
|
131
|
+
if (typ !== expectType) {
|
|
132
|
+
throw new TrzszError(buf, typ, true);
|
|
133
|
+
}
|
|
134
|
+
return buf;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Send an integer.
|
|
138
|
+
* @param typ - The type.
|
|
139
|
+
* @param val - The value.
|
|
140
|
+
*/
|
|
141
|
+
async sendInteger(typ, val) {
|
|
142
|
+
await this.sendLine(typ, val.toString());
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Receive an integer.
|
|
146
|
+
* @param typ - The type.
|
|
147
|
+
* @param mayHasJunk - Whether there may be junk.
|
|
148
|
+
*/
|
|
149
|
+
async recvInteger(typ, mayHasJunk = false) {
|
|
150
|
+
const buf = await this.recvCheck(typ, mayHasJunk);
|
|
151
|
+
return Number(buf);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Check an integer.
|
|
155
|
+
* @param expect - The expected value.
|
|
156
|
+
*/
|
|
157
|
+
async checkInteger(expect) {
|
|
158
|
+
const result = await this.recvInteger("SUCC");
|
|
159
|
+
if (result !== expect) {
|
|
160
|
+
throw new TrzszError(`Integer check [${result}] <> [${expect}]`, null, true);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Send a string.
|
|
165
|
+
* @param typ - The type.
|
|
166
|
+
* @param str - The string.
|
|
167
|
+
*/
|
|
168
|
+
async sendString(typ, str) {
|
|
169
|
+
await this.sendLine(typ, encodeBuffer(str));
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Receive a string.
|
|
173
|
+
* @param typ - The type.
|
|
174
|
+
* @param mayHasJunk - Whether there may be junk.
|
|
175
|
+
*/
|
|
176
|
+
async recvString(typ, mayHasJunk = false) {
|
|
177
|
+
const buf = await this.recvCheck(typ, mayHasJunk);
|
|
178
|
+
return await uint8ToStr(decodeBuffer(buf), "utf8");
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Check a string.
|
|
182
|
+
* @param expect - The expected string.
|
|
183
|
+
*/
|
|
184
|
+
async checkString(expect) {
|
|
185
|
+
const result = await this.recvString("SUCC");
|
|
186
|
+
if (result !== expect) {
|
|
187
|
+
throw new TrzszError(`String check [${result}] <> [${expect}]`, null, true);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Send binary data.
|
|
192
|
+
* @param typ - The type.
|
|
193
|
+
* @param buf - The buffer.
|
|
194
|
+
*/
|
|
195
|
+
async sendBinary(typ, buf) {
|
|
196
|
+
await this.sendLine(typ, encodeBuffer(buf));
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Receive binary data.
|
|
200
|
+
* @param typ - The type.
|
|
201
|
+
* @param mayHasJunk - Whether there may be junk.
|
|
202
|
+
*/
|
|
203
|
+
async recvBinary(typ, mayHasJunk = false) {
|
|
204
|
+
const buf = await this.recvCheck(typ, mayHasJunk);
|
|
205
|
+
return decodeBuffer(buf);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check binary data.
|
|
209
|
+
* @param expect - The expected buffer.
|
|
210
|
+
*/
|
|
211
|
+
async checkBinary(expect) {
|
|
212
|
+
const result = await this.recvBinary("SUCC");
|
|
213
|
+
if (result.length !== expect.length) {
|
|
214
|
+
throw new TrzszError(
|
|
215
|
+
`Binary length check [${result.length}] <> [${expect.length}]`,
|
|
216
|
+
null,
|
|
217
|
+
true
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
for (let i = 0; i < result.length; i++) {
|
|
221
|
+
if (result[i] !== expect[i]) {
|
|
222
|
+
throw new TrzszError(`Binary check [${result[i]}] <> [${expect[i]}]`, null, true);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Send data.
|
|
228
|
+
* @param data - The data.
|
|
229
|
+
* @param binary - Whether to use binary mode.
|
|
230
|
+
* @param escapeCodes - The escape codes.
|
|
231
|
+
*/
|
|
232
|
+
async sendData(data, binary, escapeCodes) {
|
|
233
|
+
if (!binary) {
|
|
234
|
+
await this.sendBinary("DATA", data);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const buf = escapeData(data, escapeCodes);
|
|
238
|
+
this.writer(`#DATA:${buf.length}
|
|
239
|
+
`);
|
|
240
|
+
this.writer(buf);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Receive data.
|
|
244
|
+
* @param binary - Whether to use binary mode.
|
|
245
|
+
* @param escapeCodes - The escape codes.
|
|
246
|
+
* @param timeoutInMilliseconds - The timeout in milliseconds.
|
|
247
|
+
*/
|
|
248
|
+
async recvData(binary, escapeCodes, timeoutInMilliseconds) {
|
|
249
|
+
return await Promise.race([
|
|
250
|
+
new Promise(
|
|
251
|
+
(_resolve, reject) => setTimeout(() => {
|
|
252
|
+
this.cleanTimeoutInMilliseconds = 3e3;
|
|
253
|
+
reject(new TrzszError("Receive data timeout"));
|
|
254
|
+
}, timeoutInMilliseconds)
|
|
255
|
+
),
|
|
256
|
+
(async () => {
|
|
257
|
+
if (!binary) {
|
|
258
|
+
return await this.recvBinary("DATA");
|
|
259
|
+
}
|
|
260
|
+
const size = await this.recvInteger("DATA");
|
|
261
|
+
const data = await this.buffer.readBinary(size);
|
|
262
|
+
return unescapeData(data, escapeCodes);
|
|
263
|
+
})()
|
|
264
|
+
]);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Send action.
|
|
268
|
+
* @param confirm - Whether to confirm.
|
|
269
|
+
* @param remoteIsWindows - Whether the remote is Windows.
|
|
270
|
+
*/
|
|
271
|
+
async sendAction(confirm, remoteIsWindows) {
|
|
272
|
+
const action = {
|
|
273
|
+
lang: "js",
|
|
274
|
+
confirm,
|
|
275
|
+
version: trzszVersion,
|
|
276
|
+
support_dir: true
|
|
277
|
+
};
|
|
278
|
+
if (this.isWindowsShell || remoteIsWindows) {
|
|
279
|
+
action.binary = false;
|
|
280
|
+
action.newline = "!\n";
|
|
281
|
+
}
|
|
282
|
+
if (remoteIsWindows) {
|
|
283
|
+
this.remoteIsWindows = true;
|
|
284
|
+
this.protocolNewline = "!\n";
|
|
285
|
+
}
|
|
286
|
+
await this.sendString("ACT", JSON.stringify(action));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Receive action.
|
|
290
|
+
*/
|
|
291
|
+
async recvAction() {
|
|
292
|
+
const buf = await this.recvString("ACT", true);
|
|
293
|
+
const action = JSON.parse(buf);
|
|
294
|
+
const newline = action.newline;
|
|
295
|
+
if (typeof newline === "string" && newline.length > 0) {
|
|
296
|
+
this.protocolNewline = newline;
|
|
297
|
+
}
|
|
298
|
+
return action;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Send config.
|
|
302
|
+
* @param args - The config arguments.
|
|
303
|
+
* @param escapeChars - The escape characters.
|
|
304
|
+
* @param tmuxMode - The tmux mode.
|
|
305
|
+
* @param tmuxPaneWidth - The tmux pane width.
|
|
306
|
+
*/
|
|
307
|
+
async sendConfig(args, escapeChars, tmuxMode, tmuxPaneWidth) {
|
|
308
|
+
const config = { lang: "js" };
|
|
309
|
+
if (args.quiet === true) {
|
|
310
|
+
config.quiet = true;
|
|
311
|
+
}
|
|
312
|
+
if (args.binary === true) {
|
|
313
|
+
config.binary = true;
|
|
314
|
+
config.escape_chars = escapeChars;
|
|
315
|
+
}
|
|
316
|
+
if (args.directory === true) {
|
|
317
|
+
config.directory = true;
|
|
318
|
+
}
|
|
319
|
+
if (typeof args.bufsize === "number") {
|
|
320
|
+
config.bufsize = args.bufsize;
|
|
321
|
+
}
|
|
322
|
+
if (typeof args.timeout === "number") {
|
|
323
|
+
config.timeout = args.timeout;
|
|
324
|
+
}
|
|
325
|
+
if (args.overwrite === true) {
|
|
326
|
+
config.overwrite = true;
|
|
327
|
+
}
|
|
328
|
+
if (tmuxMode === TmuxMode.TmuxNormalMode) {
|
|
329
|
+
config.tmux_output_junk = true;
|
|
330
|
+
}
|
|
331
|
+
if (tmuxPaneWidth > 0) {
|
|
332
|
+
config.tmux_pane_width = tmuxPaneWidth;
|
|
333
|
+
}
|
|
334
|
+
let jsonStr = JSON.stringify(config);
|
|
335
|
+
jsonStr = jsonStr.replace(/[\u007F-\uFFFF]/g, function(chr) {
|
|
336
|
+
return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).slice(-4);
|
|
337
|
+
});
|
|
338
|
+
this.transferConfig = config;
|
|
339
|
+
await this.sendString("CFG", jsonStr);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Receive config.
|
|
343
|
+
*/
|
|
344
|
+
async recvConfig() {
|
|
345
|
+
const buf = await this.recvString("CFG", true);
|
|
346
|
+
this.transferConfig = JSON.parse(buf);
|
|
347
|
+
this.tmuxOutputJunk = this.transferConfig.tmux_output_junk === true;
|
|
348
|
+
return this.transferConfig;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Send client exit.
|
|
352
|
+
* @param msg - The message.
|
|
353
|
+
*/
|
|
354
|
+
async clientExit(msg) {
|
|
355
|
+
await this.sendString("EXIT", msg);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Receive exit.
|
|
359
|
+
*/
|
|
360
|
+
async recvExit() {
|
|
361
|
+
return await this.recvString("EXIT");
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Delete created files.
|
|
365
|
+
*/
|
|
366
|
+
async deleteCreatedFiles() {
|
|
367
|
+
const deletedFiles = [];
|
|
368
|
+
for (const file of this.createdFiles) {
|
|
369
|
+
const path = await file.deleteFile();
|
|
370
|
+
if (typeof path === "string" && path.length > 0) {
|
|
371
|
+
deletedFiles.push(path);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return deletedFiles;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Handle client error.
|
|
378
|
+
* @param err - The error.
|
|
379
|
+
*/
|
|
380
|
+
async clientError(err) {
|
|
381
|
+
await this.cleanInput(this.cleanTimeoutInMilliseconds);
|
|
382
|
+
const errMsg = TrzszError.getErrorMessage(err);
|
|
383
|
+
let trace = true;
|
|
384
|
+
if (err instanceof TrzszError) {
|
|
385
|
+
trace = err.isTraceBack();
|
|
386
|
+
if (err.isRemoteExit()) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (err.isRemoteFail()) {
|
|
390
|
+
if (trace) {
|
|
391
|
+
console.log(errMsg);
|
|
392
|
+
}
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
await this.sendString(trace ? "FAIL" : "fail", errMsg);
|
|
397
|
+
if (trace) {
|
|
398
|
+
console.log(errMsg);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Handle server error.
|
|
403
|
+
* @param err - The error.
|
|
404
|
+
*/
|
|
405
|
+
async serverError(err) {
|
|
406
|
+
await this.cleanInput(this.cleanTimeoutInMilliseconds);
|
|
407
|
+
const errMsg = TrzszError.getErrorMessage(err);
|
|
408
|
+
let trace = true;
|
|
409
|
+
if (err instanceof TrzszError) {
|
|
410
|
+
if (err.isStopAndDelete()) {
|
|
411
|
+
const deletedFiles = await this.deleteCreatedFiles();
|
|
412
|
+
if (deletedFiles.length > 0) {
|
|
413
|
+
await this.serverExit([err.message + ":"].concat(deletedFiles).join("\r\n- "));
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
trace = err.isTraceBack();
|
|
418
|
+
if (err.isRemoteExit() || err.isRemoteFail()) {
|
|
419
|
+
await this.serverExit(errMsg);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
await this.sendString(trace ? "FAIL" : "fail", errMsg);
|
|
424
|
+
await this.serverExit(errMsg);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Server exit - to be implemented by subclass or user.
|
|
428
|
+
* @param msg - The message.
|
|
429
|
+
*/
|
|
430
|
+
async serverExit(_msg) {
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Send file number.
|
|
434
|
+
* @param num - The number of files.
|
|
435
|
+
* @param progressCallback - The progress callback.
|
|
436
|
+
*/
|
|
437
|
+
async sendFileNum(num, progressCallback) {
|
|
438
|
+
await this.sendInteger("NUM", num);
|
|
439
|
+
await this.checkInteger(num);
|
|
440
|
+
if (progressCallback != null) {
|
|
441
|
+
progressCallback.onNum(num);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Send file name.
|
|
446
|
+
* @param file - The file reader.
|
|
447
|
+
* @param directory - Whether it's a directory.
|
|
448
|
+
* @param progressCallback - The progress callback.
|
|
449
|
+
*/
|
|
450
|
+
async sendFileName(file, directory, progressCallback) {
|
|
451
|
+
const relPath = file.getRelPath();
|
|
452
|
+
const fileName = relPath[relPath.length - 1];
|
|
453
|
+
if (directory) {
|
|
454
|
+
const jsonName = {
|
|
455
|
+
path_id: file.getPathId(),
|
|
456
|
+
path_name: relPath,
|
|
457
|
+
is_dir: file.isDir()
|
|
458
|
+
};
|
|
459
|
+
await this.sendString("NAME", JSON.stringify(jsonName));
|
|
460
|
+
} else {
|
|
461
|
+
await this.sendString("NAME", fileName);
|
|
462
|
+
}
|
|
463
|
+
const remoteName = await this.recvString("SUCC");
|
|
464
|
+
if (progressCallback != null) {
|
|
465
|
+
progressCallback.onName(fileName);
|
|
466
|
+
}
|
|
467
|
+
return remoteName;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Send file size.
|
|
471
|
+
* @param size - The file size.
|
|
472
|
+
* @param progressCallback - The progress callback.
|
|
473
|
+
*/
|
|
474
|
+
async sendFileSize(size, progressCallback) {
|
|
475
|
+
await this.sendInteger("SIZE", size);
|
|
476
|
+
await this.checkInteger(size);
|
|
477
|
+
if (progressCallback != null) {
|
|
478
|
+
progressCallback.onSize(size);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Send file data.
|
|
483
|
+
* @param file - The file reader.
|
|
484
|
+
* @param size - The file size.
|
|
485
|
+
* @param binary - Whether to use binary mode.
|
|
486
|
+
* @param escapeCodes - The escape codes.
|
|
487
|
+
* @param maxBufSize - The maximum buffer size.
|
|
488
|
+
* @param progressCallback - The progress callback.
|
|
489
|
+
*/
|
|
490
|
+
async sendFileData(file, size, binary, escapeCodes, maxBufSize, progressCallback) {
|
|
491
|
+
let step = 0;
|
|
492
|
+
if (progressCallback != null) {
|
|
493
|
+
progressCallback.onStep(step);
|
|
494
|
+
}
|
|
495
|
+
let bufSize = 1024;
|
|
496
|
+
let buffer = new ArrayBuffer(bufSize);
|
|
497
|
+
const md5 = new Md5();
|
|
498
|
+
while (step < size) {
|
|
499
|
+
const beginTime = Date.now();
|
|
500
|
+
const data = await file.readFile(buffer);
|
|
501
|
+
await this.sendData(data, binary, escapeCodes);
|
|
502
|
+
md5.appendByteArray(data);
|
|
503
|
+
await this.checkInteger(data.length);
|
|
504
|
+
step += data.length;
|
|
505
|
+
if (progressCallback != null) {
|
|
506
|
+
progressCallback.onStep(step);
|
|
507
|
+
}
|
|
508
|
+
const chunkTime = Date.now() - beginTime;
|
|
509
|
+
if (data.length === bufSize && chunkTime < 500 && bufSize < maxBufSize) {
|
|
510
|
+
bufSize = Math.min(bufSize * 2, maxBufSize);
|
|
511
|
+
buffer = new ArrayBuffer(bufSize);
|
|
512
|
+
} else if (chunkTime >= 2e3 && bufSize > 1024) {
|
|
513
|
+
bufSize = 1024;
|
|
514
|
+
buffer = new ArrayBuffer(bufSize);
|
|
515
|
+
}
|
|
516
|
+
if (chunkTime > this.maxChunkTimeInMilliseconds) {
|
|
517
|
+
this.maxChunkTimeInMilliseconds = chunkTime;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return new Uint8Array(md5.end(true).buffer);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Send file MD5.
|
|
524
|
+
* @param digest - The MD5 digest.
|
|
525
|
+
* @param progressCallback - The progress callback.
|
|
526
|
+
*/
|
|
527
|
+
async sendFileMD5(digest, progressCallback) {
|
|
528
|
+
await this.sendBinary("MD5", digest);
|
|
529
|
+
await this.checkBinary(digest);
|
|
530
|
+
if (progressCallback != null) {
|
|
531
|
+
progressCallback.onDone();
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Send files.
|
|
536
|
+
* @param files - The file readers.
|
|
537
|
+
* @param progressCallback - The progress callback.
|
|
538
|
+
*/
|
|
539
|
+
async sendFiles(files, progressCallback) {
|
|
540
|
+
this.openedFiles.push(...files);
|
|
541
|
+
const binary = this.transferConfig.binary === true;
|
|
542
|
+
const directory = this.transferConfig.directory === true;
|
|
543
|
+
const bufsizeVal = this.transferConfig.bufsize;
|
|
544
|
+
const maxBufSize = typeof bufsizeVal === "number" ? bufsizeVal : 10 * 1024 * 1024;
|
|
545
|
+
const escapeCodes = Array.isArray(this.transferConfig.escape_chars) ? escapeCharsToCodes(this.transferConfig.escape_chars) : [];
|
|
546
|
+
await this.sendFileNum(files.length, progressCallback);
|
|
547
|
+
const remoteNames = [];
|
|
548
|
+
for (const file of files) {
|
|
549
|
+
const remoteName = await this.sendFileName(file, directory, progressCallback);
|
|
550
|
+
if (!remoteNames.includes(remoteName)) {
|
|
551
|
+
remoteNames.push(remoteName);
|
|
552
|
+
}
|
|
553
|
+
if (file.isDir()) {
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
const size = file.getSize();
|
|
557
|
+
await this.sendFileSize(size, progressCallback);
|
|
558
|
+
const digest = await this.sendFileData(file, size, binary, escapeCodes, maxBufSize, progressCallback);
|
|
559
|
+
file.closeFile();
|
|
560
|
+
await this.sendFileMD5(digest, progressCallback);
|
|
561
|
+
}
|
|
562
|
+
return remoteNames;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Receive file number.
|
|
566
|
+
* @param progressCallback - The progress callback.
|
|
567
|
+
*/
|
|
568
|
+
async recvFileNum(progressCallback) {
|
|
569
|
+
const num = await this.recvInteger("NUM");
|
|
570
|
+
await this.sendInteger("SUCC", num);
|
|
571
|
+
if (progressCallback != null) {
|
|
572
|
+
progressCallback.onNum(num);
|
|
573
|
+
}
|
|
574
|
+
return num;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Receive file name.
|
|
578
|
+
* @param saveParam - The save parameter.
|
|
579
|
+
* @param openSaveFile - The open save file function.
|
|
580
|
+
* @param directory - Whether it's a directory.
|
|
581
|
+
* @param overwrite - Whether to overwrite.
|
|
582
|
+
* @param progressCallback - The progress callback.
|
|
583
|
+
*/
|
|
584
|
+
async recvFileName(saveParam, openSaveFile, directory, overwrite, progressCallback) {
|
|
585
|
+
const fileName = await this.recvString("NAME");
|
|
586
|
+
const file = await openSaveFile(saveParam, fileName, directory, overwrite);
|
|
587
|
+
this.createdFiles.push(file);
|
|
588
|
+
await this.sendString("SUCC", file.getLocalName());
|
|
589
|
+
if (progressCallback != null) {
|
|
590
|
+
progressCallback.onName(file.getFileName());
|
|
591
|
+
}
|
|
592
|
+
return file;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Receive file size.
|
|
596
|
+
* @param progressCallback - The progress callback.
|
|
597
|
+
*/
|
|
598
|
+
async recvFileSize(progressCallback) {
|
|
599
|
+
const fileSize = await this.recvInteger("SIZE");
|
|
600
|
+
await this.sendInteger("SUCC", fileSize);
|
|
601
|
+
if (progressCallback != null) {
|
|
602
|
+
progressCallback.onSize(fileSize);
|
|
603
|
+
}
|
|
604
|
+
return fileSize;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Receive file data.
|
|
608
|
+
* @param file - The file writer.
|
|
609
|
+
* @param size - The file size.
|
|
610
|
+
* @param binary - Whether to use binary mode.
|
|
611
|
+
* @param escapeCodes - The escape codes.
|
|
612
|
+
* @param timeoutInMilliseconds - The timeout in milliseconds.
|
|
613
|
+
* @param progressCallback - The progress callback.
|
|
614
|
+
*/
|
|
615
|
+
async recvFileData(file, size, binary, escapeCodes, timeoutInMilliseconds, progressCallback) {
|
|
616
|
+
let step = 0;
|
|
617
|
+
if (progressCallback != null) {
|
|
618
|
+
progressCallback.onStep(step);
|
|
619
|
+
}
|
|
620
|
+
const md5 = new Md5();
|
|
621
|
+
while (step < size) {
|
|
622
|
+
const beginTime = Date.now();
|
|
623
|
+
const data = await this.recvData(binary, escapeCodes, timeoutInMilliseconds);
|
|
624
|
+
await file.writeFile(data);
|
|
625
|
+
step += data.length;
|
|
626
|
+
if (progressCallback != null) {
|
|
627
|
+
progressCallback.onStep(step);
|
|
628
|
+
}
|
|
629
|
+
await this.sendInteger("SUCC", data.length);
|
|
630
|
+
md5.appendByteArray(data);
|
|
631
|
+
const chunkTime = Date.now() - beginTime;
|
|
632
|
+
if (chunkTime > this.maxChunkTimeInMilliseconds) {
|
|
633
|
+
this.maxChunkTimeInMilliseconds = chunkTime;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return new Uint8Array(md5.end(true).buffer);
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Receive file MD5.
|
|
640
|
+
* @param digest - The MD5 digest.
|
|
641
|
+
* @param progressCallback - The progress callback.
|
|
642
|
+
*/
|
|
643
|
+
async recvFileMD5(digest, progressCallback) {
|
|
644
|
+
const expectDigest = await this.recvBinary("MD5");
|
|
645
|
+
if (digest.length !== expectDigest.length) {
|
|
646
|
+
throw new TrzszError("Check MD5 failed");
|
|
647
|
+
}
|
|
648
|
+
for (let j = 0; j < digest.length; j++) {
|
|
649
|
+
if (digest[j] !== expectDigest[j]) {
|
|
650
|
+
throw new TrzszError("Check MD5 failed");
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
await this.sendBinary("SUCC", digest);
|
|
654
|
+
if (progressCallback != null) {
|
|
655
|
+
progressCallback.onDone();
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Receive files.
|
|
660
|
+
* @param saveParam - The save parameter.
|
|
661
|
+
* @param openSaveFile - The open save file function.
|
|
662
|
+
* @param progressCallback - The progress callback.
|
|
663
|
+
*/
|
|
664
|
+
async recvFiles(saveParam, openSaveFile, progressCallback) {
|
|
665
|
+
const binary = this.transferConfig.binary === true;
|
|
666
|
+
const directory = this.transferConfig.directory === true;
|
|
667
|
+
const overwrite = this.transferConfig.overwrite === true;
|
|
668
|
+
const timeoutVal = this.transferConfig.timeout;
|
|
669
|
+
const timeoutInMilliseconds = typeof timeoutVal === "number" ? timeoutVal * 1e3 : 1e5;
|
|
670
|
+
const escapeCodes = Array.isArray(this.transferConfig.escape_chars) ? escapeCharsToCodes(this.transferConfig.escape_chars) : [];
|
|
671
|
+
const num = await this.recvFileNum(progressCallback);
|
|
672
|
+
const localNames = [];
|
|
673
|
+
for (let i = 0; i < num; i++) {
|
|
674
|
+
const file = await this.recvFileName(
|
|
675
|
+
saveParam,
|
|
676
|
+
openSaveFile,
|
|
677
|
+
directory,
|
|
678
|
+
overwrite,
|
|
679
|
+
progressCallback
|
|
680
|
+
);
|
|
681
|
+
if (!localNames.includes(file.getLocalName())) {
|
|
682
|
+
localNames.push(file.getLocalName());
|
|
683
|
+
}
|
|
684
|
+
if (file.isDir()) {
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
this.openedFiles.push(file);
|
|
688
|
+
const size = await this.recvFileSize(progressCallback);
|
|
689
|
+
const digest = await this.recvFileData(
|
|
690
|
+
file,
|
|
691
|
+
size,
|
|
692
|
+
binary,
|
|
693
|
+
escapeCodes,
|
|
694
|
+
timeoutInMilliseconds,
|
|
695
|
+
progressCallback
|
|
696
|
+
);
|
|
697
|
+
file.closeFile();
|
|
698
|
+
await this.recvFileMD5(digest, progressCallback);
|
|
699
|
+
}
|
|
700
|
+
return localNames;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
export {
|
|
704
|
+
TrzszTransfer
|
|
705
|
+
};
|
|
706
|
+
//# sourceMappingURL=transfer.js.map
|