imapkit 5.1.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +41 -23
  2. package/bin/help.txt +29 -3
  3. package/bin/imapkit.js +43 -0
  4. package/dist/browser/imapkit.js +99 -35
  5. package/dist/cjs/control.d.ts +16 -5
  6. package/dist/cjs/control.js +36 -5
  7. package/dist/cjs/oauth/http.d.ts +28 -0
  8. package/dist/cjs/oauth/http.js +216 -0
  9. package/dist/cjs/oauth/jwt.d.ts +33 -0
  10. package/dist/cjs/oauth/jwt.js +58 -0
  11. package/dist/cjs/oauth/pages.d.ts +47 -0
  12. package/dist/cjs/oauth/pages.js +310 -0
  13. package/dist/cjs/oauth/policy.d.ts +48 -0
  14. package/dist/cjs/oauth/policy.js +66 -0
  15. package/dist/cjs/oauth/provider.d.ts +455 -0
  16. package/dist/cjs/oauth/provider.js +1422 -0
  17. package/dist/cjs/plugins/oauthbearer.js +10 -15
  18. package/dist/cjs/plugins/xoauth2.js +4 -3
  19. package/dist/cjs/rest.d.ts +18 -1
  20. package/dist/cjs/rest.js +63 -35
  21. package/dist/cjs/server.d.ts +49 -1
  22. package/dist/cjs/server.js +126 -12
  23. package/dist/cjs/smtp-listener.d.ts +3 -2
  24. package/dist/cjs/smtp-listener.js +15 -3
  25. package/dist/cjs/types.d.ts +16 -0
  26. package/dist/esm/control.d.ts +16 -5
  27. package/dist/esm/control.js +36 -6
  28. package/dist/esm/oauth/http.d.ts +28 -0
  29. package/dist/esm/oauth/http.js +211 -0
  30. package/dist/esm/oauth/jwt.d.ts +33 -0
  31. package/dist/esm/oauth/jwt.js +50 -0
  32. package/dist/esm/oauth/pages.d.ts +47 -0
  33. package/dist/esm/oauth/pages.js +305 -0
  34. package/dist/esm/oauth/policy.d.ts +48 -0
  35. package/dist/esm/oauth/policy.js +59 -0
  36. package/dist/esm/oauth/provider.d.ts +455 -0
  37. package/dist/esm/oauth/provider.js +1415 -0
  38. package/dist/esm/plugins/oauthbearer.js +10 -15
  39. package/dist/esm/plugins/xoauth2.js +4 -3
  40. package/dist/esm/rest.d.ts +18 -1
  41. package/dist/esm/rest.js +62 -36
  42. package/dist/esm/server.d.ts +49 -1
  43. package/dist/esm/server.js +127 -13
  44. package/dist/esm/smtp-listener.d.ts +3 -2
  45. package/dist/esm/smtp-listener.js +15 -3
  46. package/dist/esm/types.d.ts +16 -0
  47. package/dist/plugin-help.json +2 -2
  48. package/package.json +7 -7
package/README.md CHANGED
@@ -27,7 +27,7 @@ imapkit -p 1143
27
27
 
28
28
  Point your IMAP client to `localhost:1143` and log in with user name `testuser` and password `testpass`. Without `-p` the server listens on port 143 (993 with `--secure`), which usually needs root privileges.
29
29
 
30
- `imapkit --smtpPort=1025` also starts an SMTP server that appends every message it receives to INBOX. SMTP needs the optional [smtp-server](https://www.npmjs.com/package/smtp-server) package, which is not installed with ImapKit: `npm install -g smtp-server` (or `npm install smtp-server` next to a local install).
30
+ `imapkit -p 1143 --tls-port=1993` also listens for implicit TLS (IMAPS) connections on port 1993, both ports serve the same mailboxes. `imapkit --smtpPort=1025` also starts an SMTP server that appends every message it receives to INBOX. SMTP needs the optional [smtp-server](https://www.npmjs.com/package/smtp-server) package, which is not installed with ImapKit: `npm install -g smtp-server` (or `npm install smtp-server` next to a local install).
31
31
 
32
32
  Run `imapkit --help` to see all command line options, the environment variables (`IMAPKIT_PORT`, `IMAPKIT_PLUGINS`, ...) and sample configuration data. For example, `imapkit -p 1143 --plugin=IDLE,MOVE,CONDSTORE --storage=storage.json` loads three plugins and the mailboxes of `storage.json`.
33
33
 
@@ -142,6 +142,21 @@ ImapKit follows these of the strategies that RFC 2180 (IMAP4 Multi-Accessed Mail
142
142
 
143
143
  By default the only account is user name `"testuser"` with password `"testpass"` (and access token `"testtoken"` for XOAUTH2 and OAUTHBEARER). The `users` option replaces the default account list, for example `{ "testuser": { "password": "testpass" }, "otheruser": { "password": "secret" } }`, and `server.control.addUser()` adds users at runtime (see [Control API](#control-api)). All users share the same mailbox tree.
144
144
 
145
+ ## OAuth 2.0 and OpenID Connect
146
+
147
+ The `oauth` option (`--oauth-port`) starts a test identity provider, so a mail client can run its whole OAuth sign-in against ImapKit: the authorization code flow with PKCE, a consent page that needs no password, and tokens that log in to IMAP (XOAUTH2, OAUTHBEARER) and SMTP (AUTH XOAUTH2), expire on the server clock, refresh and can be revoked.
148
+
149
+ ```javascript
150
+ const server = imapkit({
151
+ plugins: ['SASL-IR', 'XOAUTH2', 'OAUTHBEARER'],
152
+ users: { 'alice@example.com': { password: 'pw', displayName: 'Alice' }, 'denied@example.com': { password: 'pw', oauth: { consent: 'deny' } } },
153
+ oauth: { port: 8085 }
154
+ });
155
+ await server.start(1143); // discovery: http://127.0.0.1:8085/.well-known/openid-configuration
156
+ ```
157
+
158
+ A consent policy decides every sign-in, from the first that has one: a queued decision (`control.queueOAuthDecision()`), the user that `login_hint` names, the client, the server default. `prompt` shows the consent page (pick an account, Allow or Deny), `approve` and `deny` redirect right away with a plain HTTP 302, so a test can follow the redirect without a browser, and an error object answers with that error. The clients `imapkit`, `imapkit-approve` and `imapkit-deny` are registered by default, with loopback redirect URIs on any port. The ID token's `email` claim is the user name, so use address-style user names for OAuth tests. The provider is strict like the IMAP side: PKCE S256 for public clients, single-use codes, exact redirect URIs, no unknown scopes. Token faults, forced expiry and revocation are in the control API and the REST API. See [OAuth 2.0 and OpenID Connect](https://imapkit.com/docs/guides/oauth).
159
+
145
160
  ## Status
146
161
 
147
162
  ### IMAP4rev1
@@ -444,7 +459,7 @@ Plugins add operations for their own data, they exist only when the plugin is lo
444
459
 
445
460
  A plugin of your own adds operations with `server.control.register(name, fn, routes)`, the routes go to the REST API.
446
461
 
447
- `server.start(port, host)` resolves with the port, `server.stop()` closes the server and every session. `listen()` and `close()` still work. With the `smtp` option (`imapkit({ smtp: { port, host } })`) `start()` also starts an SMTP server that appends every message it receives to INBOX, `server.smtpServer` is that server. It needs the optional `smtp-server` package (`npm install smtp-server`), `start()` rejects with an error that says so when it is missing.
462
+ `server.start(port, host)` resolves with the port, `server.stop()` closes the server and every session. `listen()` and `close()` still work. With the `smtp` option (`imapkit({ smtp: { port, host } })`) `start()` also starts an SMTP server that appends every message it receives to INBOX, `server.smtpServer` is that server. It needs the optional `smtp-server` package (`npm install smtp-server`), `start()` rejects with an error that says so when it is missing. With the `tls` option (`imapkit({ tls: { port, host } })`) `start()` also listens for implicit TLS connections, next to the plaintext port, so one server answers on 143 and 993 alike; `server.tlsServer` is that listener.
448
463
 
449
464
  ### Events
450
465
 
@@ -453,8 +468,10 @@ Tests can wait for events instead of polling:
453
468
  - `session`: `{ type, session }`, `type` is `open`, `login`, `select` (also when the same mailbox is selected again), `unselect` (CLOSE, UNSELECT, a failed SELECT, BYE), `logout` (UNAUTHENTICATE), `waiting` (a command waits for client input, with `command`, e.g. `IDLE` after its continuation) or `close`, `session` is the session as `sessions()` describes it.
454
469
  - `command`: `{ session, tag, command, status, user }` when the tagged response of a command goes out, `status` is `OK`, `NO` or `BAD`. A line that is refused before it runs (it does not parse, or it is pipelined ambiguously) and a command that a script rule answers have no `command` event.
455
470
  - `mailbox`: `{ type, path, oldPath, mailbox, origin }` for CREATE, DELETE, RENAME, SUBSCRIBE and UNSUBSCRIBE, `origin` is null for the control API.
471
+ - `append`: `(mailbox, messages, origin)` when a message is added, by APPEND, COPY, MOVE, REPLACE, SMTP or the control API, before the sessions are told.
456
472
  - `expunge`: `(mailbox, messages, origin)` before the sessions are told, and `flags`: `(mailbox, messages, origin)` for flag changes of the control API.
457
473
  - `acl`: `(mailbox, previousAcl)` when the ACL of a mailbox changes (ACL plugin), `script`: `{ rule, event, session, tag, command }` when a script rule fires, `reset`: after `control.reset()`.
474
+ - `trace`: `{ session, direction, data }` for the protocol traffic of a session, `direction` is `in` or `out` and `data` a Buffer. It is the IMAP layer above TLS and COMPRESS, so a log shows plaintext commands and responses. Nothing is traced while no one listens.
458
475
 
459
476
  ```javascript
460
477
  const idling = new Promise(resolve => server.on('session', event => event.type === 'waiting' && event.command === 'IDLE' && resolve(event)));
@@ -478,7 +495,7 @@ The REST API controls the whole server, including the mail of every user, so it
478
495
 
479
496
  Requests and responses are JSON, and every POST needs `Content-Type: application/json`, also without a body (HTTP 415 otherwise). Errors are `{ "error": { "code", "message" } }` with HTTP status 404 for `NONEXISTENT`, 409 for `ALREADYEXISTS` and the response codes of a failed mailbox operation (`CANNOT`, `HASCHILDREN` ...), 400 for `INVALID`, 401 for a missing or wrong token, 404 `NOTFOUND` and 405 `METHOD` for an unknown route or method, 413 `TOOBIG` for a body over 64 MiB (and `addMessage` checks refused by APPENDLIMIT), 500 for a server error. A mailbox is its storage name (modified UTF-7) in the URL, URL encoded with a `/` in the name as `%2F` (`/v1/mailboxes/Work%2FProjects`). Message sources are base64 in responses (`"encoding": "base64"`), a request sends `raw` as text or as base64 with `"encoding": "base64"`. `GET /v1/openapi.json` describes every endpoint.
480
497
 
481
- `GET /v1/events` streams the [events](#events) as [Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html) (`event: <type>` with JSON `data`), so a test in another language can wait for "the client selected INBOX" or "a script rule fired" without polling. `?types=session,command` chooses the types (`session`, `command`, `mailbox`, `expunge`, `flags`, `acl`, `script`, `reset`), mailboxes are paths, messages UIDs and sessions numbers in the data:
498
+ `GET /v1/events` streams the [events](#events) as [Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html) (`event: <type>` with JSON `data`), so a test in another language can wait for "the client selected INBOX" or "a script rule fired" without polling. `?types=session,command` chooses the types (`session`, `command`, `mailbox`, `append`, `expunge`, `flags`, `acl`, `script`, `reset`, `oauth`, and `trace`, which is only sent when asked for, with base64 `data`), mailboxes are paths, messages UIDs and sessions numbers in the data:
482
499
 
483
500
  ```bash
484
501
  curl -N 'http://127.0.0.1:8143/v1/events?types=session'
@@ -486,26 +503,27 @@ curl -N 'http://127.0.0.1:8143/v1/events?types=session'
486
503
  # data: {"type":"select","session":{"session":1,"user":"testuser","state":"Selected","mailbox":"INBOX",...}}
487
504
  ```
488
505
 
489
- | Endpoint | Control API |
490
- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
491
- | `GET /v1/snapshot` | `snapshot()` |
492
- | `POST /v1/reset` | `reset()` |
493
- | `POST /v1/shutdown` (`{ graceful }`) | `shutdown()` |
494
- | `GET /v1/sessions`, `DELETE /v1/sessions/{n}` (`{ text, reset }`) | `sessions()`, `disconnect()` |
495
- | `POST /v1/sessions/{n}/inject` (`{ data, encoding }`) | `inject()` |
496
- | `GET`, `POST /v1/users`, `PUT`, `DELETE /v1/users/{name}` | `listUsers()`, `addUser()`, `updateUser()`, `deleteUser()` |
497
- | `GET`, `POST /v1/mailboxes`, `GET`, `DELETE /v1/mailboxes/{path}` | `listMailboxes()`, `createMailbox()`, `getMailbox()`, `deleteMailbox()` |
498
- | `POST /v1/mailboxes/{path}/rename` (`{ newPath }`) | `renameMailbox()` |
499
- | `PUT`, `DELETE /v1/mailboxes/{path}/subscription` | `subscribe()`, `unsubscribe()` |
500
- | `POST /v1/mailboxes/{path}/uidvalidity` | `resetUidValidity()` |
501
- | `GET /v1/mailboxes/{path}/messages` (`?uids=1,2&raw=true`), `POST` (`{ raw, flags }`) | `listMessages()`, `addMessage()` |
502
- | `GET`, `DELETE /v1/mailboxes/{path}/messages/{uid}` | `getMessage()`, `expungeMessages()` |
503
- | `POST /v1/mailboxes/{path}/messages/flags`, `.../expunge`, `.../copy`, `.../move` | `setFlags()`, `expungeMessages()`, `copyMessages()`, `moveMessages()` |
504
- | `GET /v1/mailboxes/{path}/acl`, `PUT`, `DELETE /v1/mailboxes/{path}/acl/{identifier}` (`{ rights }`) | `getAcl()`, `setAcl()`, `deleteAcl()` (ACL) |
505
- | `GET`, `PUT /v1/quota` | `getQuota()`, `setQuota()` (QUOTA) |
506
- | `GET`, `PUT /v1/metadata`, `GET`, `PUT /v1/mailboxes/{path}/metadata` | `getMetadata()`, `setMetadata()` (METADATA) |
507
- | `PUT /v1/mailboxes/{path}/special-use` (`{ specialUse }`) | `setSpecialUse()` (SPECIAL-USE) |
508
- | `GET`, `POST`, `DELETE /v1/script/rules`, `DELETE /v1/script/rules/{id}` | [script rules](#scripted-faults) in their JSON form, so runtime faults need no restart |
506
+ | Endpoint | Control API |
507
+ | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
508
+ | `GET /v1/snapshot` | `snapshot()` |
509
+ | `POST /v1/reset` | `reset()` |
510
+ | `POST /v1/shutdown` (`{ graceful }`) | `shutdown()` |
511
+ | `GET /v1/sessions`, `DELETE /v1/sessions/{n}` (`{ text, reset }`) | `sessions()`, `disconnect()` |
512
+ | `POST /v1/sessions/{n}/inject` (`{ data, encoding }`) | `inject()` |
513
+ | `GET`, `POST /v1/users`, `PUT`, `DELETE /v1/users/{name}` | `listUsers()`, `addUser()`, `updateUser()`, `deleteUser()` |
514
+ | `GET`, `POST /v1/mailboxes`, `GET`, `DELETE /v1/mailboxes/{path}` | `listMailboxes()`, `createMailbox()`, `getMailbox()`, `deleteMailbox()` |
515
+ | `POST /v1/mailboxes/{path}/rename` (`{ newPath }`) | `renameMailbox()` |
516
+ | `PUT`, `DELETE /v1/mailboxes/{path}/subscription` | `subscribe()`, `unsubscribe()` |
517
+ | `POST /v1/mailboxes/{path}/uidvalidity` | `resetUidValidity()` |
518
+ | `GET /v1/mailboxes/{path}/messages` (`?uids=1,2&raw=true`), `POST` (`{ raw, flags }`) | `listMessages()`, `addMessage()` |
519
+ | `GET`, `DELETE /v1/mailboxes/{path}/messages/{uid}` | `getMessage()`, `expungeMessages()` |
520
+ | `POST /v1/mailboxes/{path}/messages/flags`, `.../expunge`, `.../copy`, `.../move` | `setFlags()`, `expungeMessages()`, `copyMessages()`, `moveMessages()` |
521
+ | `GET /v1/mailboxes/{path}/acl`, `PUT`, `DELETE /v1/mailboxes/{path}/acl/{identifier}` (`{ rights }`) | `getAcl()`, `setAcl()`, `deleteAcl()` (ACL) |
522
+ | `GET`, `PUT /v1/quota` | `getQuota()`, `setQuota()` (QUOTA) |
523
+ | `GET`, `PUT /v1/metadata`, `GET`, `PUT /v1/mailboxes/{path}/metadata` | `getMetadata()`, `setMetadata()` (METADATA) |
524
+ | `PUT /v1/mailboxes/{path}/special-use` (`{ specialUse }`) | `setSpecialUse()` (SPECIAL-USE) |
525
+ | `GET`, `POST`, `DELETE /v1/script/rules`, `DELETE /v1/script/rules/{id}` | [script rules](#scripted-faults) in their JSON form, so runtime faults need no restart |
526
+ | `GET /v1/oauth`, `/v1/oauth/clients`, `/v1/oauth/consent`, `/v1/oauth/decisions`, `/v1/oauth/token-faults`, `/v1/oauth/tokens` | the [OAuth provider](#oauth-20-and-openid-connect), with the `oauth` option |
509
527
 
510
528
  ## Scripted faults
511
529
 
package/bin/help.txt CHANGED
@@ -22,6 +22,8 @@ Usage: imapkit [OPTS]
22
22
  "Scripted faults" below)
23
23
  --plugin=<plugin> Enables a plugin for the server. See below for
24
24
  available plugins.
25
+ --tls-port=<port> Also listens for implicit TLS (IMAPS) connections
26
+ on this port, next to the plaintext port
25
27
  --smtpPort=<port> Port number for incoming SMTP server. If not set
26
28
  SMTP server is not started. --smtp-port works too
27
29
  --rest-port=<port> Starts the REST API (the control API over HTTP,
@@ -29,6 +31,15 @@ Usage: imapkit [OPTS]
29
31
  --rest-host=<address> Address of the REST API, defaults to 127.0.0.1.
30
32
  Any other than a loopback address needs a token
31
33
  --rest-token=<token> Bearer token that REST requests must send
34
+ --oauth-port=<port> Starts the OAuth 2.0 / OpenID Connect provider:
35
+ tokens for IMAP (XOAUTH2, OAUTHBEARER) and SMTP
36
+ (AUTH XOAUTH2), no passwords, see "OAuth" below
37
+ --oauth-host=<address> Address of the OAuth provider, 127.0.0.1 default
38
+ --oauth-consent=<policy> What a sign-in does: prompt (consent page, the
39
+ default), approve or deny (redirect right away)
40
+ --oauth-secure=true Serves the OAuth provider over HTTPS with the
41
+ bundled localhost certificate
42
+ --oauth-issuer=<url> Public base URL of the OAuth provider (a tunnel)
32
43
  --quirk=<name> Makes the server behave like a known real server
33
44
  (james-fetchgroup, james-late-fetch,
34
45
  yahoo-quoted-sections, m365-throttle, no-uidplus,
@@ -38,9 +49,11 @@ Usage: imapkit [OPTS]
38
49
 
39
50
  The options can also be set with environment variables: IMAPKIT_PORT,
40
51
  IMAPKIT_SECURE, IMAPKIT_DEBUG, IMAPKIT_STORAGE, IMAPKIT_CONFIG,
41
- IMAPKIT_SCRIPT, IMAPKIT_PLUGINS (comma separated), IMAPKIT_SMTPPORT,
42
- IMAPKIT_REST_PORT, IMAPKIT_REST_HOST, IMAPKIT_REST_TOKEN, IMAPKIT_QUIRKS
43
- and IMAPKIT_SCRIPT_SEED.
52
+ IMAPKIT_SCRIPT, IMAPKIT_PLUGINS (comma separated), IMAPKIT_TLS_PORT,
53
+ IMAPKIT_SMTPPORT, IMAPKIT_REST_PORT, IMAPKIT_REST_HOST, IMAPKIT_REST_TOKEN,
54
+ IMAPKIT_OAUTH_PORT, IMAPKIT_OAUTH_HOST, IMAPKIT_OAUTH_CONSENT,
55
+ IMAPKIT_OAUTH_SECURE, IMAPKIT_OAUTH_ISSUER, IMAPKIT_QUIRKS and
56
+ IMAPKIT_SCRIPT_SEED.
44
57
 
45
58
  NB! If port or smtpPort values are below 1024 you most probably need to
46
59
  use sudo or run the command with administrator rights.
@@ -78,6 +91,19 @@ Configuration file takes the following structure
78
91
  }
79
92
  }
80
93
 
94
+ OAuth
95
+ -----
96
+
97
+ --oauth-port starts a test identity provider. Its discovery document is
98
+ at <issuer>/.well-known/openid-configuration. Three public clients are
99
+ registered (loopback redirect URIs on any port, PKCE S256 required):
100
+ "imapkit" follows the default consent policy, "imapkit-approve" always
101
+ approves and "imapkit-deny" always denies. The consent page needs no
102
+ password: pick an account and click Allow or Deny. The email claim is
103
+ the user name, so use address-style user names for OAuth tests. Load
104
+ SASL-IR with XOAUTH2 or OAUTHBEARER to log in to IMAP with the tokens.
105
+ More clients, scopes and policies go into "oauth" of the --config file.
106
+
81
107
  Scripted faults
82
108
  ---------------
83
109
 
package/bin/imapkit.js CHANGED
@@ -23,9 +23,15 @@ const { values: argv } = parseArgs({
23
23
  plugin: { type: 'string', multiple: true },
24
24
  smtpPort: { type: 'string' },
25
25
  'smtp-port': { type: 'string' },
26
+ 'tls-port': { type: 'string' },
26
27
  'rest-port': { type: 'string' },
27
28
  'rest-host': { type: 'string' },
28
29
  'rest-token': { type: 'string' },
30
+ 'oauth-port': { type: 'string' },
31
+ 'oauth-host': { type: 'string' },
32
+ 'oauth-consent': { type: 'string' },
33
+ 'oauth-secure': { type: 'boolean' },
34
+ 'oauth-issuer': { type: 'string' },
29
35
  quirk: { type: 'string', multiple: true },
30
36
  'script-seed': { type: 'string' }
31
37
  }
@@ -48,9 +54,15 @@ const configLocation = argv.config || process.env.IMAPKIT_CONFIG;
48
54
  const storageLocation = argv.storage || process.env.IMAPKIT_STORAGE;
49
55
  const scriptLocation = argv.script || process.env.IMAPKIT_SCRIPT;
50
56
  const smtpPort = argv.smtpPort || argv['smtp-port'] || process.env.IMAPKIT_SMTPPORT;
57
+ const tlsPort = argv['tls-port'] || process.env.IMAPKIT_TLS_PORT;
51
58
  const restPort = argv['rest-port'] || process.env.IMAPKIT_REST_PORT;
52
59
  const restHost = argv['rest-host'] || process.env.IMAPKIT_REST_HOST;
53
60
  const restToken = argv['rest-token'] || process.env.IMAPKIT_REST_TOKEN;
61
+ const oauthPort = argv['oauth-port'] || process.env.IMAPKIT_OAUTH_PORT;
62
+ const oauthHost = argv['oauth-host'] || process.env.IMAPKIT_OAUTH_HOST;
63
+ const oauthConsent = argv['oauth-consent'] || process.env.IMAPKIT_OAUTH_CONSENT;
64
+ const oauthSecure = isTrue(argv['oauth-secure'] || process.env.IMAPKIT_OAUTH_SECURE);
65
+ const oauthIssuer = argv['oauth-issuer'] || process.env.IMAPKIT_OAUTH_ISSUER;
54
66
  const quirksList = listOption(argv.quirk || process.env.IMAPKIT_QUIRKS);
55
67
  const scriptSeed = argv['script-seed'] || process.env.IMAPKIT_SCRIPT_SEED;
56
68
  const pluginsList = listOption(argv.plugin || process.env.IMAPKIT_PLUGINS).map(plugin => plugin.toUpperCase());
@@ -115,9 +127,24 @@ if (argv.help) {
115
127
  if (scriptSeed !== undefined) {
116
128
  config.scriptSeed = Number(scriptSeed);
117
129
  }
130
+ if (tlsPort) {
131
+ config.tls = { port: Number(tlsPort) };
132
+ }
118
133
  if (restPort) {
119
134
  config.rest = { port: Number(restPort), host: restHost, token: restToken };
120
135
  }
136
+ if (oauthPort) {
137
+ // the other oauth options (clients, scopes ...) come from the --config file
138
+ config.oauth = Object.assign({}, config.oauth, { port: Number(oauthPort) });
139
+ for (const [key, value] of Object.entries({ host: oauthHost, consent: oauthConsent, issuer: oauthIssuer })) {
140
+ if (value) {
141
+ config.oauth[key] = value;
142
+ }
143
+ }
144
+ if (oauthSecure) {
145
+ config.oauth.secure = true;
146
+ }
147
+ }
121
148
  if (smtpPort) {
122
149
  // smtp-server is an optional dependency, start() loads it only when SMTP is enabled
123
150
  config.smtp = { port: Number(smtpPort) };
@@ -135,9 +162,25 @@ if (argv.help) {
135
162
  listening => {
136
163
  // the actual port, also for -p 0
137
164
  console.log('ImapKit successfully%s listening on port %s', isSecure ? ' and securely' : '', listening);
165
+ if (server.tlsServer) {
166
+ console.log('ImapKit listening for TLS connections on port %s', server.tlsServer.address().port);
167
+ }
138
168
  if (server.restServer) {
139
169
  console.log('REST API listening on %s:%s', (config.rest && config.rest.host) || '127.0.0.1', server.restServer.address().port);
140
170
  }
171
+ if (server.oauthServer) {
172
+ const clients = server.control.listOAuthClients().map(client => client.id);
173
+ const consent = server.oauth.consent;
174
+ console.log(
175
+ 'OAuth provider listening on %s (clients: %s; default consent: %s)',
176
+ server.oauth.issuer,
177
+ clients.join(', '),
178
+ typeof consent === 'string' ? consent : 'error ' + consent.error
179
+ );
180
+ if (!server.capabilities['AUTH=XOAUTH2'] && !server.capabilities['AUTH=OAUTHBEARER']) {
181
+ console.log('Load the XOAUTH2 or OAUTHBEARER plugin (with SASL-IR) to log in to IMAP with its tokens');
182
+ }
183
+ }
141
184
  if (server.smtpServer) {
142
185
  console.log('Incoming SMTP server up and running on port %s', server.smtpServer.server.address().port);
143
186
  }