initial version - hackathon
This commit is contained in:
commit
56bc5b62a6
11 changed files with 1928 additions and 0 deletions
175
imapParsing.ts
Normal file
175
imapParsing.ts
Normal file
|
@ -0,0 +1,175 @@
|
|||
export type ImapCommandParameters = string | string[] | Record<string, any>;
|
||||
|
||||
export function parseImapCommand(commandString: string): {
|
||||
tag: string;
|
||||
command: string;
|
||||
parameters: ImapCommandParameters[];
|
||||
} {
|
||||
const parts: string[] = [];
|
||||
let currentPart = "";
|
||||
let inQuotes = false;
|
||||
let parenDepth = 0;
|
||||
let bracketDepth = 0;
|
||||
|
||||
for (let i = 0; i < commandString.length; i++) {
|
||||
const char = commandString[i];
|
||||
|
||||
if (char === '"' && (i === 0 || commandString[i - 1] !== "\\")) {
|
||||
inQuotes = !inQuotes;
|
||||
currentPart += char;
|
||||
} else if (char === "(" && !inQuotes) {
|
||||
parenDepth++;
|
||||
currentPart += char;
|
||||
} else if (char === ")" && !inQuotes) {
|
||||
parenDepth--;
|
||||
currentPart += char;
|
||||
} else if (char === "[" && !inQuotes) {
|
||||
bracketDepth++;
|
||||
currentPart += char;
|
||||
} else if (char === "]" && !inQuotes) {
|
||||
bracketDepth--;
|
||||
currentPart += char;
|
||||
} else if (
|
||||
char === " " && !inQuotes && parenDepth === 0 && bracketDepth === 0
|
||||
) {
|
||||
if (currentPart) {
|
||||
parts.push(currentPart);
|
||||
currentPart = "";
|
||||
}
|
||||
} else {
|
||||
currentPart += char;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPart) {
|
||||
parts.push(currentPart);
|
||||
}
|
||||
|
||||
if (parts.length < 2) {
|
||||
throw new Error("Invalid command");
|
||||
}
|
||||
|
||||
const tag = parts[0];
|
||||
|
||||
let command = parts[1];
|
||||
let paramStartIdx = 2;
|
||||
|
||||
if (command.toUpperCase() === "UID") {
|
||||
command = `${command} ${parts[2]}`;
|
||||
paramStartIdx = 3;
|
||||
}
|
||||
|
||||
const parameters: ImapCommandParameters[] = [];
|
||||
|
||||
for (let i = paramStartIdx; i < parts.length; i++) {
|
||||
let part = parts[i];
|
||||
|
||||
if (part.startsWith('"') && part.endsWith('"')) {
|
||||
part = part.slice(1, -1);
|
||||
}
|
||||
|
||||
if (part.includes('\\"')) {
|
||||
parameters.push(part.split('\\"'));
|
||||
} else if (part.startsWith("(") && part.endsWith(")")) {
|
||||
parameters.push(parseDataItems(part));
|
||||
} else {
|
||||
parameters.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tag,
|
||||
command,
|
||||
parameters,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseDataItems(
|
||||
dataItemsString: string,
|
||||
): ImapCommandParameters[] {
|
||||
const inner = dataItemsString.slice(1, -1).trim();
|
||||
|
||||
if (!inner) return [];
|
||||
|
||||
const result: ImapCommandParameters[] = [];
|
||||
let current = "";
|
||||
let inQuotes = false;
|
||||
let parenDepth = 0;
|
||||
let bracketDepth = 0;
|
||||
let fullSectionId = "";
|
||||
|
||||
for (let i = 0; i < inner.length; i++) {
|
||||
const char = inner[i];
|
||||
|
||||
if (char === '"' && (i === 0 || inner[i - 1] !== "\\")) {
|
||||
inQuotes = !inQuotes;
|
||||
current += char;
|
||||
} else if (char === "[" && !inQuotes) {
|
||||
bracketDepth++;
|
||||
|
||||
if (
|
||||
current.toUpperCase().startsWith("BODY.PEEK") ||
|
||||
current.toUpperCase() === "BODY"
|
||||
) {
|
||||
fullSectionId = current + char;
|
||||
current += char;
|
||||
} else {
|
||||
current += char;
|
||||
}
|
||||
} else if (char === "(" && !inQuotes) {
|
||||
parenDepth++;
|
||||
|
||||
if (bracketDepth > 0 && fullSectionId) {
|
||||
fullSectionId = current;
|
||||
current = "";
|
||||
} else {
|
||||
current += char;
|
||||
}
|
||||
} else if (char === ")" && !inQuotes) {
|
||||
parenDepth--;
|
||||
|
||||
if (parenDepth === 0 && bracketDepth > 0 && fullSectionId) {
|
||||
const fieldList = current.split(/\s+/).filter(Boolean);
|
||||
current = fieldList;
|
||||
} else {
|
||||
current += char;
|
||||
}
|
||||
} else if (char === "]" && !inQuotes) {
|
||||
bracketDepth--;
|
||||
|
||||
if (bracketDepth === 0 && fullSectionId) {
|
||||
if (Array.isArray(current)) {
|
||||
const sectionObj: Record<string, string[]> = {};
|
||||
sectionObj[`${fullSectionId.trim()}]`] = current;
|
||||
result.push(sectionObj);
|
||||
} else {
|
||||
result.push(`${fullSectionId}${current}]`);
|
||||
}
|
||||
|
||||
fullSectionId = "";
|
||||
current = "";
|
||||
} else if (fullSectionId) {
|
||||
current += char;
|
||||
} else {
|
||||
current += char;
|
||||
}
|
||||
} else if (
|
||||
char === " " && !inQuotes && parenDepth === 0 && bracketDepth === 0
|
||||
) {
|
||||
if (current) {
|
||||
result.push(current);
|
||||
current = "";
|
||||
}
|
||||
} else {
|
||||
if (!Array.isArray(current)) {
|
||||
current += char;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current && !Array.isArray(current)) {
|
||||
result.push(current);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue