Initial commit on Forgejo
Fresh repository history for elektrine/elektrine-haraka hosted at https://git.elektrine.com/elektrine/elektrine-haraka.
This commit is contained in:
commit
67c1d41a0c
82 changed files with 8713 additions and 0 deletions
181
lib/attachment-handler.js
Normal file
181
lib/attachment-handler.js
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
/**
|
||||
* Attachment Handler Module
|
||||
*
|
||||
* Extracts and processes email attachments from parsed emails.
|
||||
* Converts attachment content to base64 for API transmission.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Extract attachment information from a parsed email
|
||||
*
|
||||
* @param {Object} transaction - Haraka transaction object
|
||||
* @param {Object} parsed - Parsed email from mailparser
|
||||
* @param {Object} [options] - Options
|
||||
* @param {boolean} [options.include_content=true] - Include base64 content
|
||||
* @param {boolean} [options.debug=false] - Enable debug logging
|
||||
* @param {Function} [options.logger] - Logger function
|
||||
* @returns {Object} Object with attachments array and count
|
||||
*/
|
||||
function extract(transaction, parsed, options = {}) {
|
||||
const {
|
||||
include_content = true,
|
||||
debug = false,
|
||||
logger = console.log
|
||||
} = options;
|
||||
|
||||
if (debug) logger('Extracting attachment information');
|
||||
|
||||
let attachments = [];
|
||||
let attachment_count = 0;
|
||||
|
||||
// Primary: Use mailparser attachments (best source for content)
|
||||
if (parsed && parsed.attachments && parsed.attachments.length > 0) {
|
||||
if (debug) logger(`Found ${parsed.attachments.length} parsed attachments`);
|
||||
|
||||
attachment_count = parsed.attachments.length;
|
||||
attachments = parsed.attachments.map((att, index) => {
|
||||
return format_attachment(att, index, include_content, debug, logger);
|
||||
});
|
||||
}
|
||||
// Fallback: Check transaction notes for attachment plugin results
|
||||
else if (transaction && transaction.notes && transaction.notes.attachment) {
|
||||
if (debug) logger('Using attachment notes fallback');
|
||||
|
||||
const att_notes = transaction.notes.attachment;
|
||||
|
||||
if (att_notes.files && Array.isArray(att_notes.files)) {
|
||||
attachment_count = att_notes.files.length;
|
||||
attachments = att_notes.files.map((file, index) => ({
|
||||
filename: file.filename || file.name || `attachment_${index}`,
|
||||
content_type: file.ctype || file.content_type || 'application/octet-stream',
|
||||
size: file.bytes || file.size || 0,
|
||||
md5: file.md5,
|
||||
content: null, // No content available from plugin notes
|
||||
encoding: 'base64',
|
||||
index: index
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
const with_content = attachments.filter(a => a.content).length;
|
||||
logger(`Final attachment count: ${attachment_count}, with content: ${with_content}`);
|
||||
}
|
||||
|
||||
return {
|
||||
attachments,
|
||||
count: attachment_count,
|
||||
has_attachments: attachment_count > 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a single attachment for API transmission
|
||||
*
|
||||
* @param {Object} att - Attachment object from mailparser
|
||||
* @param {number} index - Attachment index
|
||||
* @param {boolean} include_content - Include base64 content
|
||||
* @param {boolean} debug - Debug mode
|
||||
* @param {Function} logger - Logger function
|
||||
* @returns {Object} Formatted attachment object
|
||||
*/
|
||||
function format_attachment(att, index, include_content, debug, logger) {
|
||||
let content_base64 = null;
|
||||
|
||||
if (include_content && att.content) {
|
||||
content_base64 = to_base64(att.content);
|
||||
}
|
||||
|
||||
const attachment_info = {
|
||||
filename: att.filename || att.name || `attachment_${index}`,
|
||||
content_type: att.contentType || att.content_type || 'application/octet-stream',
|
||||
size: att.size || (att.content ? att.content.length : 0),
|
||||
content_id: att.cid || null,
|
||||
content: content_base64,
|
||||
encoding: 'base64',
|
||||
index: index
|
||||
};
|
||||
|
||||
if (debug) {
|
||||
logger(`Attachment ${index}: ${attachment_info.filename}, ` +
|
||||
`${attachment_info.size} bytes, type: ${attachment_info.content_type}`);
|
||||
}
|
||||
|
||||
return attachment_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert content to base64 string
|
||||
*
|
||||
* @param {Buffer|string} content - Content to convert
|
||||
* @returns {string|null} Base64 encoded string or null
|
||||
*/
|
||||
function to_base64(content) {
|
||||
if (!content) return null;
|
||||
|
||||
if (Buffer.isBuffer(content)) {
|
||||
return content.toString('base64');
|
||||
}
|
||||
|
||||
if (typeof content === 'string') {
|
||||
return Buffer.from(content).toString('base64');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total attachment size
|
||||
*
|
||||
* @param {Array} attachments - Array of attachment objects
|
||||
* @returns {number} Total size in bytes
|
||||
*/
|
||||
function get_total_size(attachments) {
|
||||
if (!attachments || !Array.isArray(attachments)) return 0;
|
||||
|
||||
return attachments.reduce((total, att) => {
|
||||
return total + (att.size || 0);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any attachment exceeds size limit
|
||||
*
|
||||
* @param {Array} attachments - Array of attachment objects
|
||||
* @param {number} max_size - Maximum size in bytes
|
||||
* @returns {boolean} True if any attachment exceeds limit
|
||||
*/
|
||||
function has_oversized(attachments, max_size) {
|
||||
if (!attachments || !Array.isArray(attachments)) return false;
|
||||
|
||||
return attachments.some(att => (att.size || 0) > max_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter attachments by content type
|
||||
*
|
||||
* @param {Array} attachments - Array of attachment objects
|
||||
* @param {string|string[]} content_types - Content type(s) to match
|
||||
* @returns {Array} Filtered attachments
|
||||
*/
|
||||
function filter_by_type(attachments, content_types) {
|
||||
if (!attachments || !Array.isArray(attachments)) return [];
|
||||
|
||||
const types = Array.isArray(content_types) ? content_types : [content_types];
|
||||
|
||||
return attachments.filter(att => {
|
||||
const att_type = (att.content_type || '').toLowerCase();
|
||||
return types.some(type => att_type.includes(type.toLowerCase()));
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
extract,
|
||||
format_attachment,
|
||||
to_base64,
|
||||
get_total_size,
|
||||
has_oversized,
|
||||
filter_by_type
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue