mime decoder
This commit is contained in:
parent
80d8c8d28a
commit
59c7245010
@ -3,6 +3,10 @@ exports.register = function () {
|
||||
const MessageService = require("../../services/MessageService");
|
||||
const path = require('path');
|
||||
const DailyStats = require(path.join(__dirname, '../../../db/models/DailyStats'));
|
||||
const MimeDecoder = require(path.join(
|
||||
__dirname,
|
||||
"../../../utils/mimeDecoder"
|
||||
));
|
||||
|
||||
plugin.store_message = async function (next, connection) {
|
||||
const transaction = connection.transaction;
|
||||
@ -10,24 +14,31 @@ exports.register = function () {
|
||||
|
||||
try {
|
||||
// Get the body content by processing the message stream buffers
|
||||
let body = '';
|
||||
let body = "";
|
||||
|
||||
if (transaction.message_stream && transaction.message_stream._queue) {
|
||||
// Convert the buffer to string
|
||||
const fullMessage = Buffer.from(transaction.message_stream._queue[0]).toString('utf8');
|
||||
const fullMessage = Buffer.from(
|
||||
transaction.message_stream._queue[0]
|
||||
).toString("utf8");
|
||||
|
||||
// Split on double newline to separate headers and body
|
||||
const parts = fullMessage.split('\r\n\r\n');
|
||||
const parts = fullMessage.split("\r\n\r\n");
|
||||
if (parts.length > 1) {
|
||||
// Get everything after the headers
|
||||
body = parts.slice(1).join('\r\n\r\n').trim();
|
||||
body = parts.slice(1).join("\r\n\r\n").trim();
|
||||
}
|
||||
}
|
||||
|
||||
const rawSubject = transaction.header
|
||||
? transaction.header.get("subject")
|
||||
: "";
|
||||
const decodedSubject = MimeDecoder.decode(rawSubject);
|
||||
|
||||
const messageData = {
|
||||
from: transaction.mail_from.address(),
|
||||
to: transaction.rcpt_to.map((addr) => addr.address()).join(", "),
|
||||
subject: transaction.header ? transaction.header.get('subject') : '',
|
||||
subject: decodedSubject,
|
||||
body: body,
|
||||
headers: transaction.header ? transaction.header.headers_decoded : {},
|
||||
};
|
||||
@ -36,7 +47,10 @@ exports.register = function () {
|
||||
await DailyStats.incrementMessageCount();
|
||||
next();
|
||||
} catch (error) {
|
||||
connection.logerror(plugin, `Failed to store message: ${error.message}`);
|
||||
connection.logerror(
|
||||
plugin,
|
||||
`Failed to store message: ${error.message}`
|
||||
);
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
87
src/utils/mimeDecoder.js
Normal file
87
src/utils/mimeDecoder.js
Normal file
@ -0,0 +1,87 @@
|
||||
// mimeDecoder.js
|
||||
|
||||
/**
|
||||
* Decodes MIME encoded-word format strings commonly used in email headers
|
||||
* Handles both Base64 and Quoted-Printable encoding methods
|
||||
* Format: =?charset?encoding?encoded-text?=
|
||||
*/
|
||||
class MimeDecoder {
|
||||
/**
|
||||
* Decodes a complete MIME encoded-word string
|
||||
* @param {string} str - The MIME encoded string
|
||||
* @returns {string} - The decoded string
|
||||
*/
|
||||
static decode(str) {
|
||||
if (!str) return "";
|
||||
|
||||
// Remove any newlines that might be present
|
||||
str = str.replace(/\n/g, "");
|
||||
|
||||
// Regular expression to match MIME encoded-word format
|
||||
const mimeRegex = /=\?([^?]+)\?([BQbq])\?([^?]*)\?=/g;
|
||||
|
||||
return str.replace(mimeRegex, (match, charset, encoding, text) => {
|
||||
try {
|
||||
if (encoding.toUpperCase() === "B") {
|
||||
// Handle Base64 encoding
|
||||
const buffer = Buffer.from(text, "base64");
|
||||
return buffer.toString(this.normalizeCharset(charset));
|
||||
} else if (encoding.toUpperCase() === "Q") {
|
||||
// Handle Quoted-Printable encoding
|
||||
return this.decodeQuotedPrintable(text, charset);
|
||||
}
|
||||
return text;
|
||||
} catch (error) {
|
||||
console.error("Error decoding MIME string:", error);
|
||||
return match; // Return original string if decoding fails
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes charset names to Node.js compatible charset encodings
|
||||
* @param {string} charset - The charset name from the MIME string
|
||||
* @returns {string} - Normalized charset name
|
||||
*/
|
||||
static normalizeCharset(charset) {
|
||||
const charsetMap = {
|
||||
utf8: "utf8",
|
||||
"utf-8": "utf8",
|
||||
"iso-8859-1": "latin1",
|
||||
"iso8859-1": "latin1",
|
||||
"windows-1252": "latin1",
|
||||
};
|
||||
|
||||
return charsetMap[charset.toLowerCase()] || charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes Quoted-Printable encoded text
|
||||
* @param {string} text - The Quoted-Printable encoded text
|
||||
* @param {string} charset - The character set of the encoded text
|
||||
* @returns {string} - Decoded text
|
||||
*/
|
||||
static decodeQuotedPrintable(text, charset) {
|
||||
// Replace underscore with space (per QP spec)
|
||||
text = text.replace(/_/g, " ");
|
||||
|
||||
// Replace hex encoded characters
|
||||
text = text.replace(/=([0-9A-F]{2})/gi, (match, hex) => {
|
||||
try {
|
||||
return Buffer.from(hex, "hex").toString(this.normalizeCharset(charset));
|
||||
} catch (error) {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
// Example usage:
|
||||
/*
|
||||
const decodedSubject = MimeDecoder.decode('=?utf-8?B?TUVHQS1FbWFpbGJlc3TDpHRpZ3VuZyBlcmZvcmRlcmxpY2g=?=');
|
||||
console.log(decodedSubject); // Outputs: MEGA-Emailbestätigung erforderlich
|
||||
*/
|
||||
|
||||
module.exports = MimeDecoder;
|
||||
Loading…
x
Reference in New Issue
Block a user