Skip to content

Commit 6d33f97

Browse files
authored
[MAJOR] SUBSCRIBE and UNSUBSCRIBE (#235)
Support for SUBSCRIBE and UNSUBSCRIBE commands. http://tools.ietf.org/html/rfc3501#section-6.3.6 http://tools.ietf.org/html/rfc3501#section-6.3.7
1 parent 0cf4d0c commit 6d33f97

File tree

4 files changed

+114
-34
lines changed

4 files changed

+114
-34
lines changed

README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,39 @@ client.listNamespaces().then((namespaces) => { ... })
239239
}
240240
```
241241

242+
## Subscribe to mailbox
243+
244+
Subscribe to a mailbox with the given path with `subscribeMailbox(path)`.
245+
246+
Subscribing to a mailbox that is already subscribed is redundant and does return an OK just as when subscribing to unsubscribed mailbox.
247+
248+
Command: [SUBSCRIBE](http://tools.ietf.org/html/rfc3501#section-6.3.6)
249+
250+
Example
251+
252+
```javascript
253+
// On a server with unsubscribed Sent mailbox
254+
client.subscribeMailbox('Sent').then(() => { ... });
255+
```
256+
257+
## Unsubscribe from mailbox
258+
259+
Unsubscribe from a mailbox with the given path with `unsubscribeMailbox(path)`.
260+
261+
Unsubscribing from a mailbox that is already unsubscribed is redundant and does return an OK just as when unsubscribing from a subscribed mailbox.
262+
263+
Command: [UNSUBSCRIBE](http://tools.ietf.org/html/rfc3501#section-6.3.7)
264+
265+
Example
266+
267+
```javascript
268+
// On a server with subscribed Sent mailbox
269+
client.unsubscribeMailbox('Sent').then(() => { ... });
270+
```
271+
242272
## Create mailbox
243273

244-
Create a folder with the given path with `createMailbox(path)`, automatically handling utf-7 encoding. You currently need to manually build the path string yourself.
274+
Create a folder with the given path with `createMailbox(path)`. You currently need to manually build the path string yourself.
245275

246276
If the server indicates a failure that the folder already exists, but responds with the ALREADYEXISTS response code, the request will be treated as a success.
247277

@@ -260,7 +290,7 @@ client.createMailbox('Foo').then(() => { ... });
260290
```
261291
## Delete mailbox
262292

263-
Delete a folder with the given path with `deleteMailbox(path)`, automatically handling utf-7 encoding.
293+
Delete a folder with the given path with `deleteMailbox(path)`.
264294

265295
Command: [DELETE](http://tools.ietf.org/html/rfc3501#section-6.3.4)
266296

src/client-integration.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable no-unused-expressions */
22

33
import hoodiecrow from 'hoodiecrow-imap'
4-
import ImapClient, { LOG_LEVEL_NONE as logLevel } from '..'
4+
import ImapClient, { LOG_LEVEL_NONE as logLevel } from '../src/index'
55
import { parseSEARCH } from './command-parser'
66
import { buildSEARCHCommand } from './command-builder'
77

@@ -199,6 +199,22 @@ describe('browserbox integration tests', () => {
199199
})
200200
})
201201

