@social-mail/social-mail-web-server 1.4.800 → 1.4.802
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/dist/common/BufferStream.d.ts +9 -6
- package/dist/common/BufferStream.d.ts.map +1 -1
- package/dist/common/BufferStream.js +117 -13
- package/dist/common/BufferStream.js.map +1 -1
- package/dist/protocols/core/Queue.d.ts +4 -0
- package/dist/protocols/core/Queue.d.ts.map +1 -1
- package/dist/protocols/core/Queue.js +19 -0
- package/dist/protocols/core/Queue.js.map +1 -1
- package/dist/protocols/imap/client/ImapClient.js +1 -1
- package/dist/protocols/imap/client/ImapClient.js.map +1 -1
- package/dist/protocols/smtp/ESmtpClient.d.ts.map +1 -1
- package/dist/protocols/smtp/ESmtpClient.js +5 -4
- package/dist/protocols/smtp/ESmtpClient.js.map +1 -1
- package/dist/protocols/socket/client/SocketClient.d.ts +4 -7
- package/dist/protocols/socket/client/SocketClient.d.ts.map +1 -1
- package/dist/protocols/socket/client/SocketClient.js +15 -189
- package/dist/protocols/socket/client/SocketClient.js.map +1 -1
- package/dist/server/model/events/EmailLogEvents.d.ts.map +1 -1
- package/dist/server/model/events/EmailLogEvents.js.map +1 -1
- package/dist/server/seed/ui/seed-ui.js +1 -1
- package/dist/tests/core/buffer/buffer-stream.test.d.ts +2 -0
- package/dist/tests/core/buffer/buffer-stream.test.d.ts.map +1 -0
- package/dist/tests/core/buffer/buffer-stream.test.js +75 -0
- package/dist/tests/core/buffer/buffer-stream.test.js.map +1 -0
- package/dist/tests/protocols/parsers/email-parser.test.d.ts.map +1 -1
- package/dist/tests/protocols/parsers/email-parser.test.js +0 -32
- package/dist/tests/protocols/parsers/email-parser.test.js.map +1 -1
- package/dist/tests/services/smtp/auth.d.ts +1 -1
- package/dist/tests/services/smtp/auth.d.ts.map +1 -1
- package/dist/tests/services/smtp/auth.js +3 -3
- package/dist/tests/services/smtp/auth.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/BufferStream.ts +137 -10
- package/src/protocols/core/Queue.ts +23 -1
- package/src/protocols/imap/client/ImapClient.ts +1 -1
- package/src/protocols/smtp/ESmtpClient.ts +5 -4
- package/src/protocols/socket/client/SocketClient.ts +13 -130
- package/src/server/model/events/EmailLogEvents.ts +0 -4
- package/src/server/seed/ui/seed-ui.ts +1 -1
- package/src/tests/core/buffer/buffer-stream.test.ts +106 -0
- package/src/tests/protocols/parsers/email-parser.test.ts +0 -36
- package/src/tests/services/smtp/auth.ts +3 -3
package/package.json
CHANGED
|
@@ -1,18 +1,145 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Queue from "../protocols/core/Queue.js";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const empty = Buffer.alloc(0);
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
class BufferList {
|
|
6
|
+
|
|
7
|
+
get size() {
|
|
8
|
+
return this.length;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private list =[];
|
|
12
|
+
private length = 0;
|
|
13
|
+
|
|
14
|
+
add(buffer: Buffer) {
|
|
15
|
+
this.list.push(buffer);
|
|
16
|
+
this.length += buffer.length;
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
asBuffer() {
|
|
21
|
+
return Buffer.concat(this.list);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
toString(encoding: BufferEncoding) {
|
|
25
|
+
return this.asBuffer().toString(encoding);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
clear() {
|
|
29
|
+
this.length = 0;
|
|
30
|
+
this.list.length = 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class BufferStream {
|
|
36
|
+
|
|
37
|
+
store: Queue<Buffer> = new Queue();
|
|
38
|
+
start: number = 0;
|
|
39
|
+
unread: Buffer = empty;
|
|
40
|
+
|
|
41
|
+
get isDataAvailable() {
|
|
42
|
+
return !!this.store.peek;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
enqueue(buffer: Buffer) {
|
|
46
|
+
this.store.enqueue(buffer);
|
|
47
|
+
return this;
|
|
7
48
|
}
|
|
8
49
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
this.
|
|
50
|
+
async read(size: number, signal?: AbortSignal) {
|
|
51
|
+
|
|
52
|
+
if (size <= this.unread.length) {
|
|
53
|
+
// read this...
|
|
54
|
+
const slice = this.unread.subarray(0, size);
|
|
55
|
+
this.unread = size === this.unread.length
|
|
56
|
+
? empty
|
|
57
|
+
: this.unread.subarray(size);
|
|
58
|
+
return slice;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const pending = new BufferList();
|
|
62
|
+
pending.add(this.unread);
|
|
63
|
+
this.unread = empty;
|
|
64
|
+
|
|
65
|
+
size -= pending.size;
|
|
66
|
+
|
|
67
|
+
for(;;) {
|
|
68
|
+
|
|
69
|
+
if (signal?.aborted) {
|
|
70
|
+
throw new Error("aborted");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const peek = await this.store.waitForPeek(signal);
|
|
74
|
+
|
|
75
|
+
const bufLen = this.start + size;
|
|
76
|
+
|
|
77
|
+
if (bufLen <= peek.length) {
|
|
78
|
+
pending.add(peek.subarray(this.start, bufLen));
|
|
79
|
+
const buffer = pending.asBuffer();
|
|
80
|
+
this.start = bufLen;
|
|
81
|
+
size -= bufLen;
|
|
82
|
+
this.unread = empty;
|
|
83
|
+
pending.clear();
|
|
84
|
+
|
|
85
|
+
if (bufLen === peek.length) {
|
|
86
|
+
this.store.dequeue();
|
|
87
|
+
this.start = 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return buffer;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const remaining = this.start ? peek.subarray(this.start) : peek;
|
|
94
|
+
pending.add(remaining);
|
|
95
|
+
size -= remaining.length;
|
|
96
|
+
this.store.dequeue();
|
|
97
|
+
this.start = 0;
|
|
98
|
+
}
|
|
12
99
|
}
|
|
13
100
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
101
|
+
async readTill(sep : string | number | Buffer = 10, encoding: BufferEncoding = "utf8", trimR = false, signal?: AbortSignal) {
|
|
102
|
+
for(;;) {
|
|
103
|
+
const peek = await this.store.waitForPeek(signal);
|
|
104
|
+
|
|
105
|
+
if (signal?.aborted) {
|
|
106
|
+
throw new Error("aborted");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const index = peek.indexOf(sep, this.start);
|
|
110
|
+
if (index === -1) {
|
|
111
|
+
const remaining = this.start ? peek.subarray(this.start) : peek;
|
|
112
|
+
this.unread = Buffer.concat([ this.unread, remaining ]);
|
|
113
|
+
this.start = 0;
|
|
114
|
+
this.store.dequeue();
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (index === 0) {
|
|
119
|
+
// this is a special case...
|
|
120
|
+
const last = this.unread.length - 1;
|
|
121
|
+
if (trimR && this.unread.at(last) === 13) {
|
|
122
|
+
const t = this.unread.toString(encoding, 0, last);
|
|
123
|
+
this.unread = empty;
|
|
124
|
+
this.start = 1;
|
|
125
|
+
return t;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const subarray = peek.subarray(this.start, trimR && index && peek.at(index-1) === 13 ? index -1 : index);
|
|
130
|
+
const token = this.unread.length === 0
|
|
131
|
+
? subarray
|
|
132
|
+
: Buffer.concat([this.unread, subarray]);
|
|
133
|
+
// this is last...
|
|
134
|
+
if (index + 1 === peek.length) {
|
|
135
|
+
this.start = 0;
|
|
136
|
+
this.store.dequeue();
|
|
137
|
+
} else {
|
|
138
|
+
this.start = index + 1;
|
|
139
|
+
}
|
|
140
|
+
this.unread = empty;
|
|
141
|
+
return token.toString(encoding);
|
|
142
|
+
}
|
|
17
143
|
}
|
|
144
|
+
|
|
18
145
|
}
|
|
@@ -6,15 +6,37 @@ export default class Queue<T> {
|
|
|
6
6
|
|
|
7
7
|
public readonly eventItemAdded: EventSet<T> = new EventSet(this);
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
get peek() {
|
|
11
10
|
return this.store.get(this.head);
|
|
12
11
|
}
|
|
13
12
|
|
|
13
|
+
get isEmpty() {
|
|
14
|
+
return this.head === this.tail;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get signal() {
|
|
18
|
+
return this.abortController.signal;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private readonly abortController: AbortController = new AbortController();
|
|
22
|
+
|
|
14
23
|
private head = 0;
|
|
15
24
|
private tail = 0;
|
|
16
25
|
private readonly store: Map<number,T> = new Map();
|
|
17
26
|
|
|
27
|
+
public async waitForPeek(signal?: AbortSignal) {
|
|
28
|
+
if (this.isEmpty) {
|
|
29
|
+
await new Promise<any>((resolve, reject)=> {
|
|
30
|
+
this.eventItemAdded.listenOnce(resolve);
|
|
31
|
+
signal?.addEventListener("abort", () => {
|
|
32
|
+
this.abortController.abort("abort");
|
|
33
|
+
reject("abort");
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return this.peek;
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
public enqueue(item: T) {
|
|
19
41
|
this.store.set(this.tail,item);
|
|
20
42
|
this.tail++;
|
|
@@ -206,7 +206,7 @@ export default class ImapClient {
|
|
|
206
206
|
// read these many bytes...
|
|
207
207
|
|
|
208
208
|
// lastLine = lastLine.substring(0, index);
|
|
209
|
-
const literal = await this.socket.readChars(length);
|
|
209
|
+
const literal = (await this.socket.readChars(length)).toString("ascii");
|
|
210
210
|
// parser will take care of literal
|
|
211
211
|
// data.push(`${lastLine}\r\n${current}`);
|
|
212
212
|
// we will inject literal as an object with toString so
|
|
@@ -63,7 +63,7 @@ export default class ESmtpClient {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
const eSmtpClient = new ESmtpClient(client, logger);
|
|
66
|
-
await eSmtpClient.init(helo, greeting.toLocaleLowerCase().includes("esmtp"), host);
|
|
66
|
+
await eSmtpClient.init(helo, greeting.toLocaleLowerCase().includes("esmtp"), host, startTLS);
|
|
67
67
|
const login = auth?.login;
|
|
68
68
|
if (login) {
|
|
69
69
|
await eSmtpClient.sendCommand("AUTH LOGIN",334);
|
|
@@ -148,17 +148,17 @@ export default class ESmtpClient {
|
|
|
148
148
|
await this.sendCommand(".");
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
private async init(name: string, eSmtp = false, host?: string) {
|
|
151
|
+
private async init(name: string, eSmtp = false, host?: string, startTLS?: boolean) {
|
|
152
152
|
|
|
153
153
|
let startTLSSupported = false;
|
|
154
154
|
|
|
155
155
|
if (eSmtp) {
|
|
156
|
-
await this.sendCommand(`EHLO ${name ?? os.hostname() }`, 250, (t) => startTLSSupported ||= /-STARTTLS
|
|
156
|
+
await this.sendCommand(`EHLO ${name ?? os.hostname() }`, 250, (t) => startTLSSupported ||= /-STARTTLS/i.test(t) );
|
|
157
157
|
} else {
|
|
158
158
|
await this.sendCommand(`HELO ${name ?? os.hostname() }`);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
if (startTLSSupported) {
|
|
161
|
+
if (startTLSSupported && startTLS) {
|
|
162
162
|
await this.sendCommand("STARTTLS", 220);
|
|
163
163
|
await this.client.upgradeTLS( { servername: host, rejectUnauthorized: false });
|
|
164
164
|
await this.sendCommand(`EHLO ${name ?? os.hostname() }`);
|
|
@@ -175,6 +175,7 @@ export default class ESmtpClient {
|
|
|
175
175
|
const lines = [];
|
|
176
176
|
do {
|
|
177
177
|
const response = await this.client.readLine();
|
|
178
|
+
this.logger?.(response);
|
|
178
179
|
if (/^\d+\-/.test(response)) {
|
|
179
180
|
parse?.(response);
|
|
180
181
|
lines.push(response);
|
|
@@ -3,6 +3,7 @@ import Queue from "../../core/Queue.js";
|
|
|
3
3
|
import EventSet from "@entity-access/entity-access/dist/common/EventSet.js";
|
|
4
4
|
import tls, { TLSSocket } from "tls";
|
|
5
5
|
import { AsyncDisposableScope } from "@entity-access/entity-access/dist/common/usingAsync.js";
|
|
6
|
+
import { BufferStream } from "../../../common/BufferStream.js";
|
|
6
7
|
|
|
7
8
|
export default class SocketClient {
|
|
8
9
|
|
|
@@ -12,22 +13,17 @@ export default class SocketClient {
|
|
|
12
13
|
const c = ssl
|
|
13
14
|
? tls.connect({ port, host, rejectUnauthorized: false })
|
|
14
15
|
: host ? net.connect({ host, port }) : net.connect(port);
|
|
15
|
-
const asc = new AbortController();
|
|
16
16
|
c.once("error", (error) => {
|
|
17
17
|
logger?.(error);
|
|
18
18
|
reject(error);
|
|
19
19
|
});
|
|
20
|
-
|
|
21
|
-
logger?.(`Connection closed`);
|
|
22
|
-
asc.abort();
|
|
23
|
-
});
|
|
24
|
-
const q = new Queue<{ data: Buffer }>();
|
|
20
|
+
const bs = new BufferStream();
|
|
25
21
|
c.on("data", (data) => {
|
|
26
|
-
|
|
22
|
+
bs.enqueue(data);
|
|
27
23
|
});
|
|
28
24
|
c.on("connect", () => {
|
|
29
25
|
logger?.("Socket connected");
|
|
30
|
-
resolve(new SocketClient(c,
|
|
26
|
+
resolve(new SocketClient(c, bs , logger));
|
|
31
27
|
});
|
|
32
28
|
});
|
|
33
29
|
logger?.(`Connection established`);
|
|
@@ -39,7 +35,7 @@ export default class SocketClient {
|
|
|
39
35
|
eventClosed = new EventSet(this);
|
|
40
36
|
|
|
41
37
|
get isDataAvailable() {
|
|
42
|
-
return this.
|
|
38
|
+
return this.bufferStream.isDataAvailable;
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
/**
|
|
@@ -59,8 +55,8 @@ export default class SocketClient {
|
|
|
59
55
|
|
|
60
56
|
private constructor(
|
|
61
57
|
private client: net.Socket,
|
|
62
|
-
private queue: Queue<{ data: Buffer }>,
|
|
63
|
-
private
|
|
58
|
+
// private queue: Queue<{ data: Buffer }>,
|
|
59
|
+
private bufferStream: BufferStream,
|
|
64
60
|
private logger: (data) => void = () => void 0,
|
|
65
61
|
) {
|
|
66
62
|
this.client.setTimeout(30000);
|
|
@@ -111,121 +107,12 @@ export default class SocketClient {
|
|
|
111
107
|
});
|
|
112
108
|
}
|
|
113
109
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (this.signal.aborted) {
|
|
117
|
-
throw new Error("Aborted");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
await using scope = new AsyncDisposableScope();
|
|
121
|
-
return await new Promise<string>((resolve, reject) => {
|
|
122
|
-
|
|
123
|
-
let start = "";
|
|
124
|
-
|
|
125
|
-
this.signal.onabort = reject;
|
|
126
|
-
|
|
127
|
-
const processPeek = () => {
|
|
128
|
-
for(;;) {
|
|
129
|
-
if (this.signal.aborted) {
|
|
130
|
-
reject();
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
if (!this.queue.peek) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
const { data } = this.queue.peek;
|
|
137
|
-
if (data.length === 0) {
|
|
138
|
-
this.queue.dequeue();
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
// This is edge case if \r was the last character
|
|
142
|
-
// read and first character in buffer is \n (ASCII 10)
|
|
143
|
-
if (start.endsWith("\r") && data.at(0) === 10 ) {
|
|
144
|
-
start = start.substring(0, start.length-1);
|
|
145
|
-
if (data.length === 1) {
|
|
146
|
-
this.queue.dequeue();
|
|
147
|
-
} else {
|
|
148
|
-
this.queue.peek.data = data.subarray(1);
|
|
149
|
-
}
|
|
150
|
-
this.logger(`<< ${start}`);
|
|
151
|
-
resolve(start);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// search for `\r\n`
|
|
156
|
-
const index = data.indexOf('\r\n');
|
|
157
|
-
if (index === -1) {
|
|
158
|
-
|
|
159
|
-
start += data.toString("ascii");
|
|
160
|
-
this.queue.dequeue();
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// it is last
|
|
165
|
-
if (index === data.length - 2) {
|
|
166
|
-
this.queue.dequeue();
|
|
167
|
-
start += data.toString("ascii", 0, index);
|
|
168
|
-
this.logger(`<< ${start}`);
|
|
169
|
-
resolve(start);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
this.queue.peek.data = data.subarray(index + 2);
|
|
174
|
-
start += data.toString("ascii", 0, index);
|
|
175
|
-
this.logger(`<< ${start}`);
|
|
176
|
-
resolve(start);
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
scope.register(this.queue.eventItemAdded.listen(processPeek));
|
|
182
|
-
|
|
183
|
-
processPeek();
|
|
184
|
-
|
|
185
|
-
});
|
|
110
|
+
readLine(signal?: AbortSignal) {
|
|
111
|
+
return this.bufferStream.readTill("\n", "ascii", true, signal);
|
|
186
112
|
}
|
|
187
113
|
|
|
188
|
-
public async readChars(length: number) {
|
|
189
|
-
|
|
190
|
-
let text = "";
|
|
191
|
-
await using scope = new AsyncDisposableScope();
|
|
192
|
-
return await new Promise<string>((resolve, reject) => {
|
|
193
|
-
this.signal.onabort = reject;
|
|
194
|
-
const processPeek = () => {
|
|
195
|
-
for(;;) {
|
|
196
|
-
if (this.signal.aborted) {
|
|
197
|
-
reject(new Error("Aborted"));
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (!this.queue.peek) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
const { data } = this.queue.peek;
|
|
204
|
-
if (data.length === 0) {
|
|
205
|
-
this.queue.dequeue();
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (data.length > length) {
|
|
209
|
-
text += data.toString("ascii", 0, length);
|
|
210
|
-
this.queue.peek.data = data.subarray(length);
|
|
211
|
-
resolve(text);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
text += data.toString("ascii");
|
|
216
|
-
this.queue.dequeue();
|
|
217
|
-
length -= data.length;
|
|
218
|
-
if (length === 0) {
|
|
219
|
-
resolve(text);
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
scope.register(this.queue.eventItemAdded.listen(processPeek));
|
|
226
|
-
|
|
227
|
-
processPeek();
|
|
228
|
-
});
|
|
114
|
+
public async readChars(length: number, signal?: AbortSignal) {
|
|
115
|
+
return this.bufferStream.read(length, signal);
|
|
229
116
|
}
|
|
230
117
|
|
|
231
118
|
public async upgradeTLS(options: tls.ConnectionOptions = { }) {
|
|
@@ -250,12 +137,8 @@ export default class SocketClient {
|
|
|
250
137
|
ts.setTimeout(this.client.timeout);
|
|
251
138
|
this.client.setTimeout(0);
|
|
252
139
|
this.client = ts;
|
|
253
|
-
this.
|
|
254
|
-
this.client.on("data", (data) => this.
|
|
255
|
-
const ac = new AbortController();
|
|
256
|
-
this.client.on("close", () => ac.abort());
|
|
257
|
-
this.signal = ac.signal;
|
|
258
|
-
|
|
140
|
+
this.bufferStream = new BufferStream();
|
|
141
|
+
this.client.on("data", (data) => this.bufferStream.enqueue(data));
|
|
259
142
|
this.client.on("close", this.eventClosed.dispatcher);
|
|
260
143
|
this.client.on("timeout", this._onTimeout);
|
|
261
144
|
this.client.on("error", this.eventError.dispatcher);
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { IEntityQuery } from "@entity-access/entity-access/dist/model/IFilterWithParameter.js";
|
|
2
2
|
import BaseEvents from "./BaseEvents.js";
|
|
3
|
-
import ChangeEntry from "@entity-access/entity-access/dist/model/changes/ChangeEntry.js";
|
|
4
3
|
import Inject from "@entity-access/entity-access/dist/di/di.js";
|
|
5
4
|
import BaseStorage from "../../storage/BaseStorage.js";
|
|
6
|
-
import { Readable, Stream } from "stream";
|
|
7
|
-
import { BufferStream } from "../../../common/BufferStream.js";
|
|
8
|
-
import DateTime from "@entity-access/entity-access/dist/types/DateTime.js";
|
|
9
5
|
import TempFileService from "../../storage/TempFileService.js";
|
|
10
6
|
import { EmailLog } from "../entities/Email.js";
|
|
11
7
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { BufferStream } from "../../../common/BufferStream.js";
|
|
3
|
+
|
|
4
|
+
export default async function() {
|
|
5
|
+
|
|
6
|
+
await readSizes();
|
|
7
|
+
|
|
8
|
+
await readLines();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function readLines() {
|
|
12
|
+
const bf = new BufferStream();
|
|
13
|
+
|
|
14
|
+
setTimeout(() => bf.enqueue(Buffer.from("1234")), 10);
|
|
15
|
+
|
|
16
|
+
setTimeout(() => bf.enqueue(Buffer.from("5678")), 10);
|
|
17
|
+
setTimeout(() => bf.enqueue(Buffer.from("90\n12")), 10);
|
|
18
|
+
|
|
19
|
+
let line = await bf.readTill("\n");
|
|
20
|
+
assert.equal("1234567890", line);
|
|
21
|
+
|
|
22
|
+
setTimeout(() => bf.enqueue(Buffer.from("12\n34")), 10);
|
|
23
|
+
|
|
24
|
+
setTimeout(() => bf.enqueue(Buffer.from("5678")), 10);
|
|
25
|
+
setTimeout(() => bf.enqueue(Buffer.from("90\n12")), 10);
|
|
26
|
+
|
|
27
|
+
line = await bf.readTill("\n");
|
|
28
|
+
assert.equal("1212", line);
|
|
29
|
+
line = await bf.readTill("\n");
|
|
30
|
+
assert.equal("34567890", line);
|
|
31
|
+
|
|
32
|
+
line = (await bf.read(2)).toString("utf8");
|
|
33
|
+
assert.equal("12", line);
|
|
34
|
+
|
|
35
|
+
setTimeout(() => bf.enqueue(Buffer.from("12\n1234\n12345")), 10);
|
|
36
|
+
setTimeout(() => bf.enqueue(Buffer.from("6\n")), 10);
|
|
37
|
+
setTimeout(() => bf.enqueue(Buffer.from("123456\r\n")), 10);
|
|
38
|
+
|
|
39
|
+
line = await bf.readTill("\n");
|
|
40
|
+
assert.equal("12", line);
|
|
41
|
+
line = await bf.readTill("\n");
|
|
42
|
+
assert.equal("1234", line);
|
|
43
|
+
line = await bf.readTill("\n");
|
|
44
|
+
assert.equal("123456", line);
|
|
45
|
+
|
|
46
|
+
line = await bf.readTill("\n", "ascii", true);
|
|
47
|
+
assert.equal("123456", line);
|
|
48
|
+
setTimeout(() => bf.enqueue(Buffer.from("123456\r")), 10);
|
|
49
|
+
setTimeout(() => bf.enqueue(Buffer.from("\n123456\r\n")), 10);
|
|
50
|
+
line = await bf.readTill("\n", "ascii", true);
|
|
51
|
+
assert.equal("123456", line);
|
|
52
|
+
line = await bf.readTill("\n", "ascii", true);
|
|
53
|
+
assert.equal("123456", line);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function readSizes() {
|
|
57
|
+
const bf = new BufferStream();
|
|
58
|
+
|
|
59
|
+
setTimeout(() => bf.enqueue(Buffer.from("1234")), 10);
|
|
60
|
+
|
|
61
|
+
let b1 = await bf.read(1);
|
|
62
|
+
assert.equal("1", b1.toString("utf8"));
|
|
63
|
+
|
|
64
|
+
b1 = await bf.read(2);
|
|
65
|
+
assert.equal("23", b1.toString("utf8"));
|
|
66
|
+
|
|
67
|
+
b1 = await bf.read(1);
|
|
68
|
+
assert.equal("4", b1.toString("utf8"));
|
|
69
|
+
|
|
70
|
+
setTimeout(() => bf.enqueue(Buffer.from("1234")), 10);
|
|
71
|
+
|
|
72
|
+
b1 = await bf.read(1);
|
|
73
|
+
assert.equal("1", b1.toString("utf8"));
|
|
74
|
+
|
|
75
|
+
setTimeout(() => bf.enqueue(Buffer.from("1234")), 10);
|
|
76
|
+
setTimeout(() => bf.enqueue(Buffer.from("1234")), 10);
|
|
77
|
+
|
|
78
|
+
b1 = await bf.read(2);
|
|
79
|
+
assert.equal("23", b1.toString("utf8"));
|
|
80
|
+
|
|
81
|
+
b1 = await bf.read(2);
|
|
82
|
+
assert.equal("41", b1.toString("utf8"));
|
|
83
|
+
|
|
84
|
+
b1 = await bf.read(7);
|
|
85
|
+
assert.equal("2341234", b1.toString("utf8"));
|
|
86
|
+
|
|
87
|
+
setTimeout(() => bf.enqueue(Buffer.from("1234567890")), 10);
|
|
88
|
+
setTimeout(() => bf.enqueue(Buffer.from("1234567890")), 10);
|
|
89
|
+
|
|
90
|
+
b1 = await bf.read(1);
|
|
91
|
+
assert.equal("1", b1.toString("utf8"));
|
|
92
|
+
|
|
93
|
+
b1 = await bf.read(2);
|
|
94
|
+
assert.equal("23", b1.toString("utf8"));
|
|
95
|
+
|
|
96
|
+
b1 = await bf.read(1);
|
|
97
|
+
assert.equal("4", b1.toString("utf8"));
|
|
98
|
+
|
|
99
|
+
b1 = await bf.read(10);
|
|
100
|
+
assert.equal("5678901234", b1.toString("utf8"));
|
|
101
|
+
|
|
102
|
+
b1 = await bf.read(6);
|
|
103
|
+
assert.equal("567890", b1.toString("utf8"));
|
|
104
|
+
|
|
105
|
+
assert.equal(0, bf.start);
|
|
106
|
+
}
|
|
@@ -3,42 +3,6 @@ import { MimeNode, ReadableLineStream, LineStream } from "@social-mail/shared/in
|
|
|
3
3
|
import { LocalFile } from "@entity-access/server-pages/dist/core/LocalFile.js";
|
|
4
4
|
import { tmpName } from "tmp-promise";
|
|
5
5
|
|
|
6
|
-
class FileLineStream extends LineStream {
|
|
7
|
-
|
|
8
|
-
constructor(private file: LocalFile) {
|
|
9
|
-
super();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async *lines() {
|
|
13
|
-
let line = "";
|
|
14
|
-
const trimEndR = (t: string) => {
|
|
15
|
-
if (t.endsWith("\r")) {
|
|
16
|
-
return t.substring(0, t.length - 1);
|
|
17
|
-
}
|
|
18
|
-
return t;
|
|
19
|
-
};
|
|
20
|
-
for await(const buffer of this.file.readBuffers(4 * 1024 * 1024)) {
|
|
21
|
-
let start = 0;
|
|
22
|
-
do {
|
|
23
|
-
|
|
24
|
-
const index = buffer.indexOf("\n", start);
|
|
25
|
-
if (index === -1) {
|
|
26
|
-
line += buffer.toString("utf8", start);
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
yield trimEndR(line + buffer.toString("utf8", start, index));
|
|
31
|
-
start = index + 1;
|
|
32
|
-
line = "";
|
|
33
|
-
} while (true);
|
|
34
|
-
}
|
|
35
|
-
line = trimEndR(line);
|
|
36
|
-
if (line) {
|
|
37
|
-
yield line;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
6
|
export default async function() {
|
|
43
7
|
|
|
44
8
|
const tmp = await tmpName();
|
|
@@ -3,7 +3,7 @@ import ESmtpClient from "../../../protocols/smtp/ESmtpClient.js";
|
|
|
3
3
|
|
|
4
4
|
export default async function () {
|
|
5
5
|
|
|
6
|
-
return true;
|
|
6
|
+
// return true;
|
|
7
7
|
|
|
8
8
|
using c = await ESmtpClient.connect({
|
|
9
9
|
port: 25,
|
|
@@ -11,8 +11,8 @@ export default async function () {
|
|
|
11
11
|
helo: "localhost",
|
|
12
12
|
auth: {
|
|
13
13
|
login: {
|
|
14
|
-
username: "",
|
|
15
|
-
password: ""
|
|
14
|
+
username: "a",
|
|
15
|
+
password: "b"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
logger: console.log
|