import {RfcMailAddress} from "./rfc_mail_address.js" import {Received} from "./received.js" import {ContentTransferEncoding, MailPriority} from "../../enums.js" import {unquote} from "../../utils/utils.js" import {decode as decodeRfc2047} from "./rfc2047.js" import type {ContentDisposition, ContentType} from "./header_field_parser.js" import {HeaderFieldParser} from "./header_field_parser.js" type HeaderDict = Record> export class MessageHeader { /** Contains all the headers as a map */ raw: HeaderDict /// /// All headers which were not recognized and explicitly dealt with.
/// This should mostly be custom headers, which are marked as X-[name].
///
/// This list will be empty if all headers were recognized and parsed. ///
custom: HeaderDict = {} /// /// A human readable description of the body
///
/// if no Content-Description header was present in the message. ///
contentDescription: string | null = null /// /// ID of the content part (like an attached image). Used with MultiPart messages.
///
/// if no Content-ID header field was present in the message. ///
/// For an ID of the message contentId: string | null = null /// /// Message keywords
///
/// The list will be empty if no Keywords header was present in the message ///
keywords: Array = [] /// /// A List of emails to people who wishes to be notified when some event happens.
/// These events could be email: /// /// deletion /// printing /// received /// ... /// /// The list will be empty if no Disposition-Notification-To header was present in the message ///
/// See RFC 3798 for details dispositionNotificationTo: Array = [] /// /// This is the Received headers. This tells the path that the email went.
///
/// The list will be empty if no Received header was present in the message ///
received: Array = [] /// /// Importance of this email.
///
/// The importance level is set to normal, if no Importance header field was mentioned or it contained /// unknown information. This is the expected behavior according to the RFC. ///
importance: MailPriority = MailPriority.Normal /// /// This header describes the Content encoding during transfer.
///
/// If no Content-Transfer-Encoding header was present in the message, it is set /// to the default of SevenBit in accordance to the RFC. ///
/// See RFC 2045 section 6 for details contentTransferEncoding: ContentTransferEncoding = ContentTransferEncoding.SevenBit /// /// Specifies who this mail was for
///
/// The list will be empty if no To header was present in the message ///
to: Array = [] /// /// Carbon Copy. This specifies who got a copy of the message.
///
/// The list will be empty if no Cc header was present in the message ///
cc: Array = [] /// /// Blind Carbon Copy. This specifies who got a copy of the message, but others /// cannot see who these persons are.
///
/// The list will be empty if no Received Bcc was present in the message ///
bcc: Array = [] /// /// Specifies who sent the email
///
/// if no From header field was present in the message ///
from: RfcMailAddress | null = null /// /// Specifies who a reply to the message should be sent to
///
/// if no Reply-To header field was present in the message ///
replyTo: RfcMailAddress | null = null /// /// The message identifier(s) of the original message(s) to which the /// current message is a reply.
///
/// The list will be empty if no In-Reply-To header was present in the message ///
inReplyTo: Array = [] /// /// The message identifier(s) of other message(s) to which the current /// message is related to.
///
/// The list will be empty if no References header was present in the message ///
references: Array = [] /// /// This is the sender of the email address.
///
/// if no Sender header field was present in the message ///
/// /// The RFC states that this field can be used if a secretary /// is sending an email for someone she is working for. /// The email here will then be the secretary's email, and /// the Reply-To field would hold the address of the person she works for.
/// RFC states that if the Sender is the same as the From field, /// sender should not be included in the message. ///
sender: RfcMailAddress | null = null /// /// The Content-Type header field.
///
/// If not set, the ContentType is created by the default "text/plain; charset=us-ascii" which is /// defined in RFC 2045 section 5.2.
/// If set, the default is overridden. ///
contentType: ContentType | null = null /// /// Used to describe if a message part is to be displayed or to be though of as an attachment.
/// Also contains information about filename if such was sent.
///
/// if no Content-Disposition header field was present in the message ///
contentDisposition: ContentDisposition | null = null /// /// The Date when the email was sent.
/// This is the raw value. for a parsed up value of this field.
///
/// if no Date header field was present in the message or if the date could not be /// parsed. ///
/// See RFC 5322 section 3.6.1 for more details date: string | null = null /// /// The Date when the email was sent.
/// This is the parsed equivalent of .
/// Notice that the of the object is in UTC and has NOT been converted /// to local . ///
/// See RFC 5322 section 3.6.1 for more details dateSent: number | null = null /// /// An ID of the message that is SUPPOSED to be in every message according to the RFC.
/// The ID is unique.
///
/// if no Message-ID header field was present in the message ///
messageId: string | null = null /// /// The Mime Version.
/// This field will almost always show 1.0
///
/// if no Mime-Version header field was present in the message ///
mimeVersion: string | null = null /// /// A single with no username inside.
/// This is a trace header field, that should be in all messages.
/// Replies should be sent to this address.
///
/// if no Return-Path header field was present in the message ///
returnPath: RfcMailAddress | null = null /// /// The subject line of the message in decoded, one line state.
/// This should be in all messages.
///
/// if no Subject header field was present in the message ///
subject: string | null = null private _clear(): void { this.raw = {} this.custom = {} this.keywords = [] this.dispositionNotificationTo = [] this.received = [] this.importance = MailPriority.Normal this.contentTransferEncoding = ContentTransferEncoding.SevenBit this.to = [] this.cc = [] this.bcc = [] this.inReplyTo = [] this.references = [] } constructor(rawHeaders: HeaderDict) { if (rawHeaders == null) throw new Error("rawHeaders must not be null!") this._clear() this.raw = rawHeaders this._parseHeaders(rawHeaders) } private _parseHeaders(raw: HeaderDict): void { if (raw == null) throw new Error("raw must not be null!") for (let name of Object.keys(raw)) { const values = raw[name] for (let value of values) { if (value == null) continue this._parseHeader(name, value) } } } private _parseHeader(name: string, value: string) { if (name == null) throw new Error("name must not be null!") if (value == null) throw new Error("value must not be null!") switch (name.toUpperCase()) { // See http://tools.ietf.org/html/rfc5322#section-3.6.3 case "TO": this.to = RfcMailAddress.parseMailAddresses(value) break // See http://tools.ietf.org/html/rfc5322#section-3.6.3 case "CC": this.cc = RfcMailAddress.parseMailAddresses(value) break // See http://tools.ietf.org/html/rfc5322#section-3.6.3 case "BCC": this.bcc = RfcMailAddress.parseMailAddresses(value) break // See http://tools.ietf.org/html/rfc5322#section-3.6.2 case "FROM": // There is only one MailAddress in the from field this.from = RfcMailAddress.parseMailAddress(value) break // http://tools.ietf.org/html/rfc5322#section-3.6.2 // The implementation here might be wrong case "REPLY-TO": // This field may actually be a list of addresses, but no // such case has been encountered this.replyTo = RfcMailAddress.parseMailAddress(value) break // http://tools.ietf.org/html/rfc5322#section-3.6.2 case "SENDER": this.sender = RfcMailAddress.parseMailAddress(value) break // See http://tools.ietf.org/html/rfc5322#section-3.6.5 // RFC 5322: // The "Keywords:" field contains a comma-separated list of one or more // words or quoted-strings. // The field are intended to have only human-readable content // with information about the message case "KEYWORDS": this.keywords.concat(value.split(",").map((v) => v.trim()).map(unquote)) break // See http://tools.ietf.org/html/rfc5322#section-3.6.7 case "RECEIVED": // Simply add the value to the list this.received.push(new Received(value.trim())) break case "IMPORTANCE": // TODO: this.importance = HeaderFieldParser.parseImportance(value.trim()) break // See http://tools.ietf.org/html/rfc3798#section-2.1 case "DISPOSITION-NOTIFICATION-TO": this.dispositionNotificationTo = RfcMailAddress.parseMailAddresses(value) break case "MIME-VERSION": this.mimeVersion = value.trim() break // See http://tools.ietf.org/html/rfc5322#section-3.6.5 case "SUBJECT": this.subject = decodeRfc2047(value) break // See http://tools.ietf.org/html/rfc5322#section-3.6.7 case "RETURN-PATH": // Return-paths does not include a username, but we // may still use the address parser this.returnPath = RfcMailAddress.parseMailAddress(value) break // See http://tools.ietf.org/html/rfc5322#section-3.6.4 // Example Message-ID // <33cdd74d6b89ab2250ecd75b40a41405@nfs.eksperten.dk> case "MESSAGE-ID": // TODO: this.messageId = HeaderFieldParser.parseId(value) break // See http://tools.ietf.org/html/rfc5322#section-3.6.4 case "IN-REPLY-TO": this.inReplyTo = HeaderFieldParser.parseMultipleIds(value) break // See http://tools.ietf.org/html/rfc5322#section-3.6.4 case "REFERENCES": this.references = HeaderFieldParser.parseMultipleIds(value) break // See http://tools.ietf.org/html/rfc5322#section-3.6.1)) case "DATE": this.date = value.trim() this.dateSent = Date.parse(this.date) break // See http://tools.ietf.org/html/rfc2045#section-6 // See ContentTransferEncoding class for more details case "CONTENT-TRANSFER-ENCODING": // TODO: this.contentTransferEncoding = HeaderFieldParser.parseContentTransferEncoding(value.trim()) break // See http://tools.ietf.org/html/rfc2045#section-8 case "CONTENT-DESCRIPTION": // Human description of for example a file. Can be encoded this.contentDescription = decodeRfc2047(value.trim()) break // See http://tools.ietf.org/html/rfc2045#section-5.1 // Example: Content-type: text/plain; charset="us-ascii" case "CONTENT-TYPE": // tODO this.contentType = HeaderFieldParser.parseContentType(value) break // See http://tools.ietf.org/html/rfc2183 case "CONTENT-DISPOSITION": // TODO this.contentDisposition = HeaderFieldParser.parseContentDisposition(value) break // See http://tools.ietf.org/html/rfc2045#section-7 // Example: case "CONTENT-ID": // TODO this.contentId = HeaderFieldParser.parseId(value) break default: // This is an unknown header // Custom headers are allowed. That means headers // that are not mentioned in the RFC. // Such headers start with the letter "X" // We do not have any special parsing of such // Add it to custom headers if (this.custom[name] != null) { this.custom[name].push(value) } else { this.custom[name] = [value] } break } } }