Add inReplyTo header support.
This commit is contained in:
parent
bfa6e38cdc
commit
c93c2fa8c8
3 changed files with 174 additions and 2 deletions
168
lib/InReplyToMimeHeader.js
Normal file
168
lib/InReplyToMimeHeader.js
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
import MimeHeader from "./MimeHeader.js";
|
||||||
|
|
||||||
|
const atext = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-/=?^_`{|}~".split('');
|
||||||
|
const dtext = "!\"#$%^&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~".split('');
|
||||||
|
|
||||||
|
class InReplyToMimeHeader extends MimeHeader {
|
||||||
|
constructor(key, value) {
|
||||||
|
super(key, value);
|
||||||
|
this._messageIdValue = "";
|
||||||
|
}
|
||||||
|
// Message-ID: <0Q27kpOFnz1MK3ASM_vbjI8L_BYN3j5OSxIaGxerST74zBn7VZfqCowbhEWgJ0Yv4mV847u25YGjENqlsM7-15Zdus90qN7t_kj55FyuN_c=@protonmail.com>
|
||||||
|
get inReplyToValues() {
|
||||||
|
// ABNF:
|
||||||
|
// in-reply-to = "In-Reply-To:" 1*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 "\"
|
||||||
|
// 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 !== undefined ) {
|
||||||
|
return this._messageIdValue;
|
||||||
|
}
|
||||||
|
// TODO: Implement this state machine properly according to the above ABNF.
|
||||||
|
var inReplyTo = [];
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
inReplyTo.push(`${idLeft}@${idRight}`);
|
||||||
|
idLeft = "";
|
||||||
|
idRight = "";
|
||||||
|
state = "intermediate-or-end-cfws"
|
||||||
|
}
|
||||||
|
} else if ( state === "id-right-dat" ) {
|
||||||
|
if ( char === ">" && idRight.endsWith("]") ) {
|
||||||
|
state = "intermediate-or-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 === "intermediate-or-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 if ( char === "<" ) {
|
||||||
|
state = "id-left";
|
||||||
|
} else {
|
||||||
|
return null; // Error state.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inReplyTo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InReplyToMimeHeader;
|
|
@ -1,14 +1,17 @@
|
||||||
import MimeHeader from "./MimeHeader.js";
|
import MimeHeader from "./MimeHeader.js";
|
||||||
import MessageIdMimeHeader from "./MessageIdMimeHeader.js";
|
import MessageIdMimeHeader from "./MessageIdMimeHeader.js";
|
||||||
|
import InReplyToMimeHeader from "./InReplyToMimeHeader.js";
|
||||||
|
|
||||||
function MimeHeaderFactory(key, value) {
|
function MimeHeaderFactory(key, value) {
|
||||||
if ( value !== undefined ) {
|
if ( value !== undefined ) {
|
||||||
return new MimeHeader(`${key}: ${value}`);
|
return new MimeHeader(`${key}: ${value}`);
|
||||||
}
|
}
|
||||||
if ( key.toLowerCase().startsWith("message-id") ) {
|
if ( key.toLowerCase().startsWith("message-id:") || key.toLowerCase() === "message-id" ) {
|
||||||
return new MessageIdMimeHeader(key, value);
|
return new MessageIdMimeHeader(key, value);
|
||||||
|
} else if ( key.toLowerCase().startsWith("in-reply-to:") || key.toLowercase() === "in-reply-to" ) {
|
||||||
|
return new InReplyToMimeHeader(key, value);
|
||||||
}
|
}
|
||||||
return new MimeHeader(key);
|
return new MimeHeader(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MimeHeaderFactory;
|
export default MimeHeaderFactory;
|
|
@ -242,6 +242,7 @@ app.post("/api/jmap/api/", bodyParser.json(), async (req, res) => {
|
||||||
// mailboxIds: {INBOX: true} // TODO: Implement more mailboxes.
|
// mailboxIds: {INBOX: true} // TODO: Implement more mailboxes.
|
||||||
// keywords: keywords table, {keyword_column: true}
|
// keywords: keywords table, {keyword_column: true}
|
||||||
// messageId: new MimeMessage().getFirstHeaderOf("Message-Id").messageIdValue
|
// messageId: new MimeMessage().getFirstHeaderOf("Message-Id").messageIdValue
|
||||||
|
// inReplyTo: new MimeMessage().getFirstHeadderOf("In-Reply-To").inReplyToValues
|
||||||
|
|
||||||
return [
|
return [
|
||||||
"error",
|
"error",
|
||||||
|
|
Loading…
Add table
Reference in a new issue