mime decoder
This commit is contained in:
parent
80d8c8d28a
commit
59c7245010
@ -3,6 +3,10 @@ exports.register = function () {
|
|||||||
const MessageService = require("../../services/MessageService");
|
const MessageService = require("../../services/MessageService");
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const DailyStats = require(path.join(__dirname, '../../../db/models/DailyStats'));
|
const DailyStats = require(path.join(__dirname, '../../../db/models/DailyStats'));
|
||||||
|
const MimeDecoder = require(path.join(
|
||||||
|
__dirname,
|
||||||
|
"../../../utils/mimeDecoder"
|
||||||
|
));
|
||||||
|
|
||||||
plugin.store_message = async function (next, connection) {
|
plugin.store_message = async function (next, connection) {
|
||||||
const transaction = connection.transaction;
|
const transaction = connection.transaction;
|
||||||
@ -10,24 +14,31 @@ exports.register = function () {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the body content by processing the message stream buffers
|
// Get the body content by processing the message stream buffers
|
||||||
let body = '';
|
let body = "";
|
||||||
|
|
||||||
if (transaction.message_stream && transaction.message_stream._queue) {
|
if (transaction.message_stream && transaction.message_stream._queue) {
|
||||||
// Convert the buffer to string
|
// 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
|
// 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) {
|
if (parts.length > 1) {
|
||||||
// Get everything after the headers
|
// 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 = {
|
const messageData = {
|
||||||
from: transaction.mail_from.address(),
|
from: transaction.mail_from.address(),
|
||||||
to: transaction.rcpt_to.map((addr) => addr.address()).join(", "),
|
to: transaction.rcpt_to.map((addr) => addr.address()).join(", "),
|
||||||
subject: transaction.header ? transaction.header.get('subject') : '',
|
subject: decodedSubject,
|
||||||
body: body,
|
body: body,
|
||||||
headers: transaction.header ? transaction.header.headers_decoded : {},
|
headers: transaction.header ? transaction.header.headers_decoded : {},
|
||||||
};
|
};
|
||||||
@ -36,7 +47,10 @@ exports.register = function () {
|
|||||||
await DailyStats.incrementMessageCount();
|
await DailyStats.incrementMessageCount();
|
||||||
next();
|
next();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
connection.logerror(plugin, `Failed to store message: ${error.message}`);
|
connection.logerror(
|
||||||
|
plugin,
|
||||||
|
`Failed to store message: ${error.message}`
|
||||||
|
);
|
||||||
next();
|
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