From 666f1a2840b82517c7b41430b8cfc36a5adc7deb Mon Sep 17 00:00:00 2001 From: Merci Jacob Date: Fri, 23 Feb 2024 12:46:46 +0200 Subject: [PATCH] [NEW] add a way to show and download source of an email --- modules/core/functions.php | 31 +++++++++++++++++-------------- modules/core/setup.php | 1 + modules/imap/handler_modules.php | 19 +++++++++++++++++++ modules/imap/output_modules.php | 25 +++++++++++++++++++++++++ modules/imap/setup.php | 10 +++++++++- modules/imap/site.js | 25 +++++++++++++++++++++++++ 6 files changed, 96 insertions(+), 15 deletions(-) diff --git a/modules/core/functions.php b/modules/core/functions.php index 8b529f383b..5b94a242a6 100644 --- a/modules/core/functions.php +++ b/modules/core/functions.php @@ -406,10 +406,11 @@ function setup_base_ajax_page($name, $source=false) { * @subpackage core/functions * @param string $name the page id * @param string $source the module set name + * @param bool $use_layout true if this page uses the application layout * @return void */ if (!hm_exists('setup_base_page')) { -function setup_base_page($name, $source=false) { +function setup_base_page($name, $source=false, $use_layout=true) { add_handler($name, 'stay_logged_in', false, $source); add_handler($name, 'login', false, $source); add_handler($name, 'default_page_data', true, $source); @@ -429,20 +430,22 @@ function setup_base_page($name, $source=false) { add_output($name, 'js_data', false, $source); add_output($name, 'js_search_data', true, $source); add_output($name, 'header_end', false, $source); - add_output($name, 'content_start', false, $source); - add_output($name, 'login_start', false, $source); - add_output($name, 'login', false, $source); - add_output($name, 'login_end', false, $source); - add_output($name, 'loading_icon', true, $source); - add_output($name, 'date', true, $source); - add_output($name, 'msgs', false, $source); - add_output($name, 'folder_list_start', true, $source); - add_output($name, 'folder_list_end', true, $source); - add_output($name, 'content_section_start', true, $source); - add_output($name, 'content_section_end', true, $source); - add_output($name, 'save_reminder', true, $source); + if($use_layout) { + add_output($name, 'content_start', false, $source); + add_output($name, 'login_start', false, $source); + add_output($name, 'login', false, $source); + add_output($name, 'login_end', false, $source); + add_output($name, 'loading_icon', true, $source); + add_output($name, 'date', true, $source); + add_output($name, 'msgs', false, $source); + add_output($name, 'folder_list_start', true, $source); + add_output($name, 'folder_list_end', true, $source); + add_output($name, 'content_section_start', true, $source); + add_output($name, 'content_section_end', true, $source); + add_output($name, 'save_reminder', true, $source); + add_output($name, 'content_end', false, $source, 'page_js', 'after'); + } add_output($name, 'page_js', false, $source); - add_output($name, 'content_end', false, $source); }} /** diff --git a/modules/core/setup.php b/modules/core/setup.php index 6f938fbb79..a988270d89 100644 --- a/modules/core/setup.php +++ b/modules/core/setup.php @@ -196,6 +196,7 @@ 'deleted_server_id' => array(FILTER_SANITIZE_FULL_SPECIAL_CHARS, false), 'msg_headers' => array(FILTER_UNSAFE_RAW, false), 'msg_text' => array(FILTER_UNSAFE_RAW, false), + 'msg_source' => array(FILTER_UNSAFE_RAW, false), 'msg_parts' => array(FILTER_UNSAFE_RAW, false), 'page_links' => array(FILTER_UNSAFE_RAW, false), 'folder_status' => array(FILTER_DEFAULT, FILTER_REQUIRE_ARRAY), diff --git a/modules/imap/handler_modules.php b/modules/imap/handler_modules.php index be7b72f8c3..8aa13b001f 100644 --- a/modules/imap/handler_modules.php +++ b/modules/imap/handler_modules.php @@ -1920,6 +1920,25 @@ public function process() { } } +/** + * Get message source from an IMAP server + */ +class Hm_Handler_imap_message_source extends Hm_Handler_Module { + public function process() { + $imap_server_id = $this->request->get['imap_server_id']; + $imap_msg_uid = $this->request->get['imap_msg_uid']; + $folder = $this->request->get['imap_folder']; + if ($imap_server_id && $imap_msg_uid && $folder) { + $cache = Hm_IMAP_List::get_cache($this->cache, $imap_server_id); + $imap = Hm_IMAP_List::connect($imap_server_id, $cache); + if ($imap->select_mailbox(hex2bin($folder))) { + $msg_source = $imap->get_message_content($imap_msg_uid, 0, false); + $this->out('msg_source', $msg_source); + } + } + } +} + /** * Hide or unhide an IMAP server * @subpackage imap/handler diff --git a/modules/imap/output_modules.php b/modules/imap/output_modules.php index ecc02b0243..96ab134582 100644 --- a/modules/imap/output_modules.php +++ b/modules/imap/output_modules.php @@ -371,6 +371,7 @@ protected function output() { $txt .= ' | '.$this->trans('Move').''; $txt .= ' | '.$this->trans('Archive').''; $txt .= ' | ' . snooze_dropdown($this, isset($headers['X-Snoozed'])); + $txt .= ' | ' . $this->trans('Show Source') . ''; if ($this->get('sieve_filters_enabled')) { $server_id = $this->get('msg_server_id'); @@ -1283,3 +1284,27 @@ protected function output() { $this->concat('msg_controls_extra', $res); } } + +/** + * Output imap message source + * @subpackage imap/output + */ +class Hm_Output_imap_message_source extends Hm_Output_Module { + protected function output() { + $res = '
'; + $res .= ' +
+

Message source

+
+ + Copy to clipboard +
+
+ '; + if($this->get('msg_source')){ + $res .= '
'.$this->html_safe($this->get('msg_source')).'
'; + } + $res .= '
'; + return $res; + } +} \ No newline at end of file diff --git a/modules/imap/setup.php b/modules/imap/setup.php index 9135790bd9..0c34fc3c05 100644 --- a/modules/imap/setup.php +++ b/modules/imap/setup.php @@ -82,6 +82,11 @@ add_handler('message', 'imap_remove_attachment', true, 'imap', 'message_list_type', 'after'); add_output('message', 'imap_server_ids', true, 'imap', 'page_js', 'before'); +/* message source page */ +setup_base_page('message_source', 'core', false); +add_output('message_source', 'imap_message_source', true); +add_handler('message_source', 'imap_message_source', true); + /* ajax mark as read */ setup_base_ajax_page('ajax_imap_mark_as_read', 'core'); add_handler('ajax_imap_mark_as_read', 'load_imap_servers_from_config', true, 'imap', 'load_user_data', 'after'); @@ -326,6 +331,7 @@ 'ajax_imap_snooze', 'ajax_imap_unsnooze', 'ajax_imap_junk', + 'message_source', ), 'allowed_output' => array( @@ -353,7 +359,9 @@ 'imap_download_message' => FILTER_VALIDATE_BOOLEAN, 'imap_remove_attachment' => FILTER_VALIDATE_BOOLEAN, 'imap_show_message' => FILTER_VALIDATE_BOOLEAN, - 'imap_msg_part' => FILTER_DEFAULT + 'imap_msg_part' => FILTER_DEFAULT, + 'imap_msg_uid' => FILTER_DEFAULT, + 'imap_folder' => FILTER_DEFAULT, ), 'allowed_post' => array( diff --git a/modules/imap/site.js b/modules/imap/site.js index 58b8f77160..fe16345299 100644 --- a/modules/imap/site.js +++ b/modules/imap/site.js @@ -736,6 +736,11 @@ var imap_message_view_finished = function(msg_uid, detail, skip_links) { return block_unblock_sender(msg_uid, detail, scope, action, sender, reject_message); }); + $('#show_message_source').on("click", function(e) { + e.preventDefault(); + const detail = Hm_Utils.parse_folder_path(hm_list_path(), 'imap'); + window.open(`?page=message_source&imap_msg_uid=${hm_msg_uid()}&imap_server_id=${detail.server_id}&imap_folder=${detail.folder}`); + }); $(document).on('click', '#unblock_sender', function(e) { e.preventDefault(); var sender = ''; @@ -1375,3 +1380,23 @@ if (message) { childList: true }); } + +const handleDownloadMsgSource = function() { + const messageSource = document.querySelector('pre.msg_source'); + const blob = new Blob([messageSource.textContent], { type: "message/rfc822" }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + const subject = messageSource.textContent.match(/Subject: (.*)/)?.[1] || hm_msg_uid(); // Let's use the message UID if the subject is empty + a.download = subject + '.eml'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); +}; + +const handleCopyMsgSource = function(e) { + e.preventDefault(); + const messageSource = document.querySelector('pre.msg_source'); + navigator.clipboard.writeText(messageSource.textContent); + Hm_Notices.show(['Copied to clipboard']); +}