202+
describe('#subscribe', () => {
203+
it('should succeed', () => {
204+
return imap.subscribeMailbox('inbox').then(response => {
205+
expect(response.command).to.equal('OK')
206+
})
207+
})
208+
})
209+
210+
describe('#unsubscribe', () => {
211+
it('should succeed', () => {
212+
return imap.unsubscribeMailbox('inbox').then(response => {
213+
expect(response.command).to.equal('OK')
214+
})
215+
})
216+
})
217+
202218
describe('#upload', () => {
203219
it('should succeed', () => {
204220
var msgCount

src/client-unit.js

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -582,18 +582,6 @@ describe('browserbox unit tests', () => {
582582
})
583583
})
584584

585-
it('should call mutf7 encode the argument', () => {
586-
// From RFC 3501
587-
br.exec.withArgs({
588-
command: 'CREATE',
589-
attributes: ['~peter/mail/&U,BTFw-/&ZeVnLIqe-']
590-
}).returns(Promise.resolve())
591-
592-
return br.createMailbox('~peter/mail/\u53f0\u5317/\u65e5\u672c\u8a9e').then(() => {
593-
expect(br.exec.callCount).to.equal(1)
594-
})
595-
})
596-
597585
it('should treat an ALREADYEXISTS response as success', () => {
598586
var fakeErr = {
599587
code: 'ALREADYEXISTS'
@@ -624,18 +612,6 @@ describe('browserbox unit tests', () => {
624612
expect(br.exec.callCount).to.equal(1)
625613
})
626614
})
627-
628-
it('should call mutf7 encode the argument', () => {
629-
// From RFC 3501
630-
br.exec.withArgs({
631-
command: 'DELETE',
632-
attributes: ['~peter/mail/&U,BTFw-/&ZeVnLIqe-']
633-
}).returns(Promise.resolve())
634-
635-
return br.deleteMailbox('~peter/mail/\u53f0\u5317/\u65e5\u672c\u8a9e').then(() => {
636-
expect(br.exec.callCount).to.equal(1)
637-
})
638-
})
639615
})
640616

641617
describe.skip('#listMessages', () => {
@@ -1001,6 +977,34 @@ describe('browserbox unit tests', () => {
1001977
})
1002978
})
1003979

980+
describe('#subscribe and unsubscribe', () => {
981+
beforeEach(() => {
982+
sinon.stub(br, 'exec')
983+
})
984+
985+
it('should call SUBSCRIBE with a string payload', () => {
986+
br.exec.withArgs({
987+
command: 'SUBSCRIBE',
988+
attributes: ['mailboxname']
989+
}).returns(Promise.resolve())
990+
991+
return br.subscribeMailbox('mailboxname').then(() => {
992+
expect(br.exec.callCount).to.equal(1)
993+
})
994+
})
995+
996+
it('should call UNSUBSCRIBE with a string payload', () => {
997+
br.exec.withArgs({
998+
command: 'UNSUBSCRIBE',
999+
attributes: ['mailboxname']
1000+
}).returns(Promise.resolve())
1001+
1002+
return br.unsubscribeMailbox('mailboxname').then(() => {
1003+
expect(br.exec.callCount).to.equal(1)
1004+
})
1005+
})
1006+
})
1007+
10041008
describe('#hasCapability', () => {
10051009
it('should detect existing capability', () => {
10061010
br._capability = ['ZZZ']

src/client.js

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { map, pipe, union, zip, fromPairs, propOr, pathOr, flatten } from 'ramda'
2-
import { imapEncode, imapDecode } from 'emailjs-utf7'
2+
import { imapDecode } from 'emailjs-utf7'
33
import {
44
parseAPPEND,
55
parseCOPY,
@@ -281,6 +281,38 @@ export default class Client {
281281
return mailboxInfo
282282
}
283283

284+
/**
285+
* Subscribe to a mailbox with the given path
286+
*
287+
* SUBSCRIBE details:
288+
* https://tools.ietf.org/html/rfc3501#section-6.3.6
289+
*
290+
* @param {String} path
291+
* The path of the mailbox you would like to subscribe to.
292+
* @returns {Promise}
293+
* Promise resolves if mailbox is now subscribed to or was so already.
294+
*/
295+
async subscribeMailbox (path) {
296+
this.logger.debug('Subscribing to mailbox', path, '...')
297+
return this.exec({ command: 'SUBSCRIBE', attributes: [path] })
298+
}
299+
300+
/**
301+
* Unsubscribe from a mailbox with the given path
302+
*
303+
* UNSUBSCRIBE details:
304+
* https://tools.ietf.org/html/rfc3501#section-6.3.7
305+
*
306+
* @param {String} path
307+
* The path of the mailbox you would like to unsubscribe from.
308+
* @returns {Promise}
309+
* Promise resolves if mailbox is no longer subscribed to or was not before.
310+
*/
311+
async unsubscribeMailbox (path) {
312+
this.logger.debug('Unsubscribing to mailbox', path, '...')
313+
return this.exec({ command: 'UNSUBSCRIBE', attributes: [path] })
314+
}
315+
284316
/**
285317
* Runs NAMESPACE command
286318
*
@@ -348,16 +380,15 @@ export default class Client {
348380
* http://tools.ietf.org/html/rfc3501#section-6.3.3
349381
*
350382
* @param {String} path
351-
* The path of the mailbox you would like to create. This method will
352-
* handle utf7 encoding for you.
383+
* The path of the mailbox you would like to create.
353384
* @returns {Promise}
354385
* Promise resolves if mailbox was created.
355386
* In the event the server says NO [ALREADYEXISTS], we treat that as success.
356387
*/
357388
async createMailbox (path) {
358389
this.logger.debug('Creating mailbox', path, '...')
359390
try {
360-
await this.exec({ command: 'CREATE', attributes: [imapEncode(path)] })
391+
await this.exec({ command: 'CREATE', attributes: [path] })
361392
} catch (err) {
362393
if (err && err.code === 'ALREADYEXISTS') {
363394
return
@@ -373,14 +404,13 @@ export default class Client {
373404
* https://tools.ietf.org/html/rfc3501#section-6.3.4
374405
*
375406
* @param {String} path
376-
* The path of the mailbox you would like to delete. This method will
377-
* handle utf7 encoding for you.
407+
* The path of the mailbox you would like to delete.
378408
* @returns {Promise}
379409
* Promise resolves if mailbox was deleted.
380410
*/
381411
deleteMailbox (path) {
382412
this.logger.debug('Deleting mailbox', path, '...')
383-
return this.exec({ command: 'DELETE', attributes: [imapEncode(path)] })
413+
return this.exec({ command: 'DELETE', attributes: [path] })
384414
}
385415

386416
/**

0 commit comments

Comments
 (0)