m06_task3.js 1.0.0

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