source: node_modules/nodemailer/lib/qp/index.js@ 62b2964

finki-main main
Last change on this file since 62b2964 was 81bc7da, checked in by Klimentina Efremova <klimentina08642@…>, 3 months ago

Initial commit

  • Property mode set to 100644
File size: 6.4 KB
Line 
1'use strict';
2
3const Transform = require('stream').Transform;
4
5/**
6 * Encodes a Buffer into a Quoted-Printable encoded string
7 *
8 * @param {Buffer} buffer Buffer to convert
9 * @returns {String} Quoted-Printable encoded string
10 */
11function encode(buffer) {
12 if (typeof buffer === 'string') {
13 buffer = Buffer.from(buffer, 'utf-8');
14 }
15
16 // usable characters that do not need encoding
17 let ranges = [
18 // https://tools.ietf.org/html/rfc2045#section-6.7
19 [0x09], // <TAB>
20 [0x0a], // <LF>
21 [0x0d], // <CR>
22 [0x20, 0x3c], // <SP>!"#$%&'()*+,-./0123456789:;
23 [0x3e, 0x7e] // >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
24 ];
25 let result = '';
26 let ord;
27
28 for (let i = 0, len = buffer.length; i < len; i++) {
29 ord = buffer[i];
30 // if the char is in allowed range, then keep as is, unless it is a WS in the end of a line
31 if (checkRanges(ord, ranges) && !((ord === 0x20 || ord === 0x09) && (i === len - 1 || buffer[i + 1] === 0x0a || buffer[i + 1] === 0x0d))) {
32 result += String.fromCharCode(ord);
33 continue;
34 }
35 result += '=' + (ord < 0x10 ? '0' : '') + ord.toString(16).toUpperCase();
36 }
37
38 return result;
39}
40
41/**
42 * Adds soft line breaks to a Quoted-Printable string
43 *
44 * @param {String} str Quoted-Printable encoded string that might need line wrapping
45 * @param {Number} [lineLength=76] Maximum allowed length for a line
46 * @returns {String} Soft-wrapped Quoted-Printable encoded string
47 */
48function wrap(str, lineLength) {
49 str = (str || '').toString();
50 lineLength = lineLength || 76;
51
52 if (str.length <= lineLength) {
53 return str;
54 }
55
56 let pos = 0;
57 let len = str.length;
58 let match, code, line;
59 let lineMargin = Math.floor(lineLength / 3);
60 let result = '';
61
62 // insert soft linebreaks where needed
63 while (pos < len) {
64 line = str.substr(pos, lineLength);
65 if ((match = line.match(/\r\n/))) {
66 line = line.substr(0, match.index + match[0].length);
67 result += line;
68 pos += line.length;
69 continue;
70 }
71
72 if (line.substr(-1) === '\n') {
73 // nothing to change here
74 result += line;
75 pos += line.length;
76 continue;
77 } else if ((match = line.substr(-lineMargin).match(/\n.*?$/))) {
78 // truncate to nearest line break
79 line = line.substr(0, line.length - (match[0].length - 1));
80 result += line;
81 pos += line.length;
82 continue;
83 } else if (line.length > lineLength - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
84 // truncate to nearest space
85 line = line.substr(0, line.length - (match[0].length - 1));
86 } else if (line.match(/[=][\da-f]{0,2}$/i)) {
87 // push incomplete encoding sequences to the next line
88 if ((match = line.match(/[=][\da-f]{0,1}$/i))) {
89 line = line.substr(0, line.length - match[0].length);
90 }
91
92 // ensure that utf-8 sequences are not split
93 while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/[=][\da-f]{2}$/gi))) {
94 code = parseInt(match[0].substr(1, 2), 16);
95 if (code < 128) {
96 break;
97 }
98
99 line = line.substr(0, line.length - 3);
100
101 if (code >= 0xc0) {
102 break;
103 }
104 }
105 }
106
107 if (pos + line.length < len && line.substr(-1) !== '\n') {
108 if (line.length === lineLength && line.match(/[=][\da-f]{2}$/i)) {
109 line = line.substr(0, line.length - 3);
110 } else if (line.length === lineLength) {
111 line = line.substr(0, line.length - 1);
112 }
113 pos += line.length;
114 line += '=\r\n';
115 } else {
116 pos += line.length;
117 }
118
119 result += line;
120 }
121
122 return result;
123}
124
125/**
126 * Helper function to check if a number is inside provided ranges
127 *
128 * @param {Number} nr Number to check for
129 * @param {Array} ranges An Array of allowed values
130 * @returns {Boolean} True if the value was found inside allowed ranges, false otherwise
131 */
132function checkRanges(nr, ranges) {
133 for (let i = ranges.length - 1; i >= 0; i--) {
134 if (!ranges[i].length) {
135 continue;
136 }
137 if (ranges[i].length === 1 && nr === ranges[i][0]) {
138 return true;
139 }
140 if (ranges[i].length === 2 && nr >= ranges[i][0] && nr <= ranges[i][1]) {
141 return true;
142 }
143 }
144 return false;
145}
146
147/**
148 * Creates a transform stream for encoding data to Quoted-Printable encoding
149 *
150 * @constructor
151 * @param {Object} options Stream options
152 * @param {Number} [options.lineLength=76] Maximum length for lines, set to false to disable wrapping
153 */
154class Encoder extends Transform {
155 constructor(options) {
156 super();
157
158 // init Transform
159 this.options = options || {};
160
161 if (this.options.lineLength !== false) {
162 this.options.lineLength = this.options.lineLength || 76;
163 }
164
165 this._curLine = '';
166
167 this.inputBytes = 0;
168 this.outputBytes = 0;
169 }
170
171 _transform(chunk, encoding, done) {
172 let qp;
173
174 if (encoding !== 'buffer') {
175 chunk = Buffer.from(chunk, encoding);
176 }
177
178 if (!chunk || !chunk.length) {
179 return done();
180 }
181
182 this.inputBytes += chunk.length;
183
184 if (this.options.lineLength) {
185 qp = this._curLine + encode(chunk);
186 qp = wrap(qp, this.options.lineLength);
187 qp = qp.replace(/(^|\n)([^\n]*)$/, (match, lineBreak, lastLine) => {
188 this._curLine = lastLine;
189 return lineBreak;
190 });
191
192 if (qp) {
193 this.outputBytes += qp.length;
194 this.push(qp);
195 }
196 } else {
197 qp = encode(chunk);
198 this.outputBytes += qp.length;
199 this.push(qp, 'ascii');
200 }
201
202 done();
203 }
204
205 _flush(done) {
206 if (this._curLine) {
207 this.outputBytes += this._curLine.length;
208 this.push(this._curLine, 'ascii');
209 }
210 done();
211 }
212}
213
214// expose to the world
215module.exports = {
216 encode,
217 wrap,
218 Encoder
219};
Note: See TracBrowser for help on using the repository browser.