From 1446f448ea41e564f4f52826ebae862904595a4f Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Tue, 7 May 2024 16:36:36 -0700 Subject: [PATCH] Release v1.1.0 (#23) - add/update DSNs from RFCs 3886, 4468, 4954 - doc(README): improve formatting, add RFCs to Reference - doc(CONTRIBUTORS): added --- .eslintrc.json | 14 +- .github/dependabot.yml | 6 +- .github/workflows/ci.yml | 35 +--- .github/workflows/codeql.yml | 6 +- .github/workflows/publish.yml | 2 +- .prettierignore | 1 + .prettierrc.yml | 2 + .release | 2 +- Changes.md => CHANGELOG.md | 19 +- CONTRIBUTORS.md | 8 + README.md | 35 ++-- index.js | 351 +++++++++++++++++----------------- package.json | 20 +- test/index.js | 118 +++++++----- 14 files changed, 325 insertions(+), 294 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.yml rename Changes.md => CHANGELOG.md (58%) create mode 100644 CONTRIBUTORS.md diff --git a/.eslintrc.json b/.eslintrc.json index b0df879..c8cc95e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,16 +2,8 @@ "env": { "node": true, "es6": true, - "mocha": true + "mocha": true, + "es2022": true }, - "plugins": [ - "haraka" - ], - "extends": [ "eslint:recommended", "plugin:haraka/recommended" ], - "root": true, - "rules": { - "indent": [2, 4, {"SwitchCase": 1}], - "console": 0, - "no-console": 0 - } + "extends": ["@haraka"] } diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0449e4a..d450132 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,9 +2,9 @@ version: 2 updates: - - package-ecosystem: "npm" - directory: "/" + - package-ecosystem: 'npm' + directory: '/' schedule: - interval: "weekly" + interval: 'weekly' allow: - dependency-type: production diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae87d28..9475589 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,11 @@ name: CI -on: [ push, pull_request ] +on: [push, pull_request] env: CI: true jobs: - lint: uses: haraka/.github/.github/workflows/lint.yml@master @@ -15,27 +14,13 @@ jobs: secrets: inherit test: - needs: [ lint, get-lts ] - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-latest, windows-latest ] - node-version: ${{ fromJson(needs.get-lts.outputs.active) }} - fail-fast: false - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - name: Node ${{ matrix.node-version }} on ${{ matrix.os }} - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - run: npm test + needs: [lint] + uses: haraka/.github/.github/workflows/ubuntu.yml@master + + windows: + needs: [lint] + uses: haraka/.github/.github/workflows/windows.yml@master - get-lts: - runs-on: ubuntu-latest - steps: - - id: get - uses: msimerson/node-lts-versions@v1 - outputs: - active: ${{ steps.get.outputs.active }} - lts: ${{ steps.get.outputs.lts }} + macos: + needs: [lint] + uses: haraka/.github/.github/workflows/macos.yml@master diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 383aca2..816e8c3 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,10 +1,10 @@ -name: "CodeQL" +name: 'CodeQL' on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] schedule: - cron: '18 7 * * 4' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 42a9bb9..d97a994 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,4 +11,4 @@ env: jobs: publish: uses: haraka/.github/.github/workflows/publish.yml@master - secrets: inherit \ No newline at end of file + secrets: inherit diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..012a3cd --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +index.js diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000..8ded5e0 --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,2 @@ +singleQuote: true +semi: false diff --git a/.release b/.release index 954197d..afb1db8 160000 --- a/.release +++ b/.release @@ -1 +1 @@ -Subproject commit 954197dae07b32c4476ff87ec9ae7371311ec97d +Subproject commit afb1db801607dda5e859f39b600f0dd0111e4651 diff --git a/Changes.md b/CHANGELOG.md similarity index 58% rename from Changes.md rename to CHANGELOG.md index ed797e7..7290be8 100644 --- a/Changes.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ +# Changelog + +The format is based on [Keep a Changelog](https://keepachangelog.com/). ### Unreleased +### [1.1.0] - 2024-05-07 + +- add/update DSNs from RFCs 3886, 4468, 4954 +- doc(README): improve formatting, add RFCs to Reference +- doc(CONTRIBUTORS): added +- dep: eslint-plugin-haraka -> @haraka/eslint-config +- chore(ci): updated to latest shared GHA configs + ### 1.0.5 - 2024-04-04 - dev: only install mocha/eslint with npx, on demand @@ -8,14 +19,12 @@ - test: remove useless use of done - ci: add on.pull_request - ### 1.0.4 - 2022-06-05 - ci: update with shared GHA workflows - ci: add node 18 testing - ci: replace travis & appveyor with github actions - ### 1.0.3 - 2020-01-17 - Use es6 classes on full - static members instead of old style `exports.stuff` @@ -25,14 +34,14 @@ - Fix execution rights on run_tests #2 - Replaced concatenated strings with template literals #1 - ### 1.0.2 - 2017-09-11 - es6: use arrow functions - ### 1.0.1 - 2017-09-04 - initial release - +[1.0.4]: https://github.com/haraka/haraka-dsn/releases/tag/v1.0.4 +[1.0.5]: https://github.com/haraka/haraka-dsn/releases/tag/v1.0.5 +[1.1.0]: https://github.com/haraka/haraka-dsn/releases/tag/v1.1.0 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..08a2dc1 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,8 @@ +# Contributors + +This handcrafted artisinal software is brought to you by: + +|
msimerson (26) |
PSSGCSim (2) | +| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | + +this file is maintained by [.release](https://github.com/msimerson/.release) diff --git a/README.md b/README.md index c999362..01dc6e9 100644 --- a/README.md +++ b/README.md @@ -11,36 +11,41 @@ Plugins return constants to Haraka to specify how to respond to clients for a pa These constants include DENY, DENYSOFT and DENYDISCONNECT which automatically output an appropriate SMTP status code (e.g. 450 temporary failures for DENYSOFT or 550 permanent failures for DENY or DENYDISCONNECT) in the SMTP response sent back to the client along with an optional message text that you return as arguments to the next() function. -The [DSN](https://github.com/haraka/haraka-dsn) allows a plugin to return RFC 3463 compliant extended status codes and allows plugins to override the SMTP status code returned by Haraka. - -The DSN module exposes a callable function for each status condition defined in RFC 3463. - -Please refer to [RFC 3463](http://tools.ietf.org/html/rfc3463) for further information and background. +The [DSN](https://github.com/haraka/haraka-dsn) allows a plugin to return RFC compliant extended status codes and allows plugins to override the SMTP status code returned by Haraka. +The DSN module exposes a callable function for each status condition defined in the RFCs. See the References section below for further information and background. ### Using the DSN module in your plugin Load the DSN module by adding the following line to the top of your plugin: - const DSN = require('haraka-dsn'); +```js +const DSN = require('haraka-dsn') +``` Then instead of: - return next(DENY, 'No such user'); +```js +next(DENY, 'No such user') +``` You can call the DSN module and return that in place of optional message argument of next(): - return next(DENY, DSN.no_such_user()); +```js +next(DENY, DSN.no_such_user()) +``` This will cause Haraka to return the following back to the client: - 550 5.1.1 No such user + `550 5.1.1 No such user` The DSN functions are used like this: - DSN.([message], [code]); +```js +DSN.([message], [code]); +``` -The function name is required and maps to the list of defined status codes in RFC 3463. All of the available functions are detailed in the table below. +The function name is required and maps to the list of defined status codes in RFC 3463. All of the available functions are detailed in the table below. [message] is optional and should contain the message that you would like to be returned to the client, this value can be a string or an array which can contain multiple elements which will cause a multi-line reply to be sent to the client. If a message is not supplied, then the default message for the DSN function is used. @@ -408,8 +413,16 @@ The function name is required and maps to the list of defined status codes in RF +### References + +- [RFC 1893](https://www.rfc-editor.org/rfc/rfc1893) +- [RFC 3463](http://tools.ietf.org/html/rfc3463) +- [RFC 3886](https://www.rfc-editor.org/rfc/rfc3886#section-3.3.4) +- [RFC 4468](https://www.rfc-editor.org/rfc/rfc4468#section-5) +- [RFC 4954](https://www.rfc-editor.org/rfc/rfc4954#section-6) + [ci-img]: https://github.com/haraka/haraka-dsn/actions/workflows/ci.yml/badge.svg [ci-url]: https://github.com/haraka/haraka-dsn/actions/workflows/ci.yml [cov-img]: https://codecov.io/github/haraka/haraka-dsn/coverage.svg diff --git a/index.js b/index.js index 0f552f7..9018070 100644 --- a/index.js +++ b/index.js @@ -1,184 +1,189 @@ 'use strict' -// RFC 3463 Enhanced Status Codes +// Enhanced Status Codes, see RFCs in README#References const enum_status_codes = [ - [ // X.0.XXX Other or Undefined Status (unspecified) - "Other undefined status", // X.0.0 - ], - [ // X.1.XXX Addressing Status (addr_*) - "Other address status", // X.1.0 - "Bad destination mailbox address", // X.1.1 - "Bad destination system address", // X.1.2 - "Bad destination mailbox address syntax", // X.1.3 - "Destination mailbox address ambiguous", // X.1.4 - "Destination address valid", // X.1.5 - "Destination mailbox has moved, No forwarding address", // X.1.6 - "Bad sender's mailbox address syntax", // X.1.7 - "Bad sender's system address", // X.1.8 - ], - [ // X.2.XXX Mailbox Status (mbox_*) - "Other or undefined mailbox status", // X.2.0 - "Mailbox disabled, not accepting messages", // X.2.1 - "Mailbox full", // X.2.2 - "Message length exceeds administrative limit", // X.2.3 - "Mailing list expansion problem", // X.2.4 - ], - [ // X.3.XXX Mail System Status (sys_*) - "Other or undefined mail system status", // X.3.0 - "Mail system full", // X.3.1 - "System not accepting network messages", // X.3.2 - "System not capable of selected features", // X.3.3 - "Message too big for system", // X.3.4 - "System incorrectly configured", // X.3.5 - ], - [ // X.4.XXX Network and Routing Status (net_*) - "Other or undefined network or routing status", // X.4.0 - "No answer from host", // X.4.1 - "Bad connection", // X.4.2 - "Directory server failure", // X.4.3 - "Unable to route", // X.4.4 - "Mail system congestion", // X.4.5 - "Routing loop detected", // X.4.6 - "Delivery time expired", // X.4.7 - ], - [ // X.5.XXX Mail Delivery Protocol Status (proto_*) - "Other or undefined protocol status", // X.5.0 - "Invalid command", // X.5.1 - "Syntax error", // X.5.2 - "Too many recipients", // X.5.3 - "Invalid command arguments", // X.5.4 - "Wrong protocol version", // X.5.5 - "Authentication Exchange line is too long", // X.5.6 - ], - [ // X.6.XXX Message Content or Media Status (media_*) - "Other or undefined media error", // X.6.0 - "Media not supported", // X.6.1 - "Conversion required and prohibited", // X.6.2 - "Conversion required but not supported", // X.6.3 - "Conversion with loss performed", // X.6.4 - "Conversion failed", // X.6.5 - ], - [ // X.7.XXX Security or Policy Status (sec_*) - "Other or undefined security status", // X.7.0 - "Delivery not authorized, message refused", // X.7.1 - "Mailing list expansion prohibited", // X.7.2 - "Security conversion required but not possible", // X.7.3 - "Security features not supported", // X.7.4 - "Cryptographic failure", // X.7.5 - "Cryptographic algorithm not supported", // X.7.6 - "Message integrity failure", // X.7.7 - "Authentication credentials invalid", // X.7.8 - "Authentication mechanism is too weak", // X.7.9 - "", // X.7.10 - "Encryption required for requested authentication mechanism", // X.7.11 - ] + [ // X.0.XXX Other or Undefined Status (unspecified) + "Other undefined status", // X.0.0 + ], + [ // X.1.XXX Addressing Status (addr_*) + "Other address status", // X.1.0 + "Bad destination mailbox address", // X.1.1 + "Bad destination system address", // X.1.2 + "Bad destination mailbox address syntax", // X.1.3 + "Destination mailbox address ambiguous", // X.1.4 + "Destination address valid", // X.1.5 + "Destination mailbox has moved, No forwarding address", // X.1.6 + "Bad sender's mailbox address syntax", // X.1.7 + "Bad sender's system address", // X.1.8 + "Message relayed to non-compliant mailer", // X.1.9 + ], + [ // X.2.XXX Mailbox Status (mbox_*) + "Other or undefined mailbox status", // X.2.0 + "Mailbox disabled, not accepting messages", // X.2.1 + "Mailbox full", // X.2.2 + "Message length exceeds administrative limit", // X.2.3 + "Mailing list expansion problem", // X.2.4 + "" + ], + [ // X.3.XXX Mail System Status (sys_*) + "Other or undefined mail system status", // X.3.0 + "Mail system full", // X.3.1 + "System not accepting network messages", // X.3.2 + "System not capable of selected features", // X.3.3 + "Message too big for system", // X.3.4 + "System incorrectly configured", // X.3.5 + ], + [ // X.4.XXX Network and Routing Status (net_*) + "Other or undefined network or routing status", // X.4.0 + "No answer from host", // X.4.1 + "Bad connection", // X.4.2 + "Directory server failure", // X.4.3 + "Unable to route", // X.4.4 + "Mail system congestion", // X.4.5 + "Routing loop detected", // X.4.6 + "Delivery time expired", // X.4.7 + ], + [ // X.5.XXX Mail Delivery Protocol Status (proto_*) + "Other or undefined protocol status", // X.5.0 + "Invalid command", // X.5.1 + "Syntax error", // X.5.2 + "Too many recipients", // X.5.3 + "Invalid command arguments", // X.5.4 + "Wrong protocol version", // X.5.5 + "Authentication Exchange line is too long", // X.5.6 + ], + [ // X.6.XXX Message Content or Media Status (media_*) + "Other or undefined media error", // X.6.0 + "Media not supported", // X.6.1 + "Conversion required and prohibited", // X.6.2 + "Conversion required but not supported", // X.6.3 + "Conversion with loss performed", // X.6.4 + "Conversion failed", // X.6.5 + "Message content not available", // X.6.6 + ], + [ // X.7.XXX Security or Policy Status (sec_*) + "Authentication Succeeded", // X.7.0 + "Delivery not authorized, message refused", // X.7.1 + "Mailing list expansion prohibited", // X.7.2 + "Security conversion required but not possible", // X.7.3 + "Security features not supported", // X.7.4 + "Cryptographic failure", // X.7.5 + "Cryptographic algorithm not supported", // X.7.6 + "Message integrity failure", // X.7.7 + "Authentication credentials invalid", // X.7.8 + "Authentication mechanism is too weak", // X.7.9 + "Encryption Needed", // X.7.10 + "Encryption required for requested authentication mechanism", // X.7.11 + "A password transition is needed", // X.7.12 + "User Account Disabled", // X.7.13 + "Trust relationship required", // X.7.14 + ] ]; class DSN { - constructor (code, msg, def, subject, detail) { - this.code = (/^[245]\d{2}/.exec(code)) ? code : null || def || 450; - this.msg = msg; - this.cls = parseInt(new String(this.code)[0]); - this.sub = subject || 0; - this.det = detail || 0; - this.default_msg = ((enum_status_codes[this.sub]) ? enum_status_codes[this.sub][this.det] : '') || ''; - - // multi-line replies - if (Array.isArray(this.msg)) { - this.reply = []; - let m; - while ((m = this.msg.shift())) { - this.reply.push(`${[this.cls, this.sub, this.det].join('.')} ${m}`); - } - return - } - - this.reply = `${[this.cls, this.sub, this.det].join('.')} ${this.msg || this.default_msg}`; - } - - static create (code, msg, subject, detail) { - return new DSN(code, msg, null, subject, detail); + constructor (code, msg, def, subject, detail) { + this.code = (/^[245]\d{2}/.exec(code)) ? code : null || def || 450; + this.msg = msg; + this.cls = parseInt(new String(this.code)[0]); + this.sub = subject || 0; + this.det = detail || 0; + this.default_msg = ((enum_status_codes[this.sub]) ? enum_status_codes[this.sub][this.det] : '') || ''; + + // multi-line replies + if (Array.isArray(this.msg)) { + this.reply = []; + let m; + while ((m = this.msg.shift())) { + this.reply.push(`${[this.cls, this.sub, this.det].join('.')} ${m}`); + } + return } - static unspecified (msg, code) { return new DSN(code, msg, 450, 0, 0); } - - // addr_* - static addr_unspecified (msg, code) { return new DSN(code, msg, 450, 1, 0); } - static addr_bad_dest_mbox (msg, code) { return new DSN(code, msg, 550, 1, 1); } - static no_such_user (msg, code) { return new DSN(code, msg || 'No such user', 550, 1, 1); } - static addr_bad_dest_system (msg, code) { return new DSN(code, msg, 550, 1, 2); } - static addr_bad_dest_syntax (msg, code) { return new DSN(code, msg, 550, 1, 3); } - static addr_dest_ambigous (msg, code) { return new DSN(code, msg, 450, 1, 4); } - static addr_rcpt_ok (msg, code) { return new DSN(code, msg, 250, 1, 5); } - static addr_mbox_moved (msg, code) { return new DSN(code, msg, 550, 1, 6); } - static addr_bad_from_syntax (msg, code) { return new DSN(code, msg, 550, 1, 7); } - static addr_bad_from_system (msg, code) { return new DSN(code, msg, 550, 1, 8); } - - // mbox_* - static mbox_unspecified (msg, code) { return new DSN(code, msg, 450, 2, 0); } - static mbox_disabled (msg, code) { return new DSN(code, msg, 550, 2, 1); } - static mbox_full (msg, code) { return new DSN(code, msg, 450, 2, 2); } - static mbox_msg_too_long (msg, code) { return new DSN(code, msg, 550, 2, 3); } - static mbox_list_expansion_problem (msg, code) { return new DSN(code, msg, 450, 2, 4); } - - // sys_* - static sys_unspecified (msg, code) { return new DSN(code, msg, 450, 3, 0); } - static sys_disk_full (msg, code) { return new DSN(code, msg, 450, 3, 1); } - static sys_not_accepting_mail (msg, code) { return new DSN(code, msg, 450, 3, 2); } - static sys_not_supported (msg, code) { return new DSN(code, msg, 450, 3, 3); } - static sys_msg_too_big (msg, code) { return new DSN(code, msg, 550, 3, 4); } - static sys_incorrectly_configured (msg, code) { return new DSN(code, msg, 450, 3, 5); } - - // net_* - static net_unspecified (msg, code) { return new DSN(code, msg, 450, 4, 0); } - static net_no_answer (msg, code) { return new DSN(code, msg, 450, 4, 1); } - static net_bad_connection (msg, code) { return new DSN(code, msg, 450, 4, 2); } - static net_directory_server_failed (msg, code) { return new DSN(code, msg, 450, 4, 3); } - static temp_resolver_failed (msg, code) { return new DSN(code, msg || 'Temporary address resolution failure', 450, 4, 3); } - static net_unable_to_route (msg, code) { return new DSN(code, msg, 550, 4, 4); } - static net_system_congested (msg, code) { return new DSN(code, msg, 450, 4, 5); } - static net_routing_loop (msg, code) { return new DSN(code, msg, 550, 4, 6); } - static too_many_hops (msg, code) { return new DSN(code, msg || 'Too many hops', 550, 4, 6); } - static net_delivery_time_expired (msg, code) { return new DSN(code, msg, 550, 4, 7); } - - // proto_* - static proto_unspecified (msg, code) { return new DSN(code, msg, 450, 5, 0); } - static proto_invalid_command (msg, code) { return new DSN(code, msg, 550, 5, 1); } - static proto_syntax_error (msg, code) { return new DSN(code, msg, 550, 5, 2); } - static proto_too_many_rcpts (msg, code) { return new DSN(code, msg, 450, 5, 3); } - static proto_invalid_cmd_args (msg, code) { return new DSN(code, msg, 550, 5, 4); } - static proto_wrong_version (msg, code) { return new DSN(code, msg, 450, 5, 5); } - - // media_* - static media_unspecified (msg, code) { return new DSN(code, msg, 450, 6, 0); } - static media_unsupported (msg, code) { return new DSN(code, msg, 550, 6, 1); } - static media_conv_prohibited (msg, code) { return new DSN(code, msg, 550, 6, 2); } - static media_conv_unsupported (msg, code) { return new DSN(code, msg, 450, 6, 3); } - static media_conv_lossy (msg, code) { return new DSN(code, msg, 450, 6, 4); } - static media_conv_failed (msg, code) { return new DSN(code, msg, 450, 6, 5); } - - // sec_* - static sec_unspecified (msg, code) { return new DSN(code, msg, 450, 7, 0); } - static sec_unauthorized (msg, code) { return new DSN(code, msg, 550, 7, 1); } - static bad_sender_ip (msg, code) { return new DSN(code, msg || 'Bad sender IP', 550, 7, 1); } - static relaying_denied (msg, code) { return new DSN(code, msg || 'Relaying denied', 550, 7, 1); } - static sec_list_expn_prohibited (msg, code) { return new DSN(code, msg, 550, 7, 2); } - static sec_conv_failed (msg, code) { return new DSN(code, msg, 550, 7, 3); } - static sec_feature_unsupported (msg, code) { return new DSN(code, msg, 550, 7, 4); } - static sec_crypto_failure (msg, code) { return new DSN(code, msg, 550, 7, 5); } - static sec_crypto_algo_unsupported (msg, code) { return new DSN(code, msg, 450, 7, 6); } - static sec_msg_integrity_failure (msg, code) { return new DSN(code, msg, 550, 7, 7); } - - // RFC4954 - static auth_succeeded (msg, code) { return new DSN(code, msg || 'Authentication Succeeded', 235, 7, 0); } - static auth_pass_transition_needed (msg, code) { return new DSN(code, msg || 'A password transition is needed', 432, 7, 12); } - static auth_temp_fail (msg, code) { return new DSN(code, msg || 'Temporary authentication failure', 454, 7, 0); } - static auth_too_weak (msg, code) { return new DSN(code, msg, 534, 7, 9); } - static auth_invalid (msg, code) { return new DSN(code, msg, 535, 7, 8); } - static auth_exch_too_long (msg, code) { return new DSN(code, msg, 500, 5, 6)} - static auth_required (msg, code) { return new DSN(code, msg || 'Authentication required', 530, 7, 0); } - static auth_crypt_required (msg, code) { return new DSN(code, msg, 538, 7, 11); } - + this.reply = `${[this.cls, this.sub, this.det].join('.')} ${this.msg || this.default_msg}`; + } + + static create (code, msg, subject, detail) { + return new DSN(code, msg, null, subject, detail); + } + + static unspecified (msg, code) { return new DSN(code, msg, 450, 0, 0); } + + // addr_* + static addr_unspecified (msg, code) { return new DSN(code, msg, 450, 1, 0); } + static addr_bad_dest_mbox (msg, code) { return new DSN(code, msg, 550, 1, 1); } + static no_such_user (msg, code) { return new DSN(code, msg || 'No such user', 550, 1, 1); } + static addr_bad_dest_system (msg, code) { return new DSN(code, msg, 550, 1, 2); } + static addr_bad_dest_syntax (msg, code) { return new DSN(code, msg, 550, 1, 3); } + static addr_dest_ambigous (msg, code) { return new DSN(code, msg, 450, 1, 4); } + static addr_rcpt_ok (msg, code) { return new DSN(code, msg, 250, 1, 5); } + static addr_mbox_moved (msg, code) { return new DSN(code, msg, 550, 1, 6); } + static addr_bad_from_syntax (msg, code) { return new DSN(code, msg, 550, 1, 7); } + static addr_bad_from_system (msg, code) { return new DSN(code, msg, 550, 1, 8); } + + // mbox_* + static mbox_unspecified (msg, code) { return new DSN(code, msg, 450, 2, 0); } + static mbox_disabled (msg, code) { return new DSN(code, msg, 550, 2, 1); } + static mbox_full (msg, code) { return new DSN(code, msg, 450, 2, 2); } + static mbox_msg_too_long (msg, code) { return new DSN(code, msg, 550, 2, 3); } + static mbox_list_expansion_problem (msg, code) { return new DSN(code, msg, 450, 2, 4); } + + // sys_* + static sys_unspecified (msg, code) { return new DSN(code, msg, 450, 3, 0); } + static sys_disk_full (msg, code) { return new DSN(code, msg, 450, 3, 1); } + static sys_not_accepting_mail (msg, code) { return new DSN(code, msg, 450, 3, 2); } + static sys_not_supported (msg, code) { return new DSN(code, msg, 450, 3, 3); } + static sys_msg_too_big (msg, code) { return new DSN(code, msg, 550, 3, 4); } + static sys_incorrectly_configured (msg, code) { return new DSN(code, msg, 450, 3, 5); } + + // net_* + static net_unspecified (msg, code) { return new DSN(code, msg, 450, 4, 0); } + static net_no_answer (msg, code) { return new DSN(code, msg, 450, 4, 1); } + static net_bad_connection (msg, code) { return new DSN(code, msg, 450, 4, 2); } + static net_directory_server_failed (msg, code) { return new DSN(code, msg, 450, 4, 3); } + static temp_resolver_failed (msg, code) { return new DSN(code, msg || 'Temporary address resolution failure', 450, 4, 3); } + static net_unable_to_route (msg, code) { return new DSN(code, msg, 550, 4, 4); } + static net_system_congested (msg, code) { return new DSN(code, msg, 450, 4, 5); } + static net_routing_loop (msg, code) { return new DSN(code, msg, 550, 4, 6); } + static too_many_hops (msg, code) { return new DSN(code, msg || 'Too many hops', 550, 4, 6); } + static net_delivery_time_expired (msg, code) { return new DSN(code, msg, 550, 4, 7); } + + // proto_* + static proto_unspecified (msg, code) { return new DSN(code, msg, 450, 5, 0); } + static proto_invalid_command (msg, code) { return new DSN(code, msg, 550, 5, 1); } + static proto_syntax_error (msg, code) { return new DSN(code, msg, 550, 5, 2); } + static proto_too_many_rcpts (msg, code) { return new DSN(code, msg, 450, 5, 3); } + static proto_invalid_cmd_args (msg, code) { return new DSN(code, msg, 550, 5, 4); } + static proto_wrong_version (msg, code) { return new DSN(code, msg, 450, 5, 5); } + + // media_* + static media_unspecified (msg, code) { return new DSN(code, msg, 450, 6, 0); } + static media_unsupported (msg, code) { return new DSN(code, msg, 550, 6, 1); } + static media_conv_prohibited (msg, code) { return new DSN(code, msg, 550, 6, 2); } + static media_conv_unsupported (msg, code) { return new DSN(code, msg, 450, 6, 3); } + static media_conv_lossy (msg, code) { return new DSN(code, msg, 450, 6, 4); } + static media_conv_failed (msg, code) { return new DSN(code, msg, 450, 6, 5); } + + // sec_* + static sec_unspecified (msg, code) { return new DSN(code, msg, 450, 7, 0); } + static sec_unauthorized (msg, code) { return new DSN(code, msg, 550, 7, 1); } + static bad_sender_ip (msg, code) { return new DSN(code, msg || 'Bad sender IP', 550, 7, 1); } + static relaying_denied (msg, code) { return new DSN(code, msg || 'Relaying denied', 550, 7, 1); } + static sec_list_expn_prohibited (msg, code) { return new DSN(code, msg, 550, 7, 2); } + static sec_conv_failed (msg, code) { return new DSN(code, msg, 550, 7, 3); } + static sec_feature_unsupported (msg, code) { return new DSN(code, msg, 550, 7, 4); } + static sec_crypto_failure (msg, code) { return new DSN(code, msg, 550, 7, 5); } + static sec_crypto_algo_unsupported (msg, code) { return new DSN(code, msg, 450, 7, 6); } + static sec_msg_integrity_failure (msg, code) { return new DSN(code, msg, 550, 7, 7); } + + // RFC4954 + static auth_succeeded (msg, code) { return new DSN(code, msg, 235, 7, 0); } + static auth_pass_transition_needed (msg, code) { return new DSN(code, msg, 432, 7, 12); } + static auth_temp_fail (msg, code) { return new DSN(code, msg || 'Temporary authentication failure', 454, 7, 0); } + static auth_too_weak (msg, code) { return new DSN(code, msg, 534, 7, 9); } + static auth_invalid (msg, code) { return new DSN(code, msg, 535, 7, 8); } + static auth_exch_too_long (msg, code) { return new DSN(code, msg, 500, 5, 6)} + static auth_required (msg, code) { return new DSN(code, msg || 'Authentication required', 530, 7, 0); } + static auth_crypt_required (msg, code) { return new DSN(code, msg, 538, 7, 11); } } module.exports = DSN diff --git a/package.json b/package.json index db00a5d..19467f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haraka-dsn", - "version": "1.0.5", + "version": "1.1.0", "description": "Haraka DSN", "engines": { "node": ">= v14.0.0" @@ -8,15 +8,17 @@ "main": "index.js", "files": [ "Changes.md", - "LICENSE", - "README.md", - "index.js", - "package.json" + "LICENSE" ], "scripts": { + "format": "npm run prettier:fix && npm run lint:fix", "lint": "npx eslint@^8 *.js test", - "lintfix": "npx exlint@^8 --fix *.js test", - "test": "npx mocha@^10" + "lint:fix": "npx eslint@^8 *.js test --fix", + "prettier": "npx prettier . --check", + "prettier:fix": "npx prettier . --write --log-level=warn", + "test": "npx mocha@10", + "versions": "npx dependency-version-checker check", + "versions:fix": "npx dependency-version-checker update && npm run prettier:fix" }, "repository": { "type": "git", @@ -33,8 +35,8 @@ }, "homepage": "https://github.com/haraka/haraka-dsn#readme", "devDependencies": { - "eslint-plugin-haraka": "*", - "haraka-constants": "*" + "@haraka/eslint-config": "^1.0.0", + "haraka-constants": "^1.0.0" }, "dependencies": {} } diff --git a/test/index.js b/test/index.js index 4f13976..a0e5d39 100644 --- a/test/index.js +++ b/test/index.js @@ -1,58 +1,72 @@ - const assert = require('assert') -const constants = require('haraka-constants'); +const constants = require('haraka-constants') -constants.import(global); +constants.import(global) describe('haraka-dsn', function () { - beforeEach(function () { - this.DSN = require('../index') - }) - - it('should load', function () { - assert.ok(this.DSN); - }) - - it('create, only code', function () { - assert.deepEqual({ - code: 200, msg: undefined, cls: 2, sub: 0, det: 0, - default_msg: 'Other undefined status', - reply: '2.0.0 Other undefined status' - }, - this.DSN.create(200) - ) - }) - - it('create, code w/msg', function () { - assert.deepEqual({ - code: 200, msg: 'test msg', cls: 2, sub: 0, det: 0, - default_msg: 'Other undefined status', - reply: '2.0.0 test msg' - }, - this.DSN.create(200, 'test msg') - ) - }) - - it('create, code w/msg & subject', function () { - assert.deepEqual({ - code: 200, msg: 'test msg', cls: 2, sub: 7, det: 0, - default_msg: 'Other or undefined security status', - reply: '2.7.0 test msg' - }, - this.DSN.create(200, 'test msg', 7) - ); - }) - - it('net_unspecified returns expected code & message', function () { - const r = this.DSN.net_unspecified() - assert.equal(r.code, 450) - assert.equal(r.default_msg, 'Other or undefined network or routing status') - }) - - it('net_unable_to_route returns expected code & message', function () { - const r = this.DSN.net_unable_to_route() - assert.equal(r.code, 550) - assert.equal(r.default_msg, 'Unable to route') - }) + beforeEach(function () { + this.DSN = require('../index') + }) + + it('should load', function () { + assert.ok(this.DSN) + }) + + it('create, only code', function () { + assert.deepEqual( + { + code: 200, + msg: undefined, + cls: 2, + sub: 0, + det: 0, + default_msg: 'Other undefined status', + reply: '2.0.0 Other undefined status', + }, + this.DSN.create(200), + ) + }) + + it('create, code w/msg', function () { + assert.deepEqual( + { + code: 200, + msg: 'test msg', + cls: 2, + sub: 0, + det: 0, + default_msg: 'Other undefined status', + reply: '2.0.0 test msg', + }, + this.DSN.create(200, 'test msg'), + ) + }) + + it('create, code w/msg & subject', function () { + assert.deepEqual( + { + code: 200, + msg: 'test msg', + cls: 2, + sub: 7, + det: 0, + default_msg: 'Authentication Succeeded', + reply: '2.7.0 test msg', + }, + this.DSN.create(200, 'test msg', 7), + ) + }) + + it('net_unspecified returns expected code & message', function () { + const r = this.DSN.net_unspecified() + assert.equal(r.code, 450) + assert.equal(r.default_msg, 'Other or undefined network or routing status') + }) + + it('net_unable_to_route returns expected code & message', function () { + const r = this.DSN.net_unable_to_route() + assert.equal(r.code, 550) + assert.equal(r.default_msg, 'Unable to route') + }) })