• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

haraka / Haraka / 26860023436

03 Jun 2026 02:27AM UTC coverage: 72.488% (-0.006%) from 72.494%
26860023436

push

github

web-flow
dep(eslint): update @haraka/eslint-config to v3, fix surfaced warnings (#3586)

1721 of 2268 branches covered (75.88%)

33 of 45 new or added lines in 15 files covered. (73.33%)

19 existing lines in 3 files now uncovered.

7807 of 10770 relevant lines covered (72.49%)

25.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.31
/plugins/queue/qmail-queue.js
1
// Queue to qmail-queue
8✔
2

8✔
3
const childproc = require('node:child_process')
8✔
4
const fs = require('node:fs')
8✔
5

8✔
6
exports.register = function () {
8✔
7
    this.queue_exec = this.config.get('qmail-queue.path') || '/var/qmail/bin/qmail-queue'
1✔
8
    if (!fs.existsSync(this.queue_exec)) {
1✔
9
        throw new Error(`Cannot find qmail-queue binary (${this.queue_exec})`)
1✔
10
    }
1✔
11

1✔
12
    this.load_qmail_queue_ini()
1!
13

×
14
    if (this.cfg.main.enable_outbound) {
×
15
        this.register_hook('queue_outbound', 'hook_queue')
×
16
    }
×
17
}
×
18

×
19
exports.load_qmail_queue_ini = function () {
8✔
20
    this.cfg = this.config.get(
4✔
21
        'qmail-queue.ini',
4✔
22
        {
4✔
23
            booleans: ['+main.enable_outbound'],
4✔
24
        },
4✔
25
        () => {
4✔
26
            this.load_qmail_queue_ini()
4✔
27
        },
×
28
    )
×
29
}
×
30

×
31
// qmail-queue envelope: F<sender>\0 (T<rcpt>\0)* \0
8✔
32
// Built dynamically, sized to exactly the bytes needed.
8✔
33
//   doesn't emit zero padding after the terminating NUL.
8✔
34
//   encodes non-ASCII (SMTPUTF8) addresses correctly
8✔
35
exports.build_envelope = function (transaction) {
8✔
36
    const NUL = Buffer.from([0])
3✔
37
    const parts = [Buffer.from('F'), Buffer.from(transaction.mail_from.address), NUL]
3✔
38
    for (const rcpt of transaction.rcpt_to) {
3✔
39
        parts.push(Buffer.from('T'), Buffer.from(rcpt.address), NUL)
3✔
40
    }
503✔
41
    parts.push(NUL)
3✔
42
    return Buffer.concat(parts)
3✔
43
}
3✔
44

3✔
45
exports.hook_queue = function (next, connection) {
8✔
46
    const plugin = this
3✔
47

3✔
48
    const txn = connection?.transaction
3✔
49
    if (!txn) return next()
3✔
50

1✔
51
    const q_wants = txn.notes.get('queue.wants')
3✔
52
    if (q_wants && q_wants !== 'qmail-queue') return next()
3✔
53

2✔
54
    const qmail_queue = childproc.spawn(
3!
55
        this.queue_exec, // process name
×
56
        [], // arguments
×
57
        { stdio: ['pipe', 'pipe', process.stderr] },
×
58
    )
×
59

×
60
    qmail_queue.on('exit', function finished(code) {
×
61
        if (code !== 0) {
×
62
            connection.logerror(plugin, `Unable to queue message to qmail-queue: ${code}`)
×
63
            next()
×
64
        } else {
×
65
            next(OK, 'Queued!')
×
66
        }
×
67
    })
×
68

×
69
    connection.transaction.message_stream.pipe(qmail_queue.stdin, {
×
70
        line_endings: '\n',
×
71
    })
×
72

×
73
    qmail_queue.stdin.on('close', () => {
×
74
        if (!connection?.transaction) {
×
75
            plugin.logerror('Transaction went away while delivering mail to qmail-queue')
×
76
            try {
×
77
                qmail_queue.stdout.end()
×
78
            } catch (err) {
×
79
                if (err.code !== 'ENOTCONN') {
×
80
                    // Ignore ENOTCONN and re throw anything else
×
81
                    throw err
×
82
                }
×
83
            }
×
84

×
85
            connection.results.add(plugin, { err: 'dead sender' })
×
86
            return
×
87
        }
×
88
        plugin.loginfo('Message Stream sent to qmail. Now sending envelope')
×
89
        const buf = plugin.build_envelope(connection.transaction)
×
NEW
90
        qmail_queue.stdout.on('error', () => {}) // stdout throws an error on close
×
91
        qmail_queue.stdout.end(buf)
×
92
    })
×
93
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc