Skip to content

Commit 1ed3f83

Browse files
committed
Fix whitespace in stream decoder
1 parent 5e18930 commit 1ed3f83

File tree

2 files changed

+161
-1
lines changed

2 files changed

+161
-1
lines changed

lib/libqp.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class Decoder extends Transform {
285285

286286
qp = this._curLine + chunk;
287287
this._curLine = '';
288-
qp = qp.replace(/\=[^\n]?$/, lastLine => {
288+
qp = qp.replace(/[\t ]*(?:=[^\n]?)?$/, lastLine => {
289289
this._curLine = lastLine;
290290
return '';
291291
});

test/libqp-test.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,163 @@ test('Encoding tests', async t => {
3939
assert.strictEqual(encoded, 'tere j=C3=B5geva');
4040
});
4141
});
42+
43+
test('Decoding tests', async t => {
44+
// Example taken from RFC2045 section 6.7
45+
const encoded =
46+
"Now's the time =\r\n" +
47+
"for all folk to come=\r\n" +
48+
" to the aid of their country."
49+
const expectedDecoded =
50+
"Now's the time for all folk to come to the aid of their country."
51+
52+
await t.test('simple string', async () => {
53+
const decoded = libqp.decode(encoded).toString();
54+
assert.strictEqual(decoded, expectedDecoded);
55+
});
56+
57+
await t.test('stream', async () => {
58+
const decoder = new libqp.Decoder();
59+
60+
const decoded = await new Promise((resolve, reject) => {
61+
const chunks = [];
62+
decoder.on('readable', () => {
63+
let chunk;
64+
65+
while ((chunk = decoder.read()) !== null) {
66+
chunks.push(chunk);
67+
}
68+
});
69+
decoder.on('end', () => {
70+
resolve(Buffer.concat(chunks).toString());
71+
});
72+
decoder.on('Error', err => {
73+
reject(err);
74+
});
75+
76+
decoder.end(Buffer.from(encoded));
77+
});
78+
79+
assert.strictEqual(decoded, expectedDecoded);
80+
});
81+
82+
await t.test('stream, multiple chunks', async () => {
83+
const encodedChunk1Length = 3;
84+
const encodedChunk1 = encoded.substring(0, encodedChunk1Length)
85+
const encodedChunk2 = encoded.substring(encodedChunk1Length)
86+
87+
const decoder = new libqp.Decoder();
88+
89+
const decoded = await new Promise((resolve, reject) => {
90+
const chunks = [];
91+
decoder.on('readable', () => {
92+
let chunk;
93+
94+
while ((chunk = decoder.read()) !== null) {
95+
chunks.push(chunk);
96+
}
97+
});
98+
decoder.on('end', () => {
99+
resolve(Buffer.concat(chunks).toString());
100+
});
101+
decoder.on('Error', err => {
102+
reject(err);
103+
});
104+
105+
decoder.write(Buffer.from(encodedChunk1));
106+
decoder.end(Buffer.from(encodedChunk2));
107+
});
108+
109+
assert.strictEqual(decoded, expectedDecoded);
110+
});
111+
112+
await t.test('stream, space at end of chunk', async () => {
113+
const encodedChunk1Length = encoded.indexOf(' ') + 1;
114+
const encodedChunk1 = encoded.substring(0, encodedChunk1Length)
115+
const encodedChunk2 = encoded.substring(encodedChunk1Length)
116+
117+
const decoder = new libqp.Decoder();
118+
119+
const decoded = await new Promise((resolve, reject) => {
120+
const chunks = [];
121+
decoder.on('readable', () => {
122+
let chunk;
123+
124+
while ((chunk = decoder.read()) !== null) {
125+
chunks.push(chunk);
126+
}
127+
});
128+
decoder.on('end', () => {
129+
resolve(Buffer.concat(chunks).toString());
130+
});
131+
decoder.on('Error', err => {
132+
reject(err);
133+
});
134+
135+
decoder.write(Buffer.from(encodedChunk1));
136+
decoder.end(Buffer.from(encodedChunk2));
137+
});
138+
139+
assert.strictEqual(decoded, expectedDecoded);
140+
});
141+
142+
await t.test('stream, soft line break equals sign at end of chunk', async () => {
143+
const encodedChunk1Length = encoded.indexOf('=') + 1;
144+
const encodedChunk1 = encoded.substring(0, encodedChunk1Length)
145+
const encodedChunk2 = encoded.substring(encodedChunk1Length)
146+
147+
const decoder = new libqp.Decoder();
148+
149+
const decoded = await new Promise((resolve, reject) => {
150+
const chunks = [];
151+
decoder.on('readable', () => {
152+
let chunk;
153+
154+
while ((chunk = decoder.read()) !== null) {
155+
chunks.push(chunk);
156+
}
157+
});
158+
decoder.on('end', () => {
159+
resolve(Buffer.concat(chunks).toString());
160+
});
161+
decoder.on('Error', err => {
162+
reject(err);
163+
});
164+
165+
decoder.write(Buffer.from(encodedChunk1));
166+
decoder.end(Buffer.from(encodedChunk2));
167+
});
168+
169+
assert.strictEqual(decoded, expectedDecoded);
170+
});
171+
172+
await t.test('stream, CR at end of chunk', async () => {
173+
const encodedChunk1Length = encoded.indexOf('\r') + 1;
174+
const encodedChunk1 = encoded.substring(0, encodedChunk1Length)
175+
const encodedChunk2 = encoded.substring(encodedChunk1Length)
176+
177+
const decoder = new libqp.Decoder();
178+
179+
const decoded = await new Promise((resolve, reject) => {
180+
const chunks = [];
181+
decoder.on('readable', () => {
182+
let chunk;
183+
184+
while ((chunk = decoder.read()) !== null) {
185+
chunks.push(chunk);
186+
}
187+
});
188+
decoder.on('end', () => {
189+
resolve(Buffer.concat(chunks).toString());
190+
});
191+
decoder.on('Error', err => {
192+
reject(err);
193+
});
194+
195+
decoder.write(Buffer.from(encodedChunk1));
196+
decoder.end(Buffer.from(encodedChunk2));
197+
});
198+
199+
assert.strictEqual(decoded, expectedDecoded);
200+
});
201+
});

0 commit comments

Comments
 (0)