@social-mail/shared 1.0.29 → 1.1.4
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/.gitlab-ci.yml +3 -3
- package/.vscode/settings.json +1 -1
- package/dist/.tsbuildinfo +1 -0
- package/dist/QueryIterator.d.ts +1 -9
- package/dist/QueryIterator.d.ts.map +1 -1
- package/dist/QueryIterator.js +3 -14
- package/dist/QueryIterator.js.map +1 -1
- package/dist/ical-parser/Calender.js +36 -92
- package/dist/ical-parser/Calender.js.map +1 -1
- package/dist/ical-parser/parseDate.js +1 -5
- package/dist/ical-parser/parseDate.js.map +1 -1
- package/dist/mime-parser/AttachmentFile.d.ts +5 -11
- package/dist/mime-parser/AttachmentFile.d.ts.map +1 -1
- package/dist/mime-parser/AttachmentFile.js +3 -8
- package/dist/mime-parser/AttachmentFile.js.map +1 -1
- package/dist/mime-parser/HeaderContactList.d.ts +15 -0
- package/dist/mime-parser/HeaderContactList.d.ts.map +1 -0
- package/dist/mime-parser/HeaderContactList.js +37 -0
- package/dist/mime-parser/HeaderContactList.js.map +1 -0
- package/dist/mime-parser/HeaderContentDisposition.d.ts +6 -6
- package/dist/mime-parser/HeaderContentDisposition.d.ts.map +1 -1
- package/dist/mime-parser/HeaderContentDisposition.js +5 -10
- package/dist/mime-parser/HeaderContentDisposition.js.map +1 -1
- package/dist/mime-parser/HeaderContentType.d.ts +6 -6
- package/dist/mime-parser/HeaderContentType.d.ts.map +1 -1
- package/dist/mime-parser/HeaderContentType.js +5 -10
- package/dist/mime-parser/HeaderContentType.js.map +1 -1
- package/dist/mime-parser/HeaderEmailAddress.d.ts +10 -0
- package/dist/mime-parser/HeaderEmailAddress.d.ts.map +1 -0
- package/dist/mime-parser/HeaderEmailAddress.js +27 -0
- package/dist/mime-parser/HeaderEmailAddress.js.map +1 -0
- package/dist/mime-parser/HeaderLinks.d.ts +9 -0
- package/dist/mime-parser/HeaderLinks.d.ts.map +1 -0
- package/dist/mime-parser/HeaderLinks.js +42 -0
- package/dist/mime-parser/HeaderLinks.js.map +1 -0
- package/dist/mime-parser/MimeEmailAddressParser.d.ts +13 -0
- package/dist/mime-parser/MimeEmailAddressParser.d.ts.map +1 -0
- package/dist/mime-parser/MimeEmailAddressParser.js +17 -0
- package/dist/mime-parser/MimeEmailAddressParser.js.map +1 -0
- package/dist/mime-parser/MimeHeaders.d.ts +19 -0
- package/dist/mime-parser/MimeHeaders.d.ts.map +1 -0
- package/dist/mime-parser/MimeHeaders.js +47 -0
- package/dist/mime-parser/MimeHeaders.js.map +1 -0
- package/dist/mime-parser/MimeMessage.d.ts +44 -11
- package/dist/mime-parser/MimeMessage.d.ts.map +1 -1
- package/dist/mime-parser/MimeMessage.js +81 -73
- package/dist/mime-parser/MimeMessage.js.map +1 -1
- package/dist/mime-parser/MimeNode.d.ts +32 -39
- package/dist/mime-parser/MimeNode.d.ts.map +1 -1
- package/dist/mime-parser/MimeNode.js +166 -298
- package/dist/mime-parser/MimeNode.js.map +1 -1
- package/dist/mime-parser/RegexGenerator.d.ts +9 -0
- package/dist/mime-parser/RegexGenerator.d.ts.map +1 -0
- package/dist/mime-parser/RegexGenerator.js +40 -0
- package/dist/mime-parser/RegexGenerator.js.map +1 -0
- package/dist/mime-parser/encoder/RawBuffer.d.ts +5 -5
- package/dist/mime-parser/encoder/RawBuffer.d.ts.map +1 -1
- package/dist/mime-parser/encoder/RawBuffer.js +7 -13
- package/dist/mime-parser/encoder/RawBuffer.js.map +1 -1
- package/dist/mime-parser/encoder/base64-to-blob.d.ts +1 -7
- package/dist/mime-parser/encoder/base64-to-blob.d.ts.map +1 -1
- package/dist/mime-parser/encoder/base64-to-blob.js +0 -6
- package/dist/mime-parser/encoder/base64-to-blob.js.map +1 -1
- package/dist/mime-parser/encoder/quoted-printable.d.ts +5 -8
- package/dist/mime-parser/encoder/quoted-printable.d.ts.map +1 -1
- package/dist/mime-parser/encoder/quoted-printable.js +3 -34
- package/dist/mime-parser/encoder/quoted-printable.js.map +1 -1
- package/dist/mime-parser/encoder/word-encoding.d.ts +4 -6
- package/dist/mime-parser/encoder/word-encoding.d.ts.map +1 -1
- package/dist/mime-parser/encoder/word-encoding.js +4 -5
- package/dist/mime-parser/encoder/word-encoding.js.map +1 -1
- package/dist/mime-parser/parsePairs.d.ts +1 -1
- package/dist/mime-parser/parsePairs.d.ts.map +1 -1
- package/dist/mime-parser/parsePairs.js +3 -7
- package/dist/mime-parser/parsePairs.js.map +1 -1
- package/dist/mime-parser/stream/LineStream.d.ts +10 -16
- package/dist/mime-parser/stream/LineStream.d.ts.map +1 -1
- package/dist/mime-parser/stream/LineStream.js +59 -94
- package/dist/mime-parser/stream/LineStream.js.map +1 -1
- package/dist/mime-parser/stream/TextWriter.d.ts +5 -3
- package/dist/mime-parser/stream/TextWriter.d.ts.map +1 -1
- package/dist/mime-parser/stream/TextWriter.js +15 -3
- package/dist/mime-parser/stream/TextWriter.js.map +1 -1
- package/dist/mime-parser/tokenizer.d.ts +3 -3
- package/dist/mime-parser/tokenizer.d.ts.map +1 -1
- package/dist/mime-parser/tokenizer.js +10 -17
- package/dist/mime-parser/tokenizer.js.map +1 -1
- package/package.json +3 -3
- package/src/{QueryIterator.js → QueryIterator.ts} +11 -12
- package/src/ical-parser/Calender.ts +1 -1
- package/src/mime-parser/AttachmentFile.ts +24 -0
- package/src/mime-parser/HeaderContactList.ts +41 -0
- package/src/mime-parser/HeaderContentDisposition.ts +21 -0
- package/src/mime-parser/HeaderContentType.ts +22 -0
- package/src/mime-parser/HeaderEmailAddress.ts +38 -0
- package/src/mime-parser/HeaderLinks.ts +48 -0
- package/src/mime-parser/MimeEmailAddressParser.ts +22 -0
- package/src/mime-parser/MimeHeaders.ts +62 -0
- package/src/mime-parser/MimeMessage.ts +134 -0
- package/src/mime-parser/{MimeNode.js → MimeNode.ts} +92 -159
- package/src/mime-parser/RegexGenerator.ts +42 -0
- package/src/mime-parser/encoder/{RawBuffer.js → RawBuffer.ts} +15 -17
- package/src/mime-parser/encoder/{base64-to-blob.js → base64-to-blob.ts} +8 -8
- package/src/mime-parser/encoder/{quoted-printable.js → quoted-printable.ts} +9 -11
- package/src/mime-parser/encoder/{word-encoding.js → word-encoding.ts} +6 -6
- package/src/mime-parser/{parsePairs.js → parsePairs.ts} +5 -4
- package/src/mime-parser/stream/{LineStream.js → LineStream.ts} +12 -19
- package/src/mime-parser/stream/TextWriter.ts +36 -0
- package/src/mime-parser/{tokenizer.js → tokenizer.ts} +24 -21
- package/tsconfig.json +30 -9
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/mime-parser/AttachmentFile.js +0 -29
- package/src/mime-parser/HeaderContentDisposition.js +0 -32
- package/src/mime-parser/HeaderContentType.js +0 -34
- package/src/mime-parser/MimeMessage.js +0 -105
- package/src/mime-parser/stream/TextWriter.js +0 -27
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
|
|
2
1
|
// import QueryIterator from "../../common/QueryIterator";
|
|
3
2
|
import { AttachmentFile } from "./AttachmentFile.js";
|
|
4
3
|
import { HeaderContentType } from "./HeaderContentType.js";
|
|
@@ -9,32 +8,15 @@ import { quotedPrintable } from "./encoder/quoted-printable.js";
|
|
|
9
8
|
import { wordEncoding } from "./encoder/word-encoding.js";
|
|
10
9
|
import LineStream from "./stream/LineStream.js";
|
|
11
10
|
import TextWriter from "./stream/TextWriter.js";
|
|
11
|
+
import MimeHeaders from "./MimeHeaders.js";
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// boundary?: string;
|
|
16
|
-
// charset?: string
|
|
17
|
-
// };
|
|
13
|
+
export class CIMap extends Map<string, any> {
|
|
14
|
+
private ci = new Map<string, string>();
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// export interface IContentDispositionObject {
|
|
22
|
-
// type: string;
|
|
23
|
-
// filename?: string;
|
|
24
|
-
// };
|
|
25
|
-
|
|
26
|
-
// export type IContentDisposition = string | IContentDispositionObject;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export class CIMap extends Map {
|
|
31
|
-
|
|
32
|
-
ci = new Map();
|
|
33
|
-
|
|
34
|
-
set(key, value) {
|
|
16
|
+
set(key: string, value: any): this {
|
|
35
17
|
if (typeof key === "string") {
|
|
36
18
|
let ck = this.ci.get(key);
|
|
37
|
-
if (ck ===
|
|
19
|
+
if (ck === undefined) {
|
|
38
20
|
ck = key.toLocaleLowerCase();
|
|
39
21
|
this.ci.set(ck, key);
|
|
40
22
|
} else {
|
|
@@ -44,26 +26,29 @@ export class CIMap extends Map {
|
|
|
44
26
|
return super.set(key, value);
|
|
45
27
|
}
|
|
46
28
|
|
|
47
|
-
get(key) {
|
|
29
|
+
get(key: string): any {
|
|
48
30
|
if (typeof key === "string") {
|
|
49
31
|
const ck = key.toLocaleLowerCase();
|
|
50
|
-
key = this.ci.get(ck);
|
|
32
|
+
key = this.ci.get(ck) || key;
|
|
51
33
|
}
|
|
52
34
|
return super.get(key);
|
|
53
35
|
}
|
|
54
|
-
|
|
55
36
|
}
|
|
56
37
|
|
|
57
|
-
|
|
58
38
|
export class MimeNode {
|
|
59
|
-
|
|
60
39
|
static createMessage({
|
|
61
40
|
html,
|
|
62
41
|
subject,
|
|
63
42
|
text = "",
|
|
64
43
|
name = "",
|
|
65
44
|
attachments = []
|
|
66
|
-
}
|
|
45
|
+
}: {
|
|
46
|
+
html?: string;
|
|
47
|
+
subject: string;
|
|
48
|
+
text?: string;
|
|
49
|
+
name?: string;
|
|
50
|
+
attachments?: any[];
|
|
51
|
+
}): MimeNode {
|
|
67
52
|
const root = new MimeNode("multipart/mixed");
|
|
68
53
|
root.setHeader("Subject", subject);
|
|
69
54
|
if (name) {
|
|
@@ -87,11 +72,12 @@ export class MimeNode {
|
|
|
87
72
|
}
|
|
88
73
|
|
|
89
74
|
static create(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
75
|
+
text: string | File,
|
|
76
|
+
type?: string,
|
|
77
|
+
id?: string
|
|
78
|
+
): MimeNode {
|
|
93
79
|
if (typeof text === "string") {
|
|
94
|
-
const node = new MimeNode(type);
|
|
80
|
+
const node = new MimeNode(type || "");
|
|
95
81
|
node.text = text;
|
|
96
82
|
return node;
|
|
97
83
|
}
|
|
@@ -106,23 +92,36 @@ export class MimeNode {
|
|
|
106
92
|
return fileNode;
|
|
107
93
|
}
|
|
108
94
|
|
|
109
|
-
|
|
95
|
+
encoded?: string;
|
|
96
|
+
children?: MimeNode[];
|
|
97
|
+
get contentType() {
|
|
98
|
+
return this.headers.first("content-type").as(HeaderContentType);
|
|
99
|
+
}
|
|
100
|
+
get contentDisposition() {
|
|
101
|
+
return this.headers.first("content-disposition")?.as(HeaderContentDisposition);
|
|
102
|
+
}
|
|
110
103
|
|
|
111
|
-
|
|
104
|
+
set contentDisposition(v: HeaderContentDisposition) {
|
|
105
|
+
this.headers.set("content-disposition", v.toString());
|
|
106
|
+
}
|
|
112
107
|
|
|
113
|
-
|
|
108
|
+
textContent?: string;
|
|
109
|
+
blobData?: Blob;
|
|
110
|
+
headers: MimeHeaders;
|
|
114
111
|
|
|
115
|
-
|
|
112
|
+
constructor(type?: string) {
|
|
113
|
+
this.headers = new MimeHeaders();
|
|
114
|
+
this.headers.append("Content-Type", type);
|
|
115
|
+
}
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
get text() {
|
|
117
|
+
get text(): string {
|
|
119
118
|
let tc = this.textContent;
|
|
120
119
|
if (tc) {
|
|
121
120
|
return tc;
|
|
122
121
|
}
|
|
123
122
|
let { encoded } = this;
|
|
124
123
|
if (!encoded) {
|
|
125
|
-
return;
|
|
124
|
+
return "";
|
|
126
125
|
}
|
|
127
126
|
// we need to convert byte array to utf8
|
|
128
127
|
const cte = this.contentTransferEncoding?.toLowerCase();
|
|
@@ -145,118 +144,69 @@ export class MimeNode {
|
|
|
145
144
|
return tc;
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
set text(
|
|
147
|
+
set text(v: string) {
|
|
149
148
|
this.contentTransferEncoding = "quoted-printable";
|
|
150
149
|
this.textContent = v;
|
|
151
150
|
}
|
|
152
151
|
|
|
153
|
-
|
|
154
|
-
get blob() {
|
|
152
|
+
get blob(): Blob {
|
|
155
153
|
// this will create blob..
|
|
156
154
|
if (this.contentTransferEncoding?.toLocaleLowerCase() !== "base64") {
|
|
157
155
|
throw new Error("Not supported");
|
|
158
156
|
}
|
|
159
|
-
return this.blobData ??= base64toBlob(this.encoded
|
|
157
|
+
return this.blobData ??= base64toBlob(this.encoded!, this.contentType.type);
|
|
160
158
|
}
|
|
161
159
|
|
|
162
|
-
set blob(
|
|
160
|
+
set blob(v: Blob) {
|
|
163
161
|
this.blobData = v;
|
|
164
162
|
}
|
|
165
163
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return this.header("Content-Transfer-Encoding");
|
|
164
|
+
get contentTransferEncoding(): string | undefined {
|
|
165
|
+
return this.firstHeaderString("Content-Transfer-Encoding");
|
|
169
166
|
}
|
|
170
167
|
|
|
171
|
-
set contentTransferEncoding(
|
|
172
|
-
this.
|
|
168
|
+
set contentTransferEncoding(v: string | undefined) {
|
|
169
|
+
this.headers.set("Content-Transfer-Encoding", v);
|
|
173
170
|
}
|
|
174
171
|
|
|
175
|
-
|
|
176
|
-
get boundary() {
|
|
172
|
+
get boundary(): string | undefined {
|
|
177
173
|
return this.contentType.boundary;
|
|
178
174
|
}
|
|
179
175
|
|
|
180
|
-
|
|
181
|
-
get descendent() {
|
|
176
|
+
get descendent(): IterableIterator<MimeNode> {
|
|
182
177
|
return this.enumerate();
|
|
183
178
|
}
|
|
184
179
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
/** @type {Map<string, string | any>} */ headers;
|
|
189
|
-
|
|
190
|
-
constructor( /** @type {string} */ type) {
|
|
191
|
-
this.headers = new CIMap();
|
|
192
|
-
this.contentType = new HeaderContentType(type);
|
|
193
|
-
this.headers.set("Content-Type", this.contentType);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
getFirstChild(
|
|
197
|
-
/** @type {string} */ contentType,
|
|
198
|
-
create = false
|
|
199
|
-
) {
|
|
200
|
-
contentType = contentType.toLocaleLowerCase();
|
|
201
|
-
let child = QueryIterator.first(this.descendent, (x) => x.contentType.type === contentType);
|
|
202
|
-
if (!child && create) {
|
|
203
|
-
child = new MimeNode(contentType);
|
|
204
|
-
(this.children ??= []).push(child);
|
|
205
|
-
}
|
|
206
|
-
return child;
|
|
180
|
+
firstHeaderString(name: string): any {
|
|
181
|
+
return this.headers.first(name)?.value;
|
|
207
182
|
}
|
|
208
183
|
|
|
209
|
-
|
|
210
|
-
return this.headers.
|
|
184
|
+
firstHeaderObject(name: string) {
|
|
185
|
+
return this.headers.first(name);
|
|
211
186
|
}
|
|
212
187
|
|
|
213
|
-
setHeader(
|
|
214
|
-
/** @type {string} */name,
|
|
215
|
-
/** @type {string} */ value
|
|
216
|
-
) {
|
|
217
|
-
// name = name.toLocaleLowerCase();
|
|
218
|
-
// for (const [key] of this.headers.keys()) {
|
|
219
|
-
// if (key.toLocaleLowerCase() === name) {
|
|
220
|
-
// this.headers.set(key, value);
|
|
221
|
-
// return;
|
|
222
|
-
// }
|
|
223
|
-
// }
|
|
224
|
-
|
|
225
|
-
// // change name...
|
|
226
|
-
// switch(name.toLocaleLowerCase()) {
|
|
227
|
-
// case "content-type":
|
|
228
|
-
// name = "Content-Type";
|
|
229
|
-
// break;
|
|
230
|
-
// case "content-transfer-encoding":
|
|
231
|
-
// name = "Content-Transfer-Encoding";
|
|
232
|
-
// break;
|
|
233
|
-
// case "content-disposition":
|
|
234
|
-
// name = "Content-Disposition";
|
|
235
|
-
// break;
|
|
236
|
-
// }
|
|
237
|
-
|
|
188
|
+
setHeader(name: string, value: any): void {
|
|
238
189
|
this.headers.set(name, value);
|
|
239
190
|
}
|
|
240
191
|
|
|
241
|
-
async asFile() {
|
|
242
|
-
const text = this.encoded
|
|
192
|
+
async asFile(): Promise<AttachmentFile> {
|
|
193
|
+
const text = this.encoded!.split("\n").map((x) => x.trim()).join("");
|
|
243
194
|
const url = `data:${this.contentType.type};base64,${text}`;
|
|
244
195
|
const blob = await fetch(url);
|
|
245
196
|
const af = new AttachmentFile(
|
|
246
197
|
[await blob.blob()],
|
|
247
|
-
this.contentDisposition
|
|
198
|
+
this.contentDisposition!.filename!,
|
|
248
199
|
{
|
|
249
200
|
type: this.contentType.type,
|
|
250
|
-
contentId: this.
|
|
251
|
-
disposition: this.contentDisposition
|
|
201
|
+
contentId: this.firstHeaderString("Content-Id"),
|
|
202
|
+
disposition: this.contentDisposition!.type as any
|
|
252
203
|
});
|
|
253
204
|
af.node = this;
|
|
254
205
|
return af;
|
|
255
206
|
}
|
|
256
207
|
|
|
257
|
-
async attachments(
|
|
258
|
-
|
|
259
|
-
for (const iterator of this.children) {
|
|
208
|
+
async attachments(files: AttachmentFile[] = []): Promise<AttachmentFile[]> {
|
|
209
|
+
for (const iterator of this.children || []) {
|
|
260
210
|
if (iterator.children) {
|
|
261
211
|
await iterator.attachments(files);
|
|
262
212
|
continue;
|
|
@@ -268,11 +218,7 @@ export class MimeNode {
|
|
|
268
218
|
return files;
|
|
269
219
|
}
|
|
270
220
|
|
|
271
|
-
async parse(
|
|
272
|
-
/** @type {LineStream} */ lines,
|
|
273
|
-
last = ""
|
|
274
|
-
) {
|
|
275
|
-
|
|
221
|
+
async parse(lines: LineStream, last = ""): Promise<boolean> {
|
|
276
222
|
await this.parseHeaders(lines);
|
|
277
223
|
|
|
278
224
|
const end = last + "--";
|
|
@@ -319,15 +265,12 @@ export class MimeNode {
|
|
|
319
265
|
this.children.push(child);
|
|
320
266
|
|
|
321
267
|
if (isLast) {
|
|
322
|
-
return;
|
|
268
|
+
return true;
|
|
323
269
|
}
|
|
324
270
|
}
|
|
325
271
|
}
|
|
326
272
|
|
|
327
|
-
async parseHeaders(
|
|
328
|
-
/** @type {LineStream} */ lines
|
|
329
|
-
) {
|
|
330
|
-
|
|
273
|
+
async parseHeaders(lines: LineStream): Promise<void> {
|
|
331
274
|
let headerName = "";
|
|
332
275
|
let headerValue = "";
|
|
333
276
|
|
|
@@ -353,21 +296,9 @@ export class MimeNode {
|
|
|
353
296
|
} else {
|
|
354
297
|
return;
|
|
355
298
|
}
|
|
356
|
-
|
|
357
|
-
const ct = this.header("Content-Type");
|
|
358
|
-
this.contentType.parse(ct);
|
|
359
|
-
this.setHeader("Content-Type", ct);
|
|
360
|
-
|
|
361
|
-
const cd = this.header("Content-Disposition");
|
|
362
|
-
if (cd) {
|
|
363
|
-
this.contentDisposition ??= new HeaderContentDisposition();
|
|
364
|
-
this.contentDisposition.parse(cd);
|
|
365
|
-
this.setHeader("Content-Disposition", cd);
|
|
366
|
-
}
|
|
367
299
|
}
|
|
368
300
|
|
|
369
|
-
async save(
|
|
370
|
-
|
|
301
|
+
async save(writer: TextWriter): Promise<void> {
|
|
371
302
|
if (this.textContent) {
|
|
372
303
|
this.contentType.charset = "UTF-8";
|
|
373
304
|
}
|
|
@@ -376,26 +307,14 @@ export class MimeNode {
|
|
|
376
307
|
this.contentType.boundary ||= `${Date.now()}-${Date.now()}-${Date.now()}`;
|
|
377
308
|
}
|
|
378
309
|
|
|
379
|
-
for (const
|
|
380
|
-
|
|
381
|
-
case "content-disposition":
|
|
382
|
-
writer.writeLine(`Content-Disposition: ${this.contentDisposition}`);
|
|
383
|
-
continue;
|
|
384
|
-
case "content-type":
|
|
385
|
-
writer.writeLine(`Content-Type:${this.contentType.toString()}`);
|
|
386
|
-
continue;
|
|
387
|
-
case "subject":
|
|
388
|
-
writer.writeLine(`${key}: ${wordEncoding.encode(element, true)}`);
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
writer.writeLine(`${key}: ${element}`);
|
|
310
|
+
for (const h of this.headers.entries()) {
|
|
311
|
+
writer.writeLineMax80Chars(`${h.key}: ${wordEncoding.encode(h.value, true)}`);
|
|
392
312
|
}
|
|
393
313
|
|
|
394
314
|
writer.writeLine("");
|
|
395
315
|
|
|
396
316
|
// check if we have any children...
|
|
397
317
|
if (this.children?.length) {
|
|
398
|
-
|
|
399
318
|
const boundary = `--${this.contentType.boundary}`;
|
|
400
319
|
|
|
401
320
|
for (const iterator of this.children) {
|
|
@@ -404,8 +323,6 @@ export class MimeNode {
|
|
|
404
323
|
}
|
|
405
324
|
|
|
406
325
|
writer.writeLine(boundary + "--");
|
|
407
|
-
|
|
408
|
-
|
|
409
326
|
return;
|
|
410
327
|
}
|
|
411
328
|
|
|
@@ -416,7 +333,7 @@ export class MimeNode {
|
|
|
416
333
|
let start = 0;
|
|
417
334
|
const max = 80;
|
|
418
335
|
for (;;) {
|
|
419
|
-
if (
|
|
336
|
+
if (start + max > base64.length) {
|
|
420
337
|
writer.writeLine(base64.substring(start));
|
|
421
338
|
break;
|
|
422
339
|
}
|
|
@@ -429,23 +346,20 @@ export class MimeNode {
|
|
|
429
346
|
let text = this.text;
|
|
430
347
|
switch(this.contentTransferEncoding) {
|
|
431
348
|
case "quoted-printable":
|
|
432
|
-
text = quotedPrintable.encode(
|
|
349
|
+
text = quotedPrintable.encode(RawBuffer.encode(text));
|
|
433
350
|
writer.writeLine(text);
|
|
434
351
|
break;
|
|
435
352
|
case "base64":
|
|
436
|
-
text = btoa(
|
|
437
|
-
writer.
|
|
353
|
+
text = btoa(RawBuffer.encode(text, this.contentType.charset));
|
|
354
|
+
writer.writeLineMax80Chars(text);
|
|
438
355
|
break;
|
|
439
356
|
default:
|
|
440
|
-
writer.
|
|
357
|
+
writer.writeLineMax80Chars(text);
|
|
441
358
|
break;
|
|
442
359
|
}
|
|
443
|
-
// writer.writeLine("");
|
|
444
|
-
return;
|
|
445
360
|
}
|
|
446
361
|
|
|
447
|
-
|
|
448
|
-
*enumerate() {
|
|
362
|
+
*enumerate(): IterableIterator<MimeNode> {
|
|
449
363
|
yield this;
|
|
450
364
|
if (!this.children) {
|
|
451
365
|
return;
|
|
@@ -454,4 +368,23 @@ export class MimeNode {
|
|
|
454
368
|
yield* iterator.enumerate();
|
|
455
369
|
}
|
|
456
370
|
}
|
|
371
|
+
|
|
372
|
+
getFirstChild(contentType: string, create = false): MimeNode | undefined {
|
|
373
|
+
contentType = contentType.toLocaleLowerCase();
|
|
374
|
+
// const child = QueryIterator.first(this.descendent, (x) => x.contentType.type === contentType);
|
|
375
|
+
// The QueryIterator.first is not available, so we'll implement it manually:
|
|
376
|
+
let child: MimeNode | undefined;
|
|
377
|
+
for (const node of this.descendent) {
|
|
378
|
+
if (node.contentType.type === contentType) {
|
|
379
|
+
child = node;
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (!child && create) {
|
|
385
|
+
child = new MimeNode(contentType);
|
|
386
|
+
(this.children ??= []).push(child);
|
|
387
|
+
}
|
|
388
|
+
return child;
|
|
389
|
+
}
|
|
457
390
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export default class RegexGenerator{
|
|
2
|
+
|
|
3
|
+
constructor(public readonly regex: RegExp) {
|
|
4
|
+
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
*groups(text: string) {
|
|
8
|
+
if (!text) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const { regex } = this;
|
|
12
|
+
regex.lastIndex = 0;
|
|
13
|
+
let m;
|
|
14
|
+
while((m = regex.exec(text)) !== null) {
|
|
15
|
+
if (m.index === regex.lastIndex) {
|
|
16
|
+
regex.lastIndex++;
|
|
17
|
+
}
|
|
18
|
+
if (m?.length) {
|
|
19
|
+
yield m as RegExpExecArray;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
*all(text: string) {
|
|
25
|
+
if (!text) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const { regex } = this;
|
|
29
|
+
regex.lastIndex = 0;
|
|
30
|
+
let m;
|
|
31
|
+
while((m = regex.exec(text)) !== null) {
|
|
32
|
+
if (m.index === regex.lastIndex) {
|
|
33
|
+
regex.lastIndex++;
|
|
34
|
+
}
|
|
35
|
+
if (m?.length) {
|
|
36
|
+
yield { matched: m as RegExpExecArray };
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
};
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
const toBuffer = (
|
|
2
|
-
/** @type {string} */ text
|
|
3
|
-
) => {
|
|
1
|
+
const toBuffer = (text: string): ArrayBuffer => {
|
|
4
2
|
const bytes = new Uint8Array(text.length);
|
|
5
3
|
for (let index = 0; index < text.length; index++) {
|
|
6
4
|
bytes[index] = text.charCodeAt(index);
|
|
@@ -9,10 +7,10 @@ const toBuffer = (
|
|
|
9
7
|
};
|
|
10
8
|
|
|
11
9
|
export const RawBuffer = {
|
|
12
|
-
decode
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
10
|
+
decode(
|
|
11
|
+
bytesAsString: string,
|
|
12
|
+
encoding: string = "utf-8"
|
|
13
|
+
): string {
|
|
16
14
|
if (!encoding) {
|
|
17
15
|
return bytesAsString;
|
|
18
16
|
}
|
|
@@ -20,16 +18,16 @@ export const RawBuffer = {
|
|
|
20
18
|
return te.decode(toBuffer(bytesAsString));
|
|
21
19
|
},
|
|
22
20
|
|
|
23
|
-
encode
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
27
|
-
if (
|
|
21
|
+
encode(
|
|
22
|
+
text: string,
|
|
23
|
+
encoding: string = "utf-8"
|
|
24
|
+
): string {
|
|
25
|
+
if (!/utf\-?8/i.test(encoding)) {
|
|
28
26
|
throw new Error(`Encoding ${encoding} not supported`);
|
|
29
27
|
}
|
|
30
28
|
const te = new TextEncoder();
|
|
31
29
|
const array = te.encode(text);
|
|
32
|
-
const a = [];
|
|
30
|
+
const a: string[] = [];
|
|
33
31
|
for (const iterator of array) {
|
|
34
32
|
a.push(String.fromCharCode(iterator));
|
|
35
33
|
}
|
|
@@ -37,16 +35,16 @@ export const RawBuffer = {
|
|
|
37
35
|
},
|
|
38
36
|
|
|
39
37
|
toBase64Async(
|
|
40
|
-
|
|
41
|
-
asDataUrl = false
|
|
42
|
-
) {
|
|
38
|
+
blob: Blob,
|
|
39
|
+
asDataUrl: boolean = false
|
|
40
|
+
): Promise<string> {
|
|
43
41
|
return new Promise<string>((resolve, reject) => {
|
|
44
42
|
const reader = new FileReader();
|
|
45
43
|
reader.onerror = () => {
|
|
46
44
|
reject(reader.error);
|
|
47
45
|
};
|
|
48
46
|
reader.onload = () => {
|
|
49
|
-
|
|
47
|
+
const text: string = reader.result as string;
|
|
50
48
|
if (asDataUrl) {
|
|
51
49
|
resolve(text);
|
|
52
50
|
return;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param {string} base64Data
|
|
4
|
-
* @param {string} contentType
|
|
5
|
-
* @returns
|
|
2
|
+
* Converts a base64 string to a Blob
|
|
3
|
+
* @param {string} base64Data - The base64 encoded data
|
|
4
|
+
* @param {string} contentType - The content type of the blob
|
|
5
|
+
* @returns {Blob} - The resulting Blob object
|
|
6
6
|
*/
|
|
7
|
-
export function base64toBlob(base64Data, contentType) {
|
|
7
|
+
export function base64toBlob(base64Data: string, contentType: string): Blob {
|
|
8
8
|
contentType = contentType || '';
|
|
9
9
|
const sliceSize = 1024;
|
|
10
10
|
const byteCharacters = atob(base64Data);
|
|
11
11
|
const bytesLength = byteCharacters.length;
|
|
12
12
|
const slicesCount = Math.ceil(bytesLength / sliceSize);
|
|
13
|
-
const byteArrays = new Array(slicesCount);
|
|
13
|
+
const byteArrays: Uint8Array[] = new Array(slicesCount);
|
|
14
14
|
|
|
15
15
|
for (let sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
|
|
16
16
|
const begin = sliceIndex * sliceSize;
|
|
17
17
|
const end = Math.min(begin + sliceSize, bytesLength);
|
|
18
18
|
|
|
19
|
-
const bytes = new Array(end - begin);
|
|
19
|
+
const bytes: number[] = new Array(end - begin);
|
|
20
20
|
for (let offset = begin, i = 0; offset < end; ++i, ++offset) {
|
|
21
21
|
bytes[i] = byteCharacters[offset].charCodeAt(0);
|
|
22
22
|
}
|
|
23
23
|
byteArrays[sliceIndex] = new Uint8Array(bytes);
|
|
24
24
|
}
|
|
25
|
-
return new Blob(byteArrays, { type: contentType });
|
|
25
|
+
return new Blob(byteArrays as any, { type: contentType });
|
|
26
26
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Original Source Code: https://github.com/mathiasbynens/quoted-printable/blob/master/quoted-printable.js
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
const stringFromCharCode = String.fromCharCode;
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
const decode = function(input: string): string {
|
|
6
6
|
return input
|
|
7
7
|
// https://tools.ietf.org/html/rfc2045#section-6.7, rule 3:
|
|
8
8
|
// “Therefore, when decoding a `Quoted-Printable` body, any trailing white
|
|
@@ -17,26 +17,23 @@ const decode = function(input) {
|
|
|
17
17
|
// combination of two hexidecimal digits. For optimal compatibility,
|
|
18
18
|
// lowercase hexadecimal digits are supported as well. See
|
|
19
19
|
// https://tools.ietf.org/html/rfc2045#section-6.7, note 1.
|
|
20
|
-
.replace(/=([a-fA-F0-9]{2})/g, function($0, $1) {
|
|
20
|
+
.replace(/=([a-fA-F0-9]{2})/g, function($0: string, $1: string): string {
|
|
21
21
|
const codePoint = parseInt($1, 16);
|
|
22
22
|
return stringFromCharCode(codePoint);
|
|
23
23
|
});
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
const handleTrailingCharacters =
|
|
27
|
-
function(
|
|
28
|
-
/** @type {string} */
|
|
29
|
-
text) {
|
|
26
|
+
const handleTrailingCharacters = function(text: string): string {
|
|
30
27
|
return text
|
|
31
28
|
.replace(/\x20$/, '=20') // Handle trailing space.
|
|
32
29
|
.replace(/\t$/, '=09'); // Handle trailing tab.
|
|
33
30
|
};
|
|
34
31
|
|
|
35
32
|
const regexUnsafeSymbols = /[\0-\x08\n-\x1F=\x7F-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
|
|
36
|
-
const encode = function(text) {
|
|
37
33
|
|
|
34
|
+
const encode = function(text: string): string {
|
|
38
35
|
// Encode symbols that are definitely unsafe (i.e. unsafe in any context).
|
|
39
|
-
const encoded = text.replace(regexUnsafeSymbols, function(symbol) {
|
|
36
|
+
const encoded = text.replace(regexUnsafeSymbols, function(symbol: string): string {
|
|
40
37
|
if (symbol > '\xFF') {
|
|
41
38
|
throw RangeError(
|
|
42
39
|
'`quotedPrintable.encode()` expects extended ASCII input only. ' +
|
|
@@ -55,8 +52,9 @@ const encode = function(text) {
|
|
|
55
52
|
const lines = encoded.split(/\r\n?|\n/g);
|
|
56
53
|
let lineIndex = -1;
|
|
57
54
|
const lineCount = lines.length;
|
|
58
|
-
const result = [];
|
|
59
|
-
let buffer;
|
|
55
|
+
const result: string[] = [];
|
|
56
|
+
let buffer: string;
|
|
57
|
+
|
|
60
58
|
while (++lineIndex < lineCount) {
|
|
61
59
|
const line = lines[lineIndex];
|
|
62
60
|
// Leave room for the trailing `=` for soft line breaks.
|
|
@@ -5,20 +5,20 @@ const isEncodedRegEx = /\=\?([^\?\s]+)\?([^\?\s]{1})\?([^\s\?]+)\?\=\s*/gm;
|
|
|
5
5
|
const hexToUnicode = /(\=([0-9a-f]{2}))|(\_)/gmi;
|
|
6
6
|
|
|
7
7
|
const decode = (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return text.replace(isEncodedRegEx, (matched, encoding, format,
|
|
8
|
+
text: string
|
|
9
|
+
): string => {
|
|
10
|
+
return text.replace(isEncodedRegEx, (matched: string, encoding: string, format: string, buffer: string): string => {
|
|
11
11
|
if (/b/i.test(format)) {
|
|
12
12
|
return RawBuffer.decode(atob(buffer), encoding);
|
|
13
13
|
}
|
|
14
14
|
if (/q/i.test(format)) {
|
|
15
|
-
const replaced = buffer.replace(hexToUnicode, (m, group
|
|
15
|
+
const replaced = buffer.replace(hexToUnicode, (m: string, group: string, code: string): string => {
|
|
16
16
|
if (m === "_") {
|
|
17
17
|
return " ";
|
|
18
18
|
}
|
|
19
19
|
return String.fromCharCode(parseInt(code, 16));
|
|
20
20
|
});
|
|
21
|
-
return RawBuffer.decode(
|
|
21
|
+
return RawBuffer.decode(replaced, encoding);
|
|
22
22
|
}
|
|
23
23
|
return matched;
|
|
24
24
|
});
|
|
@@ -29,7 +29,7 @@ const isSimpleText = /[0-9a-zA-Z\x20]/;
|
|
|
29
29
|
export const wordEncoding = {
|
|
30
30
|
decode,
|
|
31
31
|
|
|
32
|
-
encode: (
|
|
32
|
+
encode: (word: string, ifNeeded: boolean = false): string => {
|
|
33
33
|
|
|
34
34
|
if(ifNeeded) {
|
|
35
35
|
if(isSimpleText.test(word)) {
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { tokenize, tokenizeMax } from "./tokenizer.js";
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
export const parsePairs = (
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
text: string,
|
|
5
|
+
emptyName?: string
|
|
6
|
+
): Record<string, string> & { toString(): string } => {
|
|
7
7
|
const pairs = {
|
|
8
8
|
toString() {
|
|
9
9
|
return text;
|
|
10
10
|
}
|
|
11
|
-
};
|
|
11
|
+
} as Record<string, string> & { toString(): string };
|
|
12
|
+
|
|
12
13
|
for (const iterator of tokenize(text, ";")) {
|
|
13
14
|
const trimmed = iterator.trim();
|
|
14
15
|
if (!trimmed) {
|