1 | var Parser = require('../lib/Parser').Parser;
|
2 |
|
3 | var assert = require('assert'),
|
4 | crypto = require('crypto'),
|
5 | inspect = require('util').inspect;
|
6 |
|
7 | var CR = '\r', LF = '\n', CRLF = CR + LF;
|
8 |
|
9 | [
|
10 | { source: ['A1 OK LOGIN completed', CRLF],
|
11 | expected: [ { type: 'ok',
|
12 | tagnum: 1,
|
13 | textCode: undefined,
|
14 | text: 'LOGIN completed'
|
15 | }
|
16 | ],
|
17 | what: 'Tagged OK'
|
18 | },
|
19 | { source: ['IDLE OK IDLE terminated', CRLF],
|
20 | expected: [ 'IDLE OK IDLE terminated' ],
|
21 | what: 'Unknown line'
|
22 | },
|
23 | { source: ['+ idling', CRLF],
|
24 | expected: [ { textCode: undefined,
|
25 | text: 'idling'
|
26 | }
|
27 | ],
|
28 | what: 'Continuation'
|
29 | },
|
30 | { source: ['+ [ALERT] idling', CRLF],
|
31 | expected: [ { textCode: 'ALERT',
|
32 | text: 'idling'
|
33 | }
|
34 | ],
|
35 | what: 'Continuation with text code'
|
36 | },
|
37 | { source: ['+', CRLF],
|
38 | expected: [ { textCode: undefined,
|
39 | text: undefined
|
40 | }
|
41 | ],
|
42 | what: 'Continuation (broken -- RFC violation) sent by AOL IMAP'
|
43 | },
|
44 | { source: ['* NAMESPACE ',
|
45 | '(("" "/")) ',
|
46 | '(("~" "/")) ',
|
47 | '(("#shared/" "/")("#public/" "/")("#ftp/" "/")("#news." "."))',
|
48 | CRLF],
|
49 | expected: [ { type: 'namespace',
|
50 | num: undefined,
|
51 | textCode: undefined,
|
52 | text: {
|
53 | personal: [
|
54 | { prefix: '',
|
55 | delimiter: '/',
|
56 | extensions: undefined
|
57 | }
|
58 | ],
|
59 | other: [
|
60 | { prefix: '~',
|
61 | delimiter: '/',
|
62 | extensions: undefined
|
63 | }
|
64 | ],
|
65 | shared: [
|
66 | { prefix: '#shared/',
|
67 | delimiter: '/',
|
68 | extensions: undefined
|
69 | },
|
70 | { prefix: '#public/',
|
71 | delimiter: '/',
|
72 | extensions: undefined
|
73 | },
|
74 | { prefix: '#ftp/',
|
75 | delimiter: '/',
|
76 | extensions: undefined
|
77 | },
|
78 | { prefix: '#news.',
|
79 | delimiter: '.',
|
80 | extensions: undefined
|
81 | }
|
82 | ]
|
83 | }
|
84 | }
|
85 | ],
|
86 | what: 'Multiple namespaces'
|
87 | },
|
88 | { source: ['* NAMESPACE ',
|
89 | '(("" "/" "X-PARAM" ("FLAG1" "FLAG2"))) ',
|
90 | 'NIL ',
|
91 | 'NIL',
|
92 | CRLF],
|
93 | expected: [ { type: 'namespace',
|
94 | num: undefined,
|
95 | textCode: undefined,
|
96 | text: {
|
97 | personal: [
|
98 | { prefix: '',
|
99 | delimiter: '/',
|
100 | extensions: {
|
101 | 'X-PARAM': [ 'FLAG1', 'FLAG2' ]
|
102 | }
|
103 | }
|
104 | ],
|
105 | other: null,
|
106 | shared: null
|
107 | }
|
108 | }
|
109 | ],
|
110 | what: 'Multiple namespaces'
|
111 | },
|
112 | { source: ['* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)', CRLF],
|
113 | expected: [ { type: 'flags',
|
114 | num: undefined,
|
115 | textCode: undefined,
|
116 | text: [
|
117 | '\\Answered',
|
118 | '\\Flagged',
|
119 | '\\Deleted',
|
120 | '\\Seen',
|
121 | '\\Draft'
|
122 | ]
|
123 | }
|
124 | ],
|
125 | what: 'Flags'
|
126 | },
|
127 | { source: ['* SEARCH 2 3 6', CRLF],
|
128 | expected: [ { type: 'search',
|
129 | num: undefined,
|
130 | textCode: undefined,
|
131 | text: [ 2, 3, 6 ]
|
132 | }
|
133 | ],
|
134 | what: 'Search'
|
135 | },
|
136 | { source: ['* LIST (\\Noselect) "/" ~/Mail/foo', CRLF],
|
137 | expected: [ { type: 'list',
|
138 | num: undefined,
|
139 | textCode: undefined,
|
140 | text: {
|
141 | flags: [ '\\Noselect' ],
|
142 | delimiter: '/',
|
143 | name: '~/Mail/foo'
|
144 | }
|
145 | }
|
146 | ],
|
147 | what: 'List'
|
148 | },
|
149 | { source: ['* STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)', CRLF],
|
150 | expected: [ { type: 'status',
|
151 | num: undefined,
|
152 | textCode: undefined,
|
153 | text: {
|
154 | name: 'blurdybloop',
|
155 | attrs: { messages: 231, uidnext: 44292 }
|
156 | }
|
157 | }
|
158 | ],
|
159 | what: 'Status'
|
160 | },
|
161 | { source: ['* OK [UNSEEN 17] Message 17 is the first unseen message', CRLF],
|
162 | expected: [ { type: 'ok',
|
163 | num: undefined,
|
164 | textCode: {
|
165 | key: 'UNSEEN',
|
166 | val: 17
|
167 | },
|
168 | text: 'Message 17 is the first unseen message'
|
169 | }
|
170 | ],
|
171 | what: 'Untagged OK (with text code, with text)'
|
172 | },
|
173 | { source: ['* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited', CRLF],
|
174 | expected: [ { type: 'ok',
|
175 | num: undefined,
|
176 | textCode: {
|
177 | key: 'PERMANENTFLAGS',
|
178 | val: [ '\\Deleted', '\\Seen', '\\*' ]
|
179 | },
|
180 | text: 'Limited'
|
181 | }
|
182 | ],
|
183 | what: 'Untagged OK (with text code, with text)'
|
184 | },
|
185 | { source: ['* OK [UNSEEN 17]', CRLF],
|
186 | expected: [ { type: 'ok',
|
187 | num: undefined,
|
188 | textCode: undefined,
|
189 | text: '[UNSEEN 17]'
|
190 | }
|
191 | ],
|
192 | what: 'Untagged OK (no text code, with text) (RFC violation)'
|
193 | },
|
194 | { source: ['* OK IMAP4rev1 Service Ready', CRLF],
|
195 | expected: [ { type: 'ok',
|
196 | num: undefined,
|
197 | textCode: undefined,
|
198 | text: 'IMAP4rev1 Service Ready'
|
199 | }
|
200 | ],
|
201 | what: 'Untagged OK (no text code, with text)'
|
202 | },
|
203 | { source: ['* OK', CRLF],
|
204 | expected: [ { type: 'ok',
|
205 | num: undefined,
|
206 | textCode: undefined,
|
207 | text: undefined
|
208 | }
|
209 | ],
|
210 | what: 'Untagged OK (no text code, no text) (RFC violation)'
|
211 | },
|
212 | { source: ['* 18 EXISTS', CRLF],
|
213 | expected: [ { type: 'exists',
|
214 | num: 18,
|
215 | textCode: undefined,
|
216 | text: undefined
|
217 | }
|
218 | ],
|
219 | what: 'Untagged EXISTS'
|
220 | },
|
221 | { source: ['* 2 RECENT', CRLF],
|
222 | expected: [ { type: 'recent',
|
223 | num: 2,
|
224 | textCode: undefined,
|
225 | text: undefined
|
226 | }
|
227 | ],
|
228 | what: 'Untagged RECENT'
|
229 | },
|
230 | { source: ['* 12 FETCH (BODY[HEADER] {342}', CRLF,
|
231 | 'Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT)', CRLF,
|
232 | 'From: Terry Gray <gray@cac.washington.edu>', CRLF,
|
233 | 'Subject: IMAP4rev1 WG mtg summary and minutes', CRLF,
|
234 | 'To: imap@cac.washington.edu', CRLF,
|
235 | 'cc: minutes@CNRI.Reston.VA.US, John Klensin <KLENSIN@MIT.EDU>', CRLF,
|
236 | 'Message-Id: <B27397-0100000@cac.washington.edu>', CRLF,
|
237 | 'MIME-Version: 1.0', CRLF,
|
238 | 'Content-Type: TEXT/PLAIN; CHARSET=US-ASCII', CRLF, CRLF,
|
239 | ')', CRLF],
|
240 | expected: [ { seqno: 12,
|
241 | which: 'HEADER',
|
242 | size: 342
|
243 | },
|
244 | { type: 'fetch',
|
245 | num: 12,
|
246 | textCode: undefined,
|
247 | text: {}
|
248 | }
|
249 | ],
|
250 | bodySHA1s: ['1f96faf50f6410f99237791f9e3b89454bf93fa7'],
|
251 | what: 'Untagged FETCH (body)'
|
252 | },
|
253 | { source: ['* 12 FETCH (INTERNALDATE {26}', CRLF,
|
254 | '17-Jul-1996 02:44:25 -0700)' + CRLF],
|
255 | expected: [ { type: 'fetch',
|
256 | num: 12,
|
257 | textCode: undefined,
|
258 | text: {
|
259 | internaldate: new Date('17-Jul-1996 02:44:25 -0700')
|
260 | }
|
261 | }
|
262 | ],
|
263 | what: 'Untagged FETCH with non-body literal'
|
264 | },
|
265 | { source: ['* 12 FETCH (INTERNALDATE {2',
|
266 | '6}' + CRLF + '17-Jul-1996 02:44:25 -0700)' + CRLF],
|
267 | expected: [ { type: 'fetch',
|
268 | num: 12,
|
269 | textCode: undefined,
|
270 | text: {
|
271 | internaldate: new Date('17-Jul-1996 02:44:25 -0700')
|
272 | }
|
273 | }
|
274 | ],
|
275 | what: 'Untagged FETCH with non-body literal (length split)'
|
276 | },
|
277 | { source: ['* 12 FETCH (INTERNALDATE {26}', CRLF,
|
278 | '17-Jul-1996 02:44:25 -0700)' + CR,
|
279 | LF],
|
280 | expected: [ { type: 'fetch',
|
281 | num: 12,
|
282 | textCode: undefined,
|
283 | text: {
|
284 | internaldate: new Date('17-Jul-1996 02:44:25 -0700')
|
285 | }
|
286 | }
|
287 | ],
|
288 | what: 'Untagged FETCH with non-body literal (split CRLF)'
|
289 | },
|
290 | { source: ['* 12 FETCH (FLAGS (\\Seen)',
|
291 | ' INTERNALDATE "17-Jul-1996 02:44:25 -0700"',
|
292 | ' RFC822.SIZE 4286',
|
293 | ' ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)"',
|
294 | ' "IMAP4rev1 WG mtg summary and minutes"',
|
295 | ' (("Terry Gray" NIL "gray" "cac.washington.edu"))',
|
296 | ' (("Terry Gray" NIL "gray" "cac.washington.edu"))',
|
297 | ' (("Terry Gray" NIL "gray" "cac.washington.edu"))',
|
298 | ' ((NIL NIL "imap" "cac.washington.edu"))',
|
299 | ' ((NIL NIL "minutes" "CNRI.Reston.VA.US")',
|
300 | '("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL',
|
301 | ' "<B27397-0100000@cac.washington.edu>")',
|
302 | ' BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 3028',
|
303 | ' 92))',
|
304 | CRLF],
|
305 | expected: [ { type: 'fetch',
|
306 | num: 12,
|
307 | textCode: undefined,
|
308 | text: {
|
309 | flags: [ '\\Seen' ],
|
310 | internaldate: new Date('17-Jul-1996 02:44:25 -0700'),
|
311 | 'rfc822.size': 4286,
|
312 | envelope: {
|
313 | date: new Date('Wed, 17 Jul 1996 02:23:25 -0700 (PDT)'),
|
314 | subject: 'IMAP4rev1 WG mtg summary and minutes',
|
315 | from: [
|
316 | { name: 'Terry Gray',
|
317 | mailbox: 'gray',
|
318 | host: 'cac.washington.edu'
|
319 | }
|
320 | ],
|
321 | sender: [
|
322 | { name: 'Terry Gray',
|
323 | mailbox: 'gray',
|
324 | host: 'cac.washington.edu'
|
325 | }
|
326 | ],
|
327 | replyTo: [
|
328 | { name: 'Terry Gray',
|
329 | mailbox: 'gray',
|
330 | host: 'cac.washington.edu'
|
331 | }
|
332 | ],
|
333 | to: [
|
334 | { name: null,
|
335 | mailbox: 'imap',
|
336 | host: 'cac.washington.edu'
|
337 | }
|
338 | ],
|
339 | cc: [
|
340 | { name: null,
|
341 | mailbox: 'minutes',
|
342 | host: 'CNRI.Reston.VA.US'
|
343 | },
|
344 | { name: 'John Klensin',
|
345 | mailbox: 'KLENSIN',
|
346 | host: 'MIT.EDU'
|
347 | }
|
348 | ],
|
349 | bcc: null,
|
350 | inReplyTo: null,
|
351 | messageId: '<B27397-0100000@cac.washington.edu>'
|
352 | },
|
353 | body: [
|
354 | { partID: '1',
|
355 | type: 'text',
|
356 | subtype: 'plain',
|
357 | params: { charset: 'US-ASCII' },
|
358 | id: null,
|
359 | description: null,
|
360 | encoding: '7BIT',
|
361 | size: 3028,
|
362 | lines: 92
|
363 | }
|
364 | ]
|
365 | }
|
366 | }
|
367 | ],
|
368 | what: 'Untagged FETCH (flags, date, size, envelope, body[structure])'
|
369 | },
|
370 |
|
371 | { source: ['* ESEARCH (TAG "A285") UID MIN 7 MAX 3800', CRLF],
|
372 | expected: [ { type: 'esearch',
|
373 | num: undefined,
|
374 | textCode: undefined,
|
375 | text: { min: 7, max: 3800 }
|
376 | }
|
377 | ],
|
378 | what: 'ESearch UID, 2 items'
|
379 | },
|
380 | { source: ['* ESEARCH (TAG "A284") MIN 4', CRLF],
|
381 | expected: [ { type: 'esearch',
|
382 | num: undefined,
|
383 | textCode: undefined,
|
384 | text: { min: 4 }
|
385 | }
|
386 | ],
|
387 | what: 'ESearch 1 item'
|
388 | },
|
389 | { source: ['* ESEARCH (TAG "A283") ALL 2,10:11', CRLF],
|
390 | expected: [ { type: 'esearch',
|
391 | num: undefined,
|
392 | textCode: undefined,
|
393 | text: { all: [ '2', '10:11' ] }
|
394 | }
|
395 | ],
|
396 | what: 'ESearch ALL list'
|
397 | },
|
398 | { source: ['* QUOTA "" (STORAGE 10 512)', CRLF],
|
399 | expected: [ { type: 'quota',
|
400 | num: undefined,
|
401 | textCode: undefined,
|
402 | text: {
|
403 | root: '',
|
404 | resources: {
|
405 | storage: { usage: 10, limit: 512 }
|
406 | }
|
407 | }
|
408 | }
|
409 | ],
|
410 | what: 'Quota'
|
411 | },
|
412 | { source: ['* QUOTAROOT INBOX ""', CRLF],
|
413 | expected: [ { type: 'quotaroot',
|
414 | num: undefined,
|
415 | textCode: undefined,
|
416 | text: {
|
417 | roots: [ '' ],
|
418 | mailbox: 'INBOX'
|
419 | }
|
420 | }
|
421 | ],
|
422 | what: 'QuotaRoot'
|
423 | },
|
424 | ].forEach(function(v) {
|
425 | var ss = new require('stream').Readable(), p, result = [];
|
426 | ss._read = function(){};
|
427 |
|
428 | p = new Parser(ss);
|
429 | p.on('tagged', function(info) {
|
430 | result.push(info);
|
431 | });
|
432 | p.on('untagged', function(info) {
|
433 | result.push(info);
|
434 | });
|
435 | p.on('continue', function(info) {
|
436 | result.push(info);
|
437 | });
|
438 | p.on('other', function(line) {
|
439 | result.push(line);
|
440 | });
|
441 | p.on('body', function(stream, info) {
|
442 | result.push(info);
|
443 | if (Array.isArray(v.bodySHA1s)) {
|
444 | var hash = crypto.createHash('sha1');
|
445 | stream.on('data', function(d) {
|
446 | hash.update(d);
|
447 | });
|
448 | stream.on('end', function() {
|
449 | var calculated = hash.digest('hex'),
|
450 | expected = v.bodySHA1s.shift();
|
451 | assert.equal(calculated,
|
452 | expected,
|
453 | makeMsg(v.what,
|
454 | 'Body SHA1 mismatch:'
|
455 | + '\nCalculated: ' + calculated
|
456 | + '\nExpected: ' + expected
|
457 | )
|
458 | );
|
459 | });
|
460 | }
|
461 | });
|
462 |
|
463 | try {
|
464 | v.source.forEach(function(chunk) {
|
465 | ss.push(chunk);
|
466 | });
|
467 | } catch (e) {
|
468 | console.log(makeMsg(v.what, 'JS Exception: ' + e.stack));
|
469 | return;
|
470 | }
|
471 | assert.deepEqual(result,
|
472 | v.expected,
|
473 | makeMsg(v.what,
|
474 | 'Result mismatch:'
|
475 | + '\nParsed: ' + inspect(result, false, 10)
|
476 | + '\nExpected: ' + inspect(v.expected, false, 10)
|
477 | )
|
478 | );
|
479 | });
|
480 |
|
481 | function makeMsg(what, msg) {
|
482 | return '[' + what + ']: ' + msg;
|
483 | } |
\ | No newline at end of file |