andrewpietiladotcom/lib/MessageIdMimeHeader.js

166 lines
No EOL
7.4 KiB
JavaScript

import MimeHeader from "./MimeHeader.js";
const atext = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-/=?^_`{|}~".split('');
const dtext = "!\"#$%^&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~".split('');
class MessageIdMimeHeader extends MimeHeader {
constructor(key, value) {
super(key, value);
this._messageIdValue = "";
}
// Message-ID: <0Q27kpOFnz1MK3ASM_vbjI8L_BYN3j5OSxIaGxerST74zBn7VZfqCowbhEWgJ0Yv4mV847u25YGjENqlsM7-15Zdus90qN7t_kj55FyuN_c=@protonmail.com>
get messageIdValue() {
// ABNF:
// message-id = "Message-ID:" msg-id CRLF
// msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS]
// id-left = dot-atom-text / obs-id-left
// obs-id-left = local-part
// id-right = dot-atom-text / no-fold-literal / obs-id-right
// obs-id-right = domain
// no-fold-literal = "[" *dtext "]"
// dtext = %d33-90 / ; Printable US-ASCII
// %d94-126 / ; characters not including
// obs-dtext ; "[", "]", or "\"
// dot-atom-text = 1*atext *("." 1*atext)
// atext = ALPHA / DIGIT / ; Printable US-ASCII
// "!" / "#" / ; characters not including
// "$" / "%" / ; specials. Used for atoms.
// "&" / "'" /
// "*" / "+" /
// "-" / "/" /
// "=" / "?" /
// "^" / "_" /
// "`" / "{" /
// "|" / "}" /
// "~"
// dtext = %d33-90 / ; Printable US-ASCII
// %d94-126 / ; characters not including
// obs-dtext ; "[", "]", or "\"
// CFWS = (1*([FWS] comment) [FWS]) / FWS
// FWS = ([*WSP CRLF] 1*WSP) / obs-FWS
// ; Folding white space
// comment = "(" *([FWS] ccontent) [FWS] ")"
// ccontent = ctext / quoted-pair / comment
// WSP = SP / HTAB ; white space
// obs-FWS = 1*WSP *(CRLF 1*WSP)
// quoted-pair = ("\" (VCHAR / WSP)) / obs-qp
// obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR)
// obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
// %d11 / ; characters that do not
// %d12 / ; include the carriage
// %d14-31 / ; return, line feed, and
// %d127 ; white space characters
// TODO: Implement utf-8 encoding and whatever other encodings we have to support.
if ( this._messageIdValue !== "" ) {
return this._messageIdValue;
}
// TODO: Implement this state machine properly according to the above ABNF.
var state = "";
var inCRLF = false;
var commentDepth = 0;
var inDquot = false;
var idLeft = "";
var idRight = "";
var idRightDatEmittedRightSquacket = false;
for (var char of this.rawValue) {
if ( inCRLF === true && char !== "\n" ) {
return null; // Error state.
}
if (state === "") {
// CFWS
if ( char === " ") {
continue;
} else if ( char === "\t") {
continue;
} else if ( char === "(" && !inDquot ) {
commentDepth++;
} else if ( char === "\"" ) {
inDquot = !inDquot;
} else if ( char === ")" && !inDquot ) {
commentDepth--;
} else if ( char === "\r" ) {
inCRLF = true;
} else if ( char === "\n" ) {
inCRLF = false;
} else if ( char === "<" ) {
state = "id-left";
} else {
return null; // Error state, we couldn't produce a proper message ID from the input.
}
} else if ( state === "id-left" ) {
if ( atext.includes(char) ) {
idLeft += char;
} else if ( char === "." ) {
if ( idLeft.endsWith(".") || idLeft.length === 0 ) {
return null; // Error state.
}
idLeft += char;
} else if ( char === "@" ) {
if ( idLeft.endsWith(".") || idLeft.length === 0) {
return null; // Error state.
}
state = "id-right";
}
} else if ( state === "id-right" ) {
if ( char === "[" ) {
if ( idRight.length !== 0 ) {
return null; // Error state.
}
idRight += char;
state = "id-right-dat";
} else if ( char === "." ) {
if ( idRight.endsWith(".") || idRight.length === 0 ) {
return null; // Error state.
}
idRight += char;
} else if ( char === ">" ) {
if ( idRight.endsWith(".") || idRight.length === 0) {
return null; // Error state.
}
state = "end-cfws"
}
} else if ( state === "id-right-dat" ) {
if ( char === ">" && idRight.endsWith("]") ) {
state = "end-cfws";
continue;
}
if ( dtext.includes(char) ) {
if ( idRightDatEmittedRightSquacket ) {
return null; // Error state.
}
idRight += char;
} else if ( char === "]" ) {
if ( idRightDatEmittedRightSquacket ) {
return null; // Error state.
}
idRight += char;
idRightDatEmittedRightSquacket = true;
}
} else if ( state === "end-cfws" ) {
// CFWS
if ( char === " ") {
continue;
} else if ( char === "\t") {
continue;
} else if ( char === "(" && !inDquot ) {
commentDepth++;
} else if ( char === "\"" ) {
inDquot = !inDquot;
} else if ( char === ")" && !inDquot ) {
commentDepth--;
} else if ( char === "\r" ) {
inCRLF = true;
} else if ( char === "\n" ) {
inCRLF = false;
} else {
return null; // Error state.
}
}
}
this._messageIdValue = `${idLeft}@${idRight}`;
return `${idLeft}@${idRight}`;
}
}
export default MessageIdMimeHeader;