NeahNew/node_modules/imapflow/lib/handler/parser-instance.js
2025-05-03 14:17:46 +02:00

166 lines
5.7 KiB
JavaScript

/* eslint new-cap: 0 */
'use strict';
const imapFormalSyntax = require('./imap-formal-syntax');
const { TokenParser } = require('./token-parser');
class ParserInstance {
constructor(input, options) {
this.input = (input || '').toString();
this.options = options || {};
this.remainder = this.input;
this.pos = 0;
}
async getTag() {
if (!this.tag) {
this.tag = await this.getElement(imapFormalSyntax.tag() + '*+', true);
}
return this.tag;
}
async getCommand() {
if (this.tag === '+') {
// special case
this.humanReadable = this.remainder.trim();
this.remainder = '';
return '';
}
if (!this.command) {
this.command = await this.getElement(imapFormalSyntax.command());
}
switch ((this.command || '').toString().toUpperCase()) {
case 'OK':
case 'NO':
case 'BAD':
case 'PREAUTH':
case 'BYE':
{
let match = this.remainder.match(/^\s+\[/);
if (match) {
let nesting = 1;
for (let i = match[0].length; i <= this.remainder.length; i++) {
let c = this.remainder[i];
if (c === '[') {
nesting++;
} else if (c === ']') {
nesting--;
}
if (!nesting) {
this.humanReadable = this.remainder.substring(i + 1).trim();
this.remainder = this.remainder.substring(0, i + 1);
break;
}
}
} else {
this.humanReadable = this.remainder.trim();
this.remainder = '';
}
}
break;
}
return this.command;
}
async getElement(syntax) {
let match, element, errPos;
if (this.remainder.match(/^\s/)) {
let error = new Error(`Unexpected whitespace at position ${this.pos} [E1]`);
error.code = 'ParserError1';
error.parserContext = { input: this.input, pos: this.pos };
throw error;
}
if ((match = this.remainder.match(/^\s*[^\s]+(?=\s|$)/))) {
element = match[0];
if ((errPos = imapFormalSyntax.verify(element, syntax)) >= 0) {
if (this.tag === 'Server' && element === 'Unavailable.') {
// Exchange error
let error = new Error(`Server returned an error: ${this.input}`);
error.code = 'ParserErrorExchange';
error.parserContext = {
input: this.input,
element,
pos: this.pos,
value: {
tag: '*',
command: 'BAD',
attributes: [{ type: 'TEXT', value: this.input }]
}
};
throw error;
}
let error = new Error(`Unexpected char at position ${this.pos + errPos} [E2: ${JSON.stringify(element.charAt(errPos))}]`);
error.code = 'ParserError2';
error.parserContext = { input: this.input, element, pos: this.pos };
throw error;
}
} else {
let error = new Error(`Unexpected end of input at position ${this.pos} [E3]`);
error.code = 'ParserError3';
error.parserContext = { input: this.input, pos: this.pos };
throw error;
}
this.pos += match[0].length;
this.remainder = this.remainder.substr(match[0].length);
return element;
}
async getSpace() {
if (!this.remainder.length) {
if (this.tag === '+' && this.pos === 1) {
// special case, empty + response
return;
}
let error = new Error(`Unexpected end of input at position ${this.pos} [E4]`);
error.code = 'ParserError4';
error.parserContext = { input: this.input, pos: this.pos };
throw error;
}
if (imapFormalSyntax.verify(this.remainder.charAt(0), imapFormalSyntax.SP()) >= 0) {
let error = new Error(`Unexpected char at position ${this.pos} [E5: ${JSON.stringify(this.remainder.charAt(0))}]`);
error.code = 'ParserError5';
error.parserContext = { input: this.input, element: this.remainder, pos: this.pos };
throw error;
}
this.pos++;
this.remainder = this.remainder.substr(1);
}
async getAttributes() {
if (!this.remainder.length) {
let error = new Error(`Unexpected end of input at position ${this.pos} [E6]`);
error.code = 'ParserError6';
error.parserContext = { input: this.input, pos: this.pos };
throw error;
}
if (this.remainder.match(/^\s/)) {
let error = new Error(`Unexpected whitespace at position ${this.pos} [E7]`);
error.code = 'ParserError7';
error.parserContext = { input: this.input, element: this.remainder, pos: this.pos };
throw error;
}
const tokenParser = new TokenParser(this, this.pos, this.remainder, this.options);
return await tokenParser.getAttributes();
}
}
module.exports.ParserInstance = ParserInstance;