polikolog 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. package/.idea/5lab.iml +12 -0
  2. package/.idea/inspectionProfiles/Project_Default.xml +10 -0
  3. package/.idea/jsLibraryMappings.xml +6 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/vcs.xml +6 -0
  6. package/06-02.js +48 -0
  7. package/06-03.js +22 -0
  8. package/06-04.js +22 -0
  9. package/index.html +41 -0
  10. package/m0603.js +28 -0
  11. package/mypackage/m0603.js +28 -0
  12. package/mypackage/node_modules/.package-lock.json +24 -0
  13. package/mypackage/node_modules/nodemailer/.gitattributes +6 -0
  14. package/mypackage/node_modules/nodemailer/.prettierrc.js +8 -0
  15. package/mypackage/node_modules/nodemailer/CHANGELOG.md +725 -0
  16. package/mypackage/node_modules/nodemailer/CODE_OF_CONDUCT.md +76 -0
  17. package/mypackage/node_modules/nodemailer/CONTRIBUTING.md +67 -0
  18. package/mypackage/node_modules/nodemailer/LICENSE +16 -0
  19. package/mypackage/node_modules/nodemailer/README.md +97 -0
  20. package/mypackage/node_modules/nodemailer/SECURITY.txt +22 -0
  21. package/mypackage/node_modules/nodemailer/lib/addressparser/index.js +313 -0
  22. package/mypackage/node_modules/nodemailer/lib/base64/index.js +142 -0
  23. package/mypackage/node_modules/nodemailer/lib/dkim/index.js +251 -0
  24. package/mypackage/node_modules/nodemailer/lib/dkim/message-parser.js +155 -0
  25. package/mypackage/node_modules/nodemailer/lib/dkim/relaxed-body.js +154 -0
  26. package/mypackage/node_modules/nodemailer/lib/dkim/sign.js +117 -0
  27. package/mypackage/node_modules/nodemailer/lib/fetch/cookies.js +281 -0
  28. package/mypackage/node_modules/nodemailer/lib/fetch/index.js +274 -0
  29. package/mypackage/node_modules/nodemailer/lib/json-transport/index.js +82 -0
  30. package/mypackage/node_modules/nodemailer/lib/mail-composer/index.js +558 -0
  31. package/mypackage/node_modules/nodemailer/lib/mailer/index.js +427 -0
  32. package/mypackage/node_modules/nodemailer/lib/mailer/mail-message.js +315 -0
  33. package/mypackage/node_modules/nodemailer/lib/mime-funcs/index.js +625 -0
  34. package/mypackage/node_modules/nodemailer/lib/mime-funcs/mime-types.js +2102 -0
  35. package/mypackage/node_modules/nodemailer/lib/mime-node/index.js +1290 -0
  36. package/mypackage/node_modules/nodemailer/lib/mime-node/last-newline.js +33 -0
  37. package/mypackage/node_modules/nodemailer/lib/mime-node/le-unix.js +43 -0
  38. package/mypackage/node_modules/nodemailer/lib/mime-node/le-windows.js +52 -0
  39. package/mypackage/node_modules/nodemailer/lib/nodemailer.js +143 -0
  40. package/mypackage/node_modules/nodemailer/lib/qp/index.js +219 -0
  41. package/mypackage/node_modules/nodemailer/lib/sendmail-transport/index.js +210 -0
  42. package/mypackage/node_modules/nodemailer/lib/ses-transport/index.js +349 -0
  43. package/mypackage/node_modules/nodemailer/lib/shared/index.js +638 -0
  44. package/mypackage/node_modules/nodemailer/lib/smtp-connection/data-stream.js +108 -0
  45. package/mypackage/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +143 -0
  46. package/mypackage/node_modules/nodemailer/lib/smtp-connection/index.js +1796 -0
  47. package/mypackage/node_modules/nodemailer/lib/smtp-pool/index.js +648 -0
  48. package/mypackage/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +253 -0
  49. package/mypackage/node_modules/nodemailer/lib/smtp-transport/index.js +416 -0
  50. package/mypackage/node_modules/nodemailer/lib/stream-transport/index.js +135 -0
  51. package/mypackage/node_modules/nodemailer/lib/well-known/index.js +47 -0
  52. package/mypackage/node_modules/nodemailer/lib/well-known/services.json +286 -0
  53. package/mypackage/node_modules/nodemailer/lib/xoauth2/index.js +376 -0
  54. package/mypackage/node_modules/nodemailer/package.json +46 -0
  55. package/mypackage/node_modules/nodemailer/postinstall.js +101 -0
  56. package/mypackage/package.json +15 -0
  57. package/package.json +15 -0
