imapflow 1.0.108 → 1.0.109

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ - 1.0.109 - 2022-09-29
4
+
5
+ - New method `downloadMany`
6
+
3
7
  - 1.0.102 - 2022-08-18
4
8
 
5
9
  - Added new option `{statusQuery: {<statusOptions>}` for `list()` method
package/lib/imap-flow.js CHANGED
@@ -2451,6 +2451,137 @@ class ImapFlow extends EventEmitter {
2451
2451
  };
2452
2452
  }
2453
2453
 
2454
+ /**
2455
+ * Fetch multiple attachments as Buffer values
2456
+ *
2457
+ * @param {SequenceString} range UID or sequence number for the message to fetch
2458
+ * @param {String} parts A list of bodystructure parts
2459
+ * @param {Object} [options]
2460
+ * @param {Boolean} [options.uid] If `true` then uses UID number instead of sequence number for `range`
2461
+ * @returns {Promise<Object>} Download data object
2462
+ *
2463
+ * @example
2464
+ * let mailbox = await client.mailboxOpen('INBOX');
2465
+ * // download body part nr '1.2' from latest message
2466
+ * let response = await client.downloadMany('*', ['2', '3]);
2467
+ * process.stdout.write(response[2].content)
2468
+ * process.stdout.write(response[3].content)
2469
+ */
2470
+ async downloadMany(range, parts, options) {
2471
+ if (!this.mailbox) {
2472
+ // no mailbox selected, nothing to do
2473
+ return {};
2474
+ }
2475
+
2476
+ options = Object.assign(
2477
+ {
2478
+ chunkSize: 64 * 1024,
2479
+ maxBytes: Infinity
2480
+ },
2481
+ options || {}
2482
+ );
2483
+
2484
+ let query = { bodyParts: [] };
2485
+
2486
+ for (let part of parts) {
2487
+ query.bodyParts.push(part + '.mime');
2488
+ query.bodyParts.push(part);
2489
+ }
2490
+
2491
+ let response = await this.fetchOne(range, query, options);
2492
+
2493
+ if (!response || !response.bodyParts) {
2494
+ return { response: false };
2495
+ }
2496
+
2497
+ let data = {};
2498
+
2499
+ for (let [part, content] of response.bodyParts) {
2500
+ let keyParts = part.split('.');
2501
+ if (keyParts.length === 1) {
2502
+ // content
2503
+ let key = keyParts[0];
2504
+ if (!data[key]) {
2505
+ data[key] = { content };
2506
+ } else {
2507
+ data[key].content = content;
2508
+ }
2509
+ } else if (keyParts.length === 2) {
2510
+ // header
2511
+ let key = keyParts[0];
2512
+ if (!data[key]) {
2513
+ data[key] = {};
2514
+ }
2515
+ if (!data[key].meta) {
2516
+ data[key].meta = {};
2517
+ }
2518
+
2519
+ let headers = new Headers(content);
2520
+ let contentType = libmime.parseHeaderValue(headers.getFirst('Content-Type'));
2521
+ let transferEncoding = libmime.parseHeaderValue(headers.getFirst('Content-Transfer-Encoding'));
2522
+ let disposition = libmime.parseHeaderValue(headers.getFirst('Content-Disposition'));
2523
+
2524
+ if (contentType.value.toLowerCase().trim()) {
2525
+ data[key].meta.contentType = contentType.value.toLowerCase().trim();
2526
+ }
2527
+
2528
+ if (contentType.params.charset) {
2529
+ data[key].meta.charset = contentType.params.charset.toLowerCase().trim();
2530
+ }
2531
+
2532
+ if (transferEncoding.value) {
2533
+ data[key].meta.encoding = transferEncoding.value
2534
+ .replace(/\(.*\)/g, '')
2535
+ .toLowerCase()
2536
+ .trim();
2537
+ }
2538
+
2539
+ if (disposition.value) {
2540
+ data[key].meta.disposition = disposition.value.toLowerCase().trim() || false;
2541
+ try {
2542
+ data[key].meta.disposition = libmime.decodeWords(data[key].meta.disposition);
2543
+ } catch (err) {
2544
+ // failed to parse disposition, keep as is (most probably an unknown charset is used)
2545
+ }
2546
+ }
2547
+
2548
+ if (contentType.params.format && contentType.params.format.toLowerCase().trim() === 'flowed') {
2549
+ data[key].meta.flowed = true;
2550
+ if (contentType.params.delsp && contentType.params.delsp.toLowerCase().trim() === 'yes') {
2551
+ data[key].meta.delSp = true;
2552
+ }
2553
+ }
2554
+
2555
+ let filename = disposition.params.filename || contentType.params.name || false;
2556
+ if (filename) {
2557
+ try {
2558
+ filename = this.libmime.decodeWords(filename);
2559
+ } catch (err) {
2560
+ // failed to parse filename, keep as is (most probably an unknown charset is used)
2561
+ }
2562
+ data[key].meta.filename = filename;
2563
+ }
2564
+ }
2565
+ }
2566
+
2567
+ for (let part of Object.keys(data)) {
2568
+ let meta = data[part].meta;
2569
+
2570
+ switch (meta.encoding) {
2571
+ case 'base64':
2572
+ data[part].content = data[part].content ? libbase64.decode(data[part].content.toString()) : null;
2573
+ break;
2574
+ case 'quoted-printable':
2575
+ data[part].content = data[part].content ? libqp.decode(data[part].content.toString()) : null;
2576
+ break;
2577
+ default:
2578
+ // keep as is, already a buffer
2579
+ }
2580
+ }
2581
+
2582
+ return data;
2583
+ }
2584
+
2454
2585
  async run(command, ...args) {
2455
2586
  command = command.toUpperCase();
2456
2587
  if (!this.commands.has(command)) {
package/lib/types.d.ts CHANGED
@@ -478,6 +478,22 @@ declare module "imapflow" {
478
478
  maxBytes?: number;
479
479
  chunkSize?: number;
480
480
  }): Promise<DownloadObject>;
481
+ /**
482
+ * Fetch multiple attachments as Buffer values
483
+ * @example
484
+ * let mailbox = await client.mailboxOpen('INBOX');
485
+ * // download body part nr '1.2' from latest message
486
+ * let response = await client.downloadMany('*', ['2', '3]);
487
+ * process.stdout.write(response[2].content)
488
+ * process.stdout.write(response[3].content)
489
+ * @param range - UID or sequence number for the message to fetch
490
+ * @param parts - A list of bodystructure parts
491
+ * @param [options.uid] - If `true` then uses UID number instead of sequence number for `range`
492
+ * @returns Download data object
493
+ */
494
+ downloadMany(range: SequenceString, parts: string, options?: {
495
+ uid?: boolean;
496
+ }): Promise<object>;
481
497
  /**
482
498
  * Opens a mailbox if not already open and returns a lock. Next call to `getMailboxLock()` is queued
483
499
  * until previous lock is released. This is suggested over {@link module:imapflow~ImapFlow#mailboxOpen|mailboxOpen()} as
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imapflow",
3
- "version": "1.0.108",
3
+ "version": "1.0.109",
4
4
  "description": "IMAP Client for Node",
5
5
  "main": "./lib/imap-flow.js",
6
6
  "scripts": {
@@ -30,10 +30,10 @@
30
30
  "@babel/eslint-parser": "7.19.1",
31
31
  "@babel/eslint-plugin": "7.19.1",
32
32
  "@babel/plugin-syntax-class-properties": "7.12.13",
33
- "@babel/preset-env": "7.19.1",
34
- "@types/node": "18.7.18",
33
+ "@babel/preset-env": "7.19.3",
34
+ "@types/node": "18.7.23",
35
35
  "braintree-jsdoc-template": "3.3.0",
36
- "eslint": "8.23.1",
36
+ "eslint": "8.24.0",
37
37
  "eslint-config-nodemailer": "1.2.0",
38
38
  "eslint-config-prettier": "8.5.0",
39
39
  "grunt": "1.5.3",
@@ -51,8 +51,8 @@
51
51
  "libmime": "5.1.0",
52
52
  "libqp": "1.1.0",
53
53
  "mailsplit": "5.3.2",
54
- "nodemailer": "6.7.8",
55
- "pino": "8.6.0",
54
+ "nodemailer": "6.8.0",
55
+ "pino": "8.6.1",
56
56
  "socks": "2.7.0"
57
57
  }
58
58
  }