Skip to content

Commit c05b246

Browse files
authored
Release v1.3.1 (#11)
- fix: add a compat shim for dot_stuffing on Haraka < 3.1, fixes #10
1 parent d147847 commit c05b246

File tree

5 files changed

+74
-12
lines changed

5 files changed

+74
-12
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
44

55
### Unreleased
66

7+
### [1.3.1] - 2025-07-23
8+
9+
- fix: add a compat shim for dot_stuffing on Haraka < 3.1
10+
711
### [1.3.0] - 2025-06-27
812

913
- fix: also remove dot-stuffing from leftovers #9
@@ -56,3 +60,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
5660
[1.2.3]: https://github.com/haraka/message-stream/releases/tag/v1.2.3
5761
[1.0.0]: https://github.com/haraka/message-stream/releases/tag/v1.0.0
5862
[1.3.0]: https://github.com/haraka/message-stream/releases/tag/v1.3.0
63+
[1.3.1]: https://github.com/haraka/message-stream/releases/tag/v1.3.1

CONTRIBUTORS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This handcrafted artisanal software is brought to you by:
44

5-
| <img height="80" src="https://avatars.githubusercontent.com/u/261635?v=4"><br><a href="https://github.com/msimerson">msimerson</a> (<a href="https://github.com/haraka/message-stream/commits?author=msimerson">7</a>) |
5+
| <img height="80" src="https://avatars.githubusercontent.com/u/261635?v=4"><br><a href="https://github.com/msimerson">msimerson</a> (<a href="https://github.com/haraka/message-stream/commits?author=msimerson">8</a>) |
66
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
77

88
<sub>this file is generated by [.release](https://github.com/msimerson/.release).

index.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,16 @@ class MessageStream extends Stream {
337337
Stream.prototype.pipe.call(this, destination, options)
338338
// Options
339339
this.line_endings = options?.line_endings ?? '\r\n'
340-
this.dot_stuffed = options?.dot_stuffed ?? true
340+
if (
341+
options.dot_stuffed === undefined &&
342+
options.dot_stuffing !== undefined
343+
) {
344+
// sunset: delete this if block, leaving only the else when Haraka < 3.1 is
345+
// no longer supported (2026-?)
346+
this.dot_stuffed = !options.dot_stuffing
347+
} else {
348+
this.dot_stuffed = options?.dot_stuffed ?? true
349+
}
341350
this.ending_dot = options?.ending_dot ?? false
342351
this.clamd_style = !!options?.clamd_style
343352
this.buffer_size = options?.buffer_size ?? 1024 * 64

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "haraka-message-stream",
3-
"version": "1.3.0",
3+
"version": "1.3.1",
44
"description": "Haraka email message stream",
55
"main": "index.js",
66
"files": [
@@ -32,7 +32,7 @@
3232
},
3333
"homepage": "https://github.com/haraka/message-stream#readme",
3434
"devDependencies": {
35-
"@haraka/eslint-config": "^2.0.3",
35+
"@haraka/eslint-config": "^2.0.2",
3636
"haraka-test-fixtures": "^1.3.10"
3737
},
3838
"dependencies": {},

test/message-stream.js

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,31 +25,56 @@ describe('message-stream', () => {
2525
})
2626
})
2727

28-
function getOutputFromStream(inputLines) {
28+
function getOutputFromStream(inputLines, pipeOpts) {
2929
return new Promise((resolve) => {
3030
const ms = new MessageStream({ main: {} }, 'msg', [])
3131
const output = new stream.PassThrough()
3232
const chunks = []
3333

34-
output.on('data', chunk => chunks.push(chunk.toString()))
34+
output.on('data', (chunk) => chunks.push(chunk.toString()))
3535
output.on('end', () => resolve(chunks.join('')))
3636

37-
ms.pipe(output, { dot_stuffed: true })
37+
ms.pipe(output, pipeOpts)
3838

39-
inputLines.forEach(line => ms.add_line(line))
39+
inputLines.forEach((line) => ms.add_line(line))
4040
ms.add_line_end()
4141
})
4242
}
4343

44-
describe('dot-unstuffing', function () {
44+
describe('dot_stuffed = false', function () {
45+
const pipeOpts = { dot_stuffed: false }
46+
47+
it('does not stuff "..\\r\\n', async () => {
48+
const result = await getOutputFromStream(['..\r\n'], pipeOpts)
49+
assert.match(result, /^..\r\n/m)
50+
})
51+
52+
it('does not stuff "..dot start\\r\\n"', async () => {
53+
const result = await getOutputFromStream(['..dot start\r\n'], pipeOpts)
54+
assert.match(result, /^..dot start\r\n/m)
55+
})
56+
57+
it('leaves normal lines untouched', async () => {
58+
const result = await getOutputFromStream([
59+
'hello\r\n',
60+
'..dot line\r\n',
61+
'..\r\n',
62+
], pipeOpts)
63+
64+
assert.equal(result, 'hello\r\n..dot line\r\n..\r\n')
65+
})
66+
})
67+
68+
describe('dot_stuffed = true', function () {
69+
const pipeOpts = { dot_stuffed: true }
4570

4671
it('unstuffs "..\\r\\n" to ".\\r\\n"', async () => {
47-
const result = await getOutputFromStream(['..\r\n'])
72+
const result = await getOutputFromStream(['..\r\n'], pipeOpts)
4873
assert.match(result, /^.\r\n/m)
4974
})
5075

5176
it('unstuffs "..dot start\\r\\n" to ".dot start\\r\\n"', async () => {
52-
const result = await getOutputFromStream(['..dot start\r\n'])
77+
const result = await getOutputFromStream(['..dot start\r\n'], pipeOpts)
5378
assert.match(result, /^.dot start\r\n/m)
5479
})
5580

@@ -58,13 +83,36 @@ describe('dot-unstuffing', function () {
5883
'hello\r\n',
5984
'..dot line\r\n',
6085
'..\r\n',
61-
])
86+
], pipeOpts)
6287

6388
assert.equal(result, 'hello\r\n.dot line\r\n.\r\n')
6489
assert.match(result, /^hello\r\n/m)
6590
assert.match(result, /^.dot line\r\n/m)
6691
assert.match(result, /^.\r\n/m)
6792
})
93+
})
6894

95+
describe('dot_stuffing = false (legacy)', function () {
96+
// sunset, delete after 2026
97+
const pipeOpts = { dot_stuffing: false }
6998

99+
it('unstuffs "..\\r\\n" to ".\\r\\n"', async () => {
100+
const result = await getOutputFromStream(['..\r\n'], pipeOpts)
101+
assert.match(result, /^.\r\n/m)
102+
})
103+
104+
it('unstuffs "..dot start\\r\\n" to ".dot start\\r\\n"', async () => {
105+
const result = await getOutputFromStream(['..dot start\r\n'], pipeOpts)
106+
assert.match(result, /^.dot start\r\n/m)
107+
})
108+
109+
it('leaves normal lines untouched', async () => {
110+
const result = await getOutputFromStream([
111+
'hello\r\n',
112+
'..dot line\r\n',
113+
'..\r\n',
114+
], pipeOpts)
115+
116+
assert.equal(result, 'hello\r\n.dot line\r\n.\r\n')
117+
})
70118
})

0 commit comments

Comments
 (0)