Projektstart
This commit is contained in:
216
backend/node_modules/imapflow/lib/commands/select.js
generated
vendored
Normal file
216
backend/node_modules/imapflow/lib/commands/select.js
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
'use strict';
|
||||
|
||||
const { encodePath, normalizePath, enhanceCommandError } = require('../tools.js');
|
||||
|
||||
// Selects a mailbox
|
||||
module.exports = async (connection, path, options) => {
|
||||
if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
|
||||
// nothing to do here
|
||||
return;
|
||||
}
|
||||
options = options || {};
|
||||
|
||||
path = normalizePath(connection, path);
|
||||
|
||||
if (!connection.folders.has(path)) {
|
||||
let folders = await connection.run('LIST', '', path);
|
||||
if (!folders) {
|
||||
throw new Error('Failed to fetch folders');
|
||||
}
|
||||
folders.forEach(folder => {
|
||||
connection.folders.set(folder.path, folder);
|
||||
});
|
||||
}
|
||||
|
||||
let folderListData = connection.folders.has(path) ? connection.folders.get(path) : false;
|
||||
|
||||
let response;
|
||||
try {
|
||||
let map = { path };
|
||||
if (folderListData) {
|
||||
['delimiter', 'specialUse', 'subscribed', 'listed'].forEach(key => {
|
||||
if (folderListData[key]) {
|
||||
map[key] = folderListData[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let extraArgs = [];
|
||||
if (connection.enabled.has('QRESYNC') && options.changedSince && options.uidValidity) {
|
||||
extraArgs.push([
|
||||
{ type: 'ATOM', value: 'QRESYNC' },
|
||||
[
|
||||
{ type: 'ATOM', value: options.uidValidity?.toString() },
|
||||
{ type: 'ATOM', value: options.changedSince.toString() }
|
||||
]
|
||||
]);
|
||||
map.qresync = true;
|
||||
}
|
||||
|
||||
let encodedPath = encodePath(connection, path);
|
||||
|
||||
let selectCommand = {
|
||||
command: !options.readOnly ? 'SELECT' : 'EXAMINE',
|
||||
arguments: [{ type: encodedPath.indexOf('&') >= 0 ? 'STRING' : 'ATOM', value: encodedPath }].concat(extraArgs || [])
|
||||
};
|
||||
|
||||
response = await connection.exec(selectCommand.command, selectCommand.arguments, {
|
||||
untagged: {
|
||||
OK: async untagged => {
|
||||
if (!untagged.attributes || !untagged.attributes.length) {
|
||||
return;
|
||||
}
|
||||
let section = !untagged.attributes[0].value && untagged.attributes[0].section;
|
||||
if (section && section.length > 1 && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
|
||||
let key = section[0].value.toLowerCase();
|
||||
let value;
|
||||
|
||||
if (typeof section[1].value === 'string') {
|
||||
value = section[1].value;
|
||||
} else if (Array.isArray(section[1])) {
|
||||
value = section[1].map(entry => (typeof entry.value === 'string' ? entry.value : false)).filter(entry => entry);
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case 'highestmodseq':
|
||||
key = 'highestModseq';
|
||||
if (/^[0-9]+$/.test(value)) {
|
||||
value = BigInt(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mailboxid':
|
||||
key = 'mailboxId';
|
||||
if (Array.isArray(value) && value.length) {
|
||||
value = value[0];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'permanentflags':
|
||||
key = 'permanentFlags';
|
||||
value = new Set(value);
|
||||
break;
|
||||
|
||||
case 'uidnext':
|
||||
key = 'uidNext';
|
||||
value = Number(value);
|
||||
break;
|
||||
|
||||
case 'uidvalidity':
|
||||
key = 'uidValidity';
|
||||
if (/^[0-9]+$/.test(value)) {
|
||||
value = BigInt(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
map[key] = value;
|
||||
}
|
||||
|
||||
if (section && section.length === 1 && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
|
||||
let key = section[0].value.toLowerCase();
|
||||
switch (key) {
|
||||
case 'nomodseq':
|
||||
key = 'noModseq';
|
||||
map[key] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
FLAGS: async untagged => {
|
||||
if (!untagged.attributes || (!untagged.attributes.length && Array.isArray(untagged.attributes[0]))) {
|
||||
return;
|
||||
}
|
||||
let flags = untagged.attributes[0].map(flag => (typeof flag.value === 'string' ? flag.value : false)).filter(flag => flag);
|
||||
map.flags = new Set(flags);
|
||||
},
|
||||
EXISTS: async untagged => {
|
||||
let num = Number(untagged.command);
|
||||
if (isNaN(num)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
map.exists = num;
|
||||
},
|
||||
VANISHED: async untagged => {
|
||||
await connection.untaggedVanished(
|
||||
untagged,
|
||||
// mailbox is not yet open, so use a dummy mailbox object
|
||||
{ path, uidNext: false, uidValidity: false }
|
||||
);
|
||||
},
|
||||
// we should only get an untagged FETCH for a SELECT/EXAMINE if QRESYNC was asked for
|
||||
FETCH: async untagged => {
|
||||
await connection.untaggedFetch(
|
||||
untagged,
|
||||
// mailbox is not yet open, so use a dummy mailbox object
|
||||
{ path, uidNext: false, uidValidity: false }
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let section = !response.response.attributes[0].value && response.response.attributes[0].section;
|
||||
if (section && section.length && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
|
||||
switch (section[0].value.toUpperCase()) {
|
||||
case 'READ-ONLY':
|
||||
map.readOnly = true;
|
||||
break;
|
||||
case 'READ-WRITE':
|
||||
default:
|
||||
map.readOnly = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
map.qresync &&
|
||||
// UIDVALIDITY must be the same
|
||||
(options.uidValidity !== map.uidValidity ||
|
||||
// HIGHESTMODSEQ response must be present
|
||||
!map.highestModseq ||
|
||||
// NOMODSEQ is not allowed
|
||||
map.noModseq)
|
||||
) {
|
||||
// QRESYNC does not apply here, so unset it
|
||||
map.qresync = false;
|
||||
}
|
||||
|
||||
let currentMailbox = connection.mailbox;
|
||||
connection.mailbox = false;
|
||||
|
||||
if (currentMailbox && currentMailbox.path !== path) {
|
||||
connection.emit('mailboxClose', currentMailbox);
|
||||
}
|
||||
|
||||
connection.mailbox = map;
|
||||
connection.currentSelectCommand = selectCommand;
|
||||
connection.state = connection.states.SELECTED;
|
||||
|
||||
if (!currentMailbox || currentMailbox.path !== path) {
|
||||
connection.emit('mailboxOpen', connection.mailbox);
|
||||
}
|
||||
|
||||
response.next();
|
||||
return map;
|
||||
} catch (err) {
|
||||
await enhanceCommandError(err);
|
||||
|
||||
if (connection.state === connection.states.SELECTED) {
|
||||
// reset selected state
|
||||
|
||||
let currentMailbox = connection.mailbox;
|
||||
|
||||
connection.mailbox = false;
|
||||
connection.currentSelectCommand = false;
|
||||
connection.state = connection.states.AUTHENTICATED;
|
||||
|
||||
if (currentMailbox) {
|
||||
connection.emit('mailboxClose', currentMailbox);
|
||||
}
|
||||
}
|
||||
|
||||
connection.log.warn({ err, cid: connection.id });
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user