@@ -0,0 +1,625 @@
1
+ /* eslint no-control-regex:0 */
2
+
3
+ 'use strict';
4
+
5
+ const base64 = require('../base64');
6
+ const qp = require('../qp');
7
+ const mimeTypes = require('./mime-types');
8
+
9
+ module.exports = {
10
+ /**
11
+ * Checks if a value is plaintext string (uses only printable 7bit chars)
12
+ *
13
+ * @param {String} value String to be tested
14
+ * @returns {Boolean} true if it is a plaintext string
15
+ */
16
+ isPlainText(value, isParam) {
17
+ const re = isParam ? /[\x00-\x08\x0b\x0c\x0e-\x1f"\u0080-\uFFFF]/ : /[\x00-\x08\x0b\x0c\x0e-\x1f\u0080-\uFFFF]/;
18
+ if (typeof value !== 'string' || re.test(value)) {
19
+ return false;
20
+ } else {
21
+ return true;
22
+ }
23
+ },
24
+
25
+ /**
26
+ * Checks if a multi line string containes lines longer than the selected value.
27
+ *
28
+ * Useful when detecting if a mail message needs any processing at all –
29
+ * if only plaintext characters are used and lines are short, then there is
30
+ * no need to encode the values in any way. If the value is plaintext but has
31
+ * longer lines then allowed, then use format=flowed
32
+ *
33
+ * @param {Number} lineLength Max line length to check for
34
+ * @returns {Boolean} Returns true if there is at least one line longer than lineLength chars
35
+ */
36
+ hasLongerLines(str, lineLength) {
37
+ if (str.length > 128 * 1024) {
38
+ // do not test strings longer than 128kB
39
+ return true;
40
+ }
41
+ return new RegExp('^.{' + (lineLength + 1) + ',}', 'm').test(str);
42
+ },
43
+
44
+ /**
45
+ * Encodes a string or an Buffer to an UTF-8 MIME Word (rfc2047)
46
+ *
47
+ * @param {String|Buffer} data String to be encoded
48
+ * @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B
49
+ * @param {Number} [maxLength=0] If set, split mime words into several chunks if needed
50
+ * @return {String} Single or several mime words joined together
51
+ */
52
+ encodeWord(data, mimeWordEncoding, maxLength) {
53
+ mimeWordEncoding = (mimeWordEncoding || 'Q').toString().toUpperCase().trim().charAt(0);
54
+ maxLength = maxLength || 0;
55
+
56
+ let encodedStr;
57
+ let toCharset = 'UTF-8';
58
+
59
+ if (maxLength && maxLength > 7 + toCharset.length) {
60
+ maxLength -= 7 + toCharset.length;
61
+ }
62
+
63
+ if (mimeWordEncoding === 'Q') {
64
+ // https://tools.ietf.org/html/rfc2047#section-5 rule (3)
65
+ encodedStr = qp.encode(data).replace(/[^a-z0-9!*+\-/=]/gi, chr => {
66
+ let ord = chr.charCodeAt(0).toString(16).toUpperCase();
67
+ if (chr === ' ') {
68
+ return '_';
69
+ } else {
70
+ return '=' + (ord.length === 1 ? '0' + ord : ord);
71
+ }
72
+ });
73
+ } else if (mimeWordEncoding === 'B') {
74
+ encodedStr = typeof data === 'string' ? data : base64.encode(data);
75
+ maxLength = maxLength ? Math.max(3, ((maxLength - (maxLength % 4)) / 4) * 3) : 0;
76
+ }
77
+
78
+ if (maxLength && (mimeWordEncoding !== 'B' ? encodedStr : base64.encode(data)).length > maxLength) {
79
+ if (mimeWordEncoding === 'Q') {
80
+ encodedStr = this.splitMimeEncodedString(encodedStr, maxLength).join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');
81
+ } else {
82
+ // RFC2047 6.3 (2) states that encoded-word must include an integral number of characters, so no chopping unicode sequences
83
+ let parts = [];
84
+ let lpart = '';
85
+ for (let i = 0, len = encodedStr.length; i < len; i++) {
86
+ let chr = encodedStr.charAt(i);
87
+
88
+ if (/[\ud83c\ud83d\ud83e]/.test(chr) && i < len - 1) {
89
+ // composite emoji byte, so add the next byte as well
90
+ chr += encodedStr.charAt(++i);
91
+ }
92
+
93
+ // check if we can add this character to the existing string
94
+ // without breaking byte length limit
95
+ if (Buffer.byteLength(lpart + chr) <= maxLength || i === 0) {
96
+ lpart += chr;
97
+ } else {
98
+ // we hit the length limit, so push the existing string and start over
99
+ parts.push(base64.encode(lpart));
100
+ lpart = chr;
101
+ }
102
+ }
103
+ if (lpart) {
104
+ parts.push(base64.encode(lpart));
105
+ }
106
+
107
+ if (parts.length > 1) {
108
+ encodedStr = parts.join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');
109
+ } else {
110
+ encodedStr = parts.join('');
111
+ }
112
+ }
113
+ } else if (mimeWordEncoding === 'B') {
114
+ encodedStr = base64.encode(data);
115
+ }
116
+
117
+ return '=?' + toCharset + '?' + mimeWordEncoding + '?' + encodedStr + (encodedStr.substr(-2) === '?=' ? '' : '?=');
118
+ },
119
+
120
+ /**
121
+ * Finds word sequences with non ascii text and converts these to mime words
122
+ *
123
+ * @param {String} value String to be encoded
124
+ * @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B
125
+ * @param {Number} [maxLength=0] If set, split mime words into several chunks if needed
126
+ * @param {Boolean} [encodeAll=false] If true and the value needs encoding then encodes entire string, not just the smallest match
127
+ * @return {String} String with possible mime words
128
+ */
129
+ encodeWords(value, mimeWordEncoding, maxLength, encodeAll) {
130
+ maxLength = maxLength || 0;
131
+
132
+ let encodedValue;
133
+
134
+ // find first word with a non-printable ascii or special symbol in it
135
+ let firstMatch = value.match(/(?:^|\s)([^\s]*["\u0080-\uFFFF])/);
136
+ if (!firstMatch) {
137
+ return value;
138
+ }
139
+
140
+ if (encodeAll) {
141
+ // if it is requested to encode everything or the string contains something that resebles encoded word, then encode everything
142
+
143
+ return this.encodeWord(value, mimeWordEncoding, maxLength);
144
+ }
145
+
146
+ // find the last word with a non-printable ascii in it
147
+ let lastMatch = value.match(/(["\u0080-\uFFFF][^\s]*)[^"\u0080-\uFFFF]*$/);
148
+ if (!lastMatch) {
149
+ // should not happen
150
+ return value;
151
+ }
152
+
153
+ let startIndex =
154
+ firstMatch.index +
155
+ (
156
+ firstMatch[0].match(/[^\s]/) || {
157
+ index: 0
158
+ }
159
+ ).index;
160
+ let endIndex = lastMatch.index + (lastMatch[1] || '').length;
161
+
162
+ encodedValue =
163
+ (startIndex ? value.substr(0, startIndex) : '') +
164
+ this.encodeWord(value.substring(startIndex, endIndex), mimeWordEncoding || 'Q', maxLength) +
165
+ (endIndex < value.length ? value.substr(endIndex) : '');
166
+
167
+ return encodedValue;
168
+ },
169
+
170
+ /**
171
+ * Joins parsed header value together as 'value; param1=value1; param2=value2'
172
+ * PS: We are following RFC 822 for the list of special characters that we need to keep in quotes.
173
+ * Refer: https://www.w3.org/Protocols/rfc1341/4_Content-Type.html
174
+ * @param {Object} structured Parsed header value
175
+ * @return {String} joined header value
176
+ */
177
+ buildHeaderValue(structured) {
178
+ let paramsArray = [];
179
+
180
+ Object.keys(structured.params || {}).forEach(param => {
181
+ // filename might include unicode characters so it is a special case
182
+ // other values probably do not
183
+ let value = structured.params[param];
184
+ if (!this.isPlainText(value, true) || value.length >= 75) {
185
+ this.buildHeaderParam(param, value, 50).forEach(encodedParam => {
186
+ if (!/[\s"\\;:/=(),<>@[\]?]|^[-']|'$/.test(encodedParam.value) || encodedParam.key.substr(-1) === '*') {
187
+ paramsArray.push(encodedParam.key + '=' + encodedParam.value);
188
+ } else {
189
+ paramsArray.push(encodedParam.key + '=' + JSON.stringify(encodedParam.value));
190
+ }
191
+ });
192
+ } else if (/[\s'"\\;:/=(),<>@[\]?]|^-/.test(value)) {
193
+ paramsArray.push(param + '=' + JSON.stringify(value));
194
+ } else {
195
+ paramsArray.push(param + '=' + value);
196
+ }
197
+ });
198
+
199
+ return structured.value + (paramsArray.length ? '; ' + paramsArray.join('; ') : '');
200
+ },
201
+
202
+ /**
203
+ * Encodes a string or an Buffer to an UTF-8 Parameter Value Continuation encoding (rfc2231)
204
+ * Useful for splitting long parameter values.
205
+ *
206
+ * For example
207
+ * title="unicode string"
208
+ * becomes
209
+ * title*0*=utf-8''unicode
210
+ * title*1*=%20string
211
+ *
212
+ * @param {String|Buffer} data String to be encoded
213
+ * @param {Number} [maxLength=50] Max length for generated chunks
214
+ * @param {String} [fromCharset='UTF-8'] Source sharacter set
215
+ * @return {Array} A list of encoded keys and headers
216
+ */
217
+ buildHeaderParam(key, data, maxLength) {
218
+ let list = [];
219
+ let encodedStr = typeof data === 'string' ? data : (data || '').toString();
220
+ let encodedStrArr;
221
+ let chr, ord;
222
+ let line;
223
+ let startPos = 0;
224
+ let i, len;
225
+
226
+ maxLength = maxLength || 50;
227
+
228
+ // process ascii only text
229
+ if (this.isPlainText(data, true)) {
230
+ // check if conversion is even needed
231
+ if (encodedStr.length <= maxLength) {
232
+ return [
233
+ {
234
+ key,
235
+ value: encodedStr
236
+ }
237
+ ];
238
+ }
239
+
240
+ encodedStr = encodedStr.replace(new RegExp('.{' + maxLength + '}', 'g'), str => {
241
+ list.push({
242
+ line: str
243
+ });
244
+ return '';
245
+ });
246
+
247
+ if (encodedStr) {
248
+ list.push({
249
+ line: encodedStr
250
+ });
251
+ }
252
+ } else {
253
+ if (/[\uD800-\uDBFF]/.test(encodedStr)) {
254
+ // string containts surrogate pairs, so normalize it to an array of bytes
255
+ encodedStrArr = [];
256
+ for (i = 0, len = encodedStr.length; i < len; i++) {
257
+ chr = encodedStr.charAt(i);
258
+ ord = chr.charCodeAt(0);
259
+ if (ord >= 0xd800 && ord <= 0xdbff && i < len - 1) {
260
+ chr += encodedStr.charAt(i + 1);
261
+ encodedStrArr.push(chr);
262
+ i++;
263
+ } else {
264
+ encodedStrArr.push(chr);
265
+ }
266
+ }
267
+ encodedStr = encodedStrArr;
268
+ }
269
+
270
+ // first line includes the charset and language info and needs to be encoded
271
+ // even if it does not contain any unicode characters
272
+ line = 'utf-8\x27\x27';
273
+ let encoded = true;
274
+ startPos = 0;
275
+
276
+ // process text with unicode or special chars
277
+ for (i = 0, len = encodedStr.length; i < len; i++) {
278
+ chr = encodedStr[i];
279
+
280
+ if (encoded) {
281
+ chr = this.safeEncodeURIComponent(chr);
282
+ } else {
283
+ // try to urlencode current char
284
+ chr = chr === ' ' ? chr : this.safeEncodeURIComponent(chr);
285
+ // By default it is not required to encode a line, the need
286
+ // only appears when the string contains unicode or special chars
287
+ // in this case we start processing the line over and encode all chars
288
+ if (chr !== encodedStr[i]) {
289
+ // Check if it is even possible to add the encoded char to the line
290
+ // If not, there is no reason to use this line, just push it to the list
291
+ // and start a new line with the char that needs encoding
292
+ if ((this.safeEncodeURIComponent(line) + chr).length >= maxLength) {
293
+ list.push({
294
+ line,
295
+ encoded
296
+ });
297
+ line = '';
298
+ startPos = i - 1;
299
+ } else {
300
+ encoded = true;
301
+ i = startPos;
302
+ line = '';
303
+ continue;
304
+ }
305
+ }
306
+ }
307
+
308
+ // if the line is already too long, push it to the list and start a new one
309
+ if ((line + chr).length >= maxLength) {
310
+ list.push({
311
+ line,
312
+ encoded
313
+ });
314
+ line = chr = encodedStr[i] === ' ' ? ' ' : this.safeEncodeURIComponent(encodedStr[i]);
315
+ if (chr === encodedStr[i]) {
316
+ encoded = false;
317
+ startPos = i - 1;
318
+ } else {
319
+ encoded = true;
320
+ }
321
+ } else {
322
+ line += chr;
323
+ }
324
+ }
325
+
326
+ if (line) {
327
+ list.push({
328
+ line,
329
+ encoded
330
+ });
331
+ }
332
+ }
333
+
334
+ return list.map((item, i) => ({
335
+ // encoded lines: {name}*{part}*
336
+ // unencoded lines: {name}*{part}
337
+ // if any line needs to be encoded then the first line (part==0) is always encoded
338
+ key: key + '*' + i + (item.encoded ? '*' : ''),
339
+ value: item.line
340
+ }));
341
+ },
342
+
343
+ /**
344
+ * Parses a header value with key=value arguments into a structured
345
+ * object.
346
+ *
347
+ * parseHeaderValue('content-type: text/plain; CHARSET='UTF-8'') ->
348
+ * {
349
+ * 'value': 'text/plain',
350
+ * 'params': {
351
+ * 'charset': 'UTF-8'
352
+ * }
353
+ * }
354
+ *
355
+ * @param {String} str Header value
356
+ * @return {Object} Header value as a parsed structure
357
+ */
358
+ parseHeaderValue(str) {
359
+ let response = {
360
+ value: false,
361
+ params: {}
362
+ };
363
+ let key = false;
364
+ let value = '';
365
+ let type = 'value';
366
+ let quote = false;
367
+ let escaped = false;
368
+ let chr;
369
+
370
+ for (let i = 0, len = str.length; i < len; i++) {
371
+ chr = str.charAt(i);
372
+ if (type === 'key') {
373
+ if (chr === '=') {
374
+ key = value.trim().toLowerCase();
375
+ type = 'value';
376
+ value = '';
377
+ continue;
378
+ }
379
+ value += chr;
380
+ } else {
381
+ if (escaped) {
382
+ value += chr;
383
+ } else if (chr === '\\') {
384
+ escaped = true;
385
+ continue;
386
+ } else if (quote && chr === quote) {
387
+ quote = false;
388
+ } else if (!quote && chr === '"') {
389
+ quote = chr;
390
+ } else if (!quote && chr === ';') {
391
+ if (key === false) {
392
+ response.value = value.trim();
393
+ } else {
394
+ response.params[key] = value.trim();
395
+ }
396
+ type = 'key';
397
+ value = '';
398
+ } else {
399
+ value += chr;
400
+ }
401
+ escaped = false;
402
+ }
403
+ }
404
+
405
+ if (type === 'value') {
406
+ if (key === false) {
407
+ response.value = value.trim();
408
+ } else {
409
+ response.params[key] = value.trim();
410
+ }
411
+ } else if (value.trim()) {
412
+ response.params[value.trim().toLowerCase()] = '';
413
+ }
414
+
415
+ // handle parameter value continuations
416
+ // https://tools.ietf.org/html/rfc2231#section-3
417
+
418
+ // preprocess values
419
+ Object.keys(response.params).forEach(key => {
420
+ let actualKey, nr, match, value;
421
+ if ((match = key.match(/(\*(\d+)|\*(\d+)\*|\*)$/))) {
422
+ actualKey = key.substr(0, match.index);
423
+ nr = Number(match[2] || match[3]) || 0;
424
+
425
+ if (!response.params[actualKey] || typeof response.params[actualKey] !== 'object') {
426
+ response.params[actualKey] = {
427
+ charset: false,
428
+ values: []
429
+ };
430
+ }
431
+
432
+ value = response.params[key];
433
+
434
+ if (nr === 0 && match[0].substr(-1) === '*' && (match = value.match(/^([^']*)'[^']*'(.*)$/))) {
435
+ response.params[actualKey].charset = match[1] || 'iso-8859-1';
436
+ value = match[2];
437
+ }
438
+
439
+ response.params[actualKey].values[nr] = value;
440
+
441
+ // remove the old reference
442
+ delete response.params[key];
443
+ }
444
+ });
445
+
446
+ // concatenate split rfc2231 strings and convert encoded strings to mime encoded words
447
+ Object.keys(response.params).forEach(key => {
448
+ let value;
449
+ if (response.params[key] && Array.isArray(response.params[key].values)) {
450
+ value = response.params[key].values.map(val => val || '').join('');
451
+
452
+ if (response.params[key].charset) {
453
+ // convert "%AB" to "=?charset?Q?=AB?="
454
+ response.params[key] =
455
+ '=?' +
456
+ response.params[key].charset +
457
+ '?Q?' +
458
+ value
459
+ // fix invalidly encoded chars
460
+ .replace(/[=?_\s]/g, s => {
461
+ let c = s.charCodeAt(0).toString(16);
462
+ if (s === ' ') {
463
+ return '_';
464
+ } else {
465
+ return '%' + (c.length < 2 ? '0' : '') + c;
466
+ }
467
+ })
468
+ // change from urlencoding to percent encoding
469
+ .replace(/%/g, '=') +
470
+ '?=';
471
+ } else {
472
+ response.params[key] = value;
473
+ }
474
+ }
475
+ });
476
+
477
+ return response;
478
+ },
479
+
480
+ /**
481
+ * Returns file extension for a content type string. If no suitable extensions
482
+ * are found, 'bin' is used as the default extension
483
+ *
484
+ * @param {String} mimeType Content type to be checked for
485
+ * @return {String} File extension
486
+ */
487
+ detectExtension: mimeType => mimeTypes.detectExtension(mimeType),
488
+
489
+ /**
490
+ * Returns content type for a file extension. If no suitable content types
491
+ * are found, 'application/octet-stream' is used as the default content type
492
+ *
493
+ * @param {String} extension Extension to be checked for
494
+ * @return {String} File extension
495
+ */
496
+ detectMimeType: extension => mimeTypes.detectMimeType(extension),
497
+
498
+ /**
499
+ * Folds long lines, useful for folding header lines (afterSpace=false) and
500
+ * flowed text (afterSpace=true)
501
+ *
502
+ * @param {String} str String to be folded
503
+ * @param {Number} [lineLength=76] Maximum length of a line
504
+ * @param {Boolean} afterSpace If true, leave a space in th end of a line
505
+ * @return {String} String with folded lines
506
+ */
507
+ foldLines(str, lineLength, afterSpace) {
508
+ str = (str || '').toString();
509
+ lineLength = lineLength || 76;
510
+
511
+ let pos = 0,
512
+ len = str.length,
513
+ result = '',
514
+ line,
515
+ match;
516
+
517
+ while (pos < len) {
518
+ line = str.substr(pos, lineLength);
519
+ if (line.length < lineLength) {
520
+ result += line;
521
+ break;
522
+ }
523
+ if ((match = line.match(/^[^\n\r]*(\r?\n|\r)/))) {
524
+ line = match[0];
525
+ result += line;
526
+ pos += line.length;
527
+ continue;
528
+ } else if ((match = line.match(/(\s+)[^\s]*$/)) && match[0].length - (afterSpace ? (match[1] || '').length : 0) < line.length) {
529
+ line = line.substr(0, line.length - (match[0].length - (afterSpace ? (match[1] || '').length : 0)));
530
+ } else if ((match = str.substr(pos + line.length).match(/^[^\s]+(\s*)/))) {
531
+ line = line + match[0].substr(0, match[0].length - (!afterSpace ? (match[1] || '').length : 0));
532
+ }
533
+
534
+ result += line;
535
+ pos += line.length;
536
+ if (pos < len) {
537
+ result += '\r\n';
538
+ }
539
+ }
540
+
541
+ return result;
542
+ },
543
+
544
+ /**
545
+ * Splits a mime encoded string. Needed for dividing mime words into smaller chunks
546
+ *
547
+ * @param {String} str Mime encoded string to be split up
548
+ * @param {Number} maxlen Maximum length of characters for one part (minimum 12)
549
+ * @return {Array} Split string
550
+ */
551
+ splitMimeEncodedString: (str, maxlen) => {
552
+ let curLine,
553
+ match,
554
+ chr,
555
+ done,
556
+ lines = [];
557
+
558
+ // require at least 12 symbols to fit possible 4 octet UTF-8 sequences
559
+ maxlen = Math.max(maxlen || 0, 12);
560
+
561
+ while (str.length) {
562
+ curLine = str.substr(0, maxlen);
563
+
564
+ // move incomplete escaped char back to main
565
+ if ((match = curLine.match(/[=][0-9A-F]?$/i))) {
566
+ curLine = curLine.substr(0, match.index);
567
+ }
568
+
569
+ done = false;
570
+ while (!done) {
571
+ done = true;
572
+ // check if not middle of a unicode char sequence
573
+ if ((match = str.substr(curLine.length).match(/^[=]([0-9A-F]{2})/i))) {
574
+ chr = parseInt(match[1], 16);
575
+ // invalid sequence, move one char back anc recheck
576
+ if (chr < 0xc2 && chr > 0x7f) {
577
+ curLine = curLine.substr(0, curLine.length - 3);
578
+ done = false;
579
+ }
580
+ }
581
+ }
582
+
583
+ if (curLine.length) {
584
+ lines.push(curLine);
585
+ }
586
+ str = str.substr(curLine.length);
587
+ }
588
+
589
+ return lines;
590
+ },
591
+
592
+ encodeURICharComponent: chr => {
593
+ let res = '';
594
+ let ord = chr.charCodeAt(0).toString(16).toUpperCase();
595
+
596
+ if (ord.length % 2) {
597
+ ord = '0' + ord;
598
+ }
599
+
600
+ if (ord.length > 2) {
601
+ for (let i = 0, len = ord.length / 2; i < len; i++) {
602
+ res += '%' + ord.substr(i, 2);
603
+ }
604
+ } else {
605
+ res += '%' + ord;
606
+ }
607
+
608
+ return res;
609
+ },
610
+
611
+ safeEncodeURIComponent(str) {
612
+ str = (str || '').toString();
613
+
614
+ try {
615
+ // might throw if we try to encode invalid sequences, eg. partial emoji
616
+ str = encodeURIComponent(str);
617
+ } catch (E) {
618
+ // should never run
619
+ return str.replace(/[^\x00-\x1F *'()<>@,;:\\"[\]?=\u007F-\uFFFF]+/g, '');
620
+ }
621
+
622
+ // ensure chars that are not handled by encodeURICompent are converted as well
623
+ return str.replace(/[\x00-\x1F *'()<>@,;:\\"[\]?=\u007F-\uFFFF]/g, chr => this.encodeURICharComponent(chr));
624
+ }
625
+ };