"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = status; const tools_js_1 = require("../tools.js"); const status_fields_js_1 = require("./status-fields.js"); const errors_js_1 = require("../errors.js"); // STATUS fields that also refresh the live mailbox state when the queried mailbox is the // currently selected one. Keyed by the output property name parseStatusList() reports. const MAILBOX_UPDATERS = { messages: (value, mailbox, connection, path) => { let prevCount = mailbox.exists; if (prevCount !== value) { mailbox.exists = value; (0, tools_js_1.emitSafe)(connection, 'exists', { path, count: value, prevCount }); } }, uidNext: (value, mailbox) => { mailbox.uidNext = value; }, highestModseq: (value, mailbox, connection, path) => { let prevHighestModseq = mailbox.highestModseq; if (prevHighestModseq !== value) { mailbox.highestModseq = value; // the changes behind it are not reported by STATUS, the event is the cue to resync (0, tools_js_1.emitSafe)(connection, 'highestModseq', { path, highestModseq: value, prevHighestModseq }); } } }; /** * Requests status information about a mailbox. * * @param connection - IMAP connection instance * @param path - Mailbox path to query * @param query - Status data items to request (e.g., {messages: true, uidNext: true, unseen: true}) * @returns Status information object, or false if preconditions not met or on failure * @throws {Error} If the mailbox does not exist */ async function status(connection, path, query) { if (!(0, tools_js_1.isAuthenticatedState)(connection) || !path) { // nothing to do here return false; } path = (0, tools_js_1.normalizePath)(connection, path); let encodedPath = (0, tools_js_1.encodePath)(connection, path); // Use quoted STRING if the encoded path contains '&' (modified UTF-7 marker), // otherwise use unquoted ATOM. Same approach as in SELECT. let attributes = [{ type: encodedPath.indexOf('&') >= 0 ? 'STRING' : 'ATOM', value: encodedPath }]; // Build the list of STATUS data items the caller wants let queryAttributes = (0, tools_js_1.buildStatusQueryAttributes)(connection, query); // RECENT does not exist in IMAP4rev2 so it is never requested from a rev2 // session; its defined value there is always 0. Synthesizing it keeps the // return shape identical to a rev1 session for the same query. let syntheticRecent = query && query.recent && (0, tools_js_1.isRev2Active)(connection); if (!queryAttributes.length) { // A query that only contained items unavailable on this session - the // caller still gets a status object if every such item has a defined value return syntheticRecent ? { path, recent: 0 } : false; } attributes.push(queryAttributes); let response; try { let map = { path }; response = await connection.exec('STATUS', attributes, { untagged: { // STATUS response: * STATUS ( ...) // Parsed as alternating key-value pairs (i % 2 pattern). STATUS: async (untagged) => { // If querying the currently selected mailbox, also update the // connection's live mailbox state and emit events for changes. let selected = (0, tools_js_1.getSelectedMailbox)(connection); let currentMailbox = selected && selected.path === path ? selected : false; let list = untagged.attributes && Array.isArray(untagged.attributes[1]) ? untagged.attributes[1] : false; if (!list) { return; } let updates = []; (0, status_fields_js_1.parseStatusList)(list, (key, value) => { map[key] = value; if (currentMailbox && MAILBOX_UPDATERS[key]) { updates.push([key, value]); } }); // Under a new UIDVALIDITY the counts describe a mailbox the selected view no // longer matches, so they are not applied to it; the STATUS poll selects the // mailbox again instead, which reports it as opened anew if (currentMailbox && map.uidValidity && currentMailbox.uidValidity && map.uidValidity !== currentMailbox.uidValidity) { return; } for (let [key, value] of updates) { MAILBOX_UPDATERS[key](value, currentMailbox, connection, path); } } } }); response.next(); if (syntheticRecent) { map.recent = 0; } return map; } catch (err) { // A NO response usually means the mailbox doesn't exist. Verify by // running LIST: if no results, throw a clear NotFound error instead // of the generic IMAP error. // Note: this uses run(), so when STATUS was dispatched by fallback polling through // runInternal() the LIST awaits that polling session's own preCheck and cancels it. // Not a deadlock, and only reachable when the server rejects the STATUS, but a polled // STATUS of a missing folder ends the poll early. if (err.responseStatus === 'NO') { // A failing probe (lost connection, throttling) answers nothing about the mailbox, // so STATUS then fails the same way as any other failed STATUS let folders = await connection.run('LIST', '', path, { listOnly: true }).catch((listErr) => { (0, tools_js_1.logConnectionError)(connection, 'Failed to check if the mailbox exists', listErr); return false; }); if (folders && !folders.length) { throw (0, errors_js_1.createImapError)(`Mailbox doesn't exist: ${path}`, 'NotFound', { response: err }); } } connection.log.warn({ err, cid: connection.id }); return false; } } module.exports = exports.default; Object.defineProperty(module.exports, 'default', { value: exports.default, enumerable: false, writable: true, configurable: true });