Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions modules/core/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,17 @@ function process_site_setting($type, $handler, $callback=false, $default=false,
}
}
else {
list($success, $form) = $handler->process_form(array('save_settings', $type));
list($success, $form) = $handler->process_form(array('save_settings'));
if ($success) {
if (array_key_exists($type, $handler->request->post)) {
$form[$type] = $handler->request->post[$type];
}
else {
$form[$type] = '';
}
}
}

$new_settings = $handler->get('new_user_settings', array());
$settings = $handler->get('user_settings', array());

Expand All @@ -291,7 +300,8 @@ function process_site_setting($type, $handler, $callback=false, $default=false,
$new_settings[$type.'_setting'] = $result;
}
else {
$settings[$type] = $handler->user_config->get($type.'_setting', $default);
$value_from_config = $handler->user_config->get($type.'_setting', $default);
$settings[$type] = $value_from_config;
}
$handler->out('new_user_settings', $new_settings, false);
$handler->out('user_settings', $settings, false);
Expand Down Expand Up @@ -785,3 +795,23 @@ function isPageConfigured($page) {
return in_array($page, $pages);
}

/**
* Get setting value with fallback to _setting suffix
* @subpackage core/functions
* @param array $settings User settings array
* @param string $key Setting key without _setting suffix
* @param mixed $default Default value
* @return mixed Setting value
*/
if (!hm_exists('get_setting_value')) {
function get_setting_value($settings, $key, $default = '') {
if (array_key_exists($key, $settings)) {
return $settings[$key];
}
if (array_key_exists($key . '_setting', $settings)) {
return $settings[$key . '_setting'];
}
return $default;
}
}

69 changes: 69 additions & 0 deletions modules/core/handler_modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,75 @@ public function process() {
}
}

/**
* Process SpamCop reporting settings from the Report Spam section
* @subpackage core/handler
*/
class Hm_Handler_process_spam_report_settings extends Hm_Handler_Module {
public function process() {
list($success, $form) = $this->process_form(array('save_settings'));
if (!$success) {
return;
}

$new_settings = $this->get('new_user_settings', array());

$set_setting = function($key, $value, $validator = null) use (&$new_settings) {
if ($validator && !$validator($value)) {
$new_settings[$key] = '';
} else {
$new_settings[$key] = $value;
}
};

// Process SpamCop settings
if (array_key_exists('spamcop_settings', $this->request->post)) {
$spamcop = $this->request->post['spamcop_settings'];
$new_settings['spamcop_enabled_setting'] = isset($spamcop['enabled']);
$set_setting('spamcop_submission_email_setting', $spamcop['submission_email'] ?? '', function($v) {
return filter_var($v, FILTER_VALIDATE_EMAIL);
});
$set_setting('spamcop_from_email_setting', $spamcop['from_email'] ?? '', function($v) {
return filter_var($v, FILTER_VALIDATE_EMAIL);
});
}

// Process APWG settings
if (array_key_exists('apwg_settings', $this->request->post)) {
$apwg = $this->request->post['apwg_settings'];
$new_settings['apwg_enabled_setting'] = isset($apwg['enabled']);
$set_setting('apwg_from_email_setting', $apwg['from_email'] ?? '', function($v) {
return filter_var($v, FILTER_VALIDATE_EMAIL);
});
}

// Process AbuseIPDB settings
if (array_key_exists('abuseipdb_settings', $this->request->post)) {
$abuseipdb = $this->request->post['abuseipdb_settings'];
$new_settings['abuseipdb_enabled_setting'] = isset($abuseipdb['enabled']);

// Handle API key
$api_key = $abuseipdb['api_key'] ?? '';
$api_key_was_set = isset($abuseipdb['api_key_set']) && $abuseipdb['api_key_set'] == '1';

if (empty($api_key) && $api_key_was_set) {
$original_key = $this->user_config->get('abuseipdb_api_key_setting', '');
if (!empty($original_key)) {
$new_settings['abuseipdb_api_key_setting'] = $original_key;
} else {
$new_settings['abuseipdb_api_key_setting'] = '';
}
} else {
$set_setting('abuseipdb_api_key_setting', $api_key, function($v) {
return !empty($v) && strlen($v) >= 10 && strlen($v) <= 200;
});
}
}

$this->out('new_user_settings', $new_settings, false);
}
}

/**
* Process warn for unsaved changes in the settings page
* @subpackage core/handler
Expand Down
2 changes: 1 addition & 1 deletion modules/core/message_list_functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ function icon_callback($vals, $style, $output_mod) {
if (!hm_exists('message_controls')) {
function message_controls($output_mod) {
$txt = '';
$controls = ['read', 'unread', 'flag', 'unflag', 'delete', 'archive', 'junk'];
$controls = ['read', 'unread', 'flag', 'unflag', 'delete', 'archive', 'junk']; // 'report_spam'
$controls = array_filter($controls, function($val) use ($output_mod) {
if (in_array($val, [$output_mod->get('list_path', ''), strtolower($output_mod->get('core_msg_control_folder', ''))])) {
return false;
Expand Down
185 changes: 184 additions & 1 deletion modules/core/output_modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -1775,7 +1775,45 @@ protected function output() {
$share_folder_modal .= '</div>';
$share_folder_modal .= '</div>';

return $share_folder_modal;
// Report Spam Modal
$report_spam_modal = '<div class="modal fade" id="reportSpamModal" tabindex="-1" aria-labelledby="reportSpamModalLabel" aria-hidden="true">';
$report_spam_modal .= '<div class="modal-dialog">';
$report_spam_modal .= '<div class="modal-content">';
$report_spam_modal .= '<div class="modal-header">';
$report_spam_modal .= '<h5 class="modal-title" id="reportSpamModalLabel">'.$this->trans('Report Spam').'</h5>';
$report_spam_modal .= '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>';
$report_spam_modal .= '</div>';
$report_spam_modal .= '<div class="modal-body">';
$report_spam_modal .= '<p>'.$this->trans('Please tell us why you\'re reporting this email:').'</p>';
$report_spam_modal .= '<form id="reportSpamForm">';
$report_spam_modal .= '<div class="mb-3">';
$report_spam_modal .= '<label for="spam_reason_select" class="form-label">'.$this->trans('Select one or more reasons:').'</label>';
$report_spam_modal .= '<select class="form-select" id="spam_reason_select" name="spam_reason[]" multiple size="7">';
$report_spam_modal .= '<option value="unsolicited">'.$this->trans('Unsolicited / Spam').'</option>';
$report_spam_modal .= '<option value="phishing">'.$this->trans('Phishing or scam attempt').'</option>';
$report_spam_modal .= '<option value="malicious">'.$this->trans('Malicious or harmful content').'</option>';
$report_spam_modal .= '<option value="advertising">'.$this->trans('Advertising / Promotional').'</option>';
$report_spam_modal .= '<option value="offensive">'.$this->trans('Offensive or inappropriate').'</option>';
$report_spam_modal .= '<option value="wrong_recipient">'.$this->trans('Sent to the wrong recipient').'</option>';
$report_spam_modal .= '<option value="other">'.$this->trans('Other – please specify').'</option>';
$report_spam_modal .= '</select>';
$report_spam_modal .= '<small class="form-text text-muted">'.$this->trans('Hold Ctrl (or Cmd on Mac) to select multiple options.').'</small>';
$report_spam_modal .= '</div>';
$report_spam_modal .= '<div class="mb-3" id="spam_reason_other_input" style="display: none;">';
$report_spam_modal .= '<label for="spam_reason_other_text" class="form-label">'.$this->trans('Please specify:').'</label>';
$report_spam_modal .= '<input type="text" class="form-control" id="spam_reason_other_text" placeholder="'.$this->trans('Please specify').'">';
$report_spam_modal .= '</div>';
$report_spam_modal .= '</form>';
$report_spam_modal .= '</div>';
$report_spam_modal .= '<div class="modal-footer">';
$report_spam_modal .= '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">'.$this->trans('Cancel').'</button>';
$report_spam_modal .= '<button type="button" class="btn btn-warning" id="confirm_report_spam">'.$this->trans('Report as Spam').'</button>';
$report_spam_modal .= '</div>';
$report_spam_modal .= '</div>';
$report_spam_modal .= '</div>';
$report_spam_modal .= '</div>';

return $share_folder_modal . $report_spam_modal;
}
}

Expand Down Expand Up @@ -2277,6 +2315,151 @@ protected function output() {
}
}

/**
* Starts the Report Spam section on the settings page
* @subpackage core/output
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature is already big enough that it deserves its own module. Please move everything from core and imap modules to a new module 'report_spam' or similar.

class Hm_Output_start_report_spam_settings extends Hm_Output_Module {
/**
* Settings in this section control spam reporting features
*/
protected function output() {
return '<tr><td data-target=".report_spam_setting" colspan="2" class="settings_subtitle cursor-pointer border-bottom p-2">'.
'<i class="bi bi-shield-exclamation fs-5 me-2"></i>'.
$this->trans('Report Spam').'</td></tr>';
}
}

/**
* Option to enable/disable SpamCop reporting
* @subpackage core/output
*/
class Hm_Output_spamcop_enabled_setting extends Hm_Output_Module {
protected function output() {
$settings = $this->get('user_settings', array());
$enabled = get_setting_value($settings, 'spamcop_enabled', false);
$checked = $enabled ? ' checked="checked"' : '';
$reset = $enabled ? '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>' : '';

return '<tr class="report_spam_setting"><td><label class="form-check-label" for="spamcop_enabled">'.
'<strong>'.$this->trans('Enable SpamCop reporting').'</strong></label></td>'.
'<td><input class="form-check-input" type="checkbox" '.$checked.' id="spamcop_enabled" name="spamcop_settings[enabled]" data-default-value="false" value="1" />'.$reset.'</td></tr>';
}
}

/**
* Option for SpamCop submission email address
* @subpackage core/output
*/
class Hm_Output_spamcop_submission_email_setting extends Hm_Output_Module {
protected function output() {
$settings = $this->get('user_settings', array());
$email = get_setting_value($settings, 'spamcop_submission_email', '');
$reset = !empty($email) ? '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_input"></i></span>' : '';

return '<tr class="report_spam_setting"><td><label for="spamcop_submission_email">'.
$this->trans('SpamCop submission email').'</label></td>'.
'<td class="d-flex"><input class="form-control form-control-sm" type="email" id="spamcop_submission_email" name="spamcop_settings[submission_email]" value="'.$this->html_safe($email).'" placeholder="[email protected]" />'.$reset.'</td></tr>';
}
}

/**
* Option for SpamCop from email address
* @subpackage core/output
*/
class Hm_Output_spamcop_from_email_setting extends Hm_Output_Module {
protected function output() {
$settings = $this->get('user_settings', array());
$email = get_setting_value($settings, 'spamcop_from_email', '');
$reset = !empty($email) ? '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_input"></i></span>' : '';

return '<tr class="report_spam_setting"><td><label for="spamcop_from_email">'.
$this->trans('From email address (optional)').'</label></td>'.
'<td class="d-flex"><input class="form-control form-control-sm" type="email" id="spamcop_from_email" name="spamcop_settings[from_email]" value="'.$this->html_safe($email).'" placeholder="'.$this->trans('Uses your IMAP email if not set').'" />'.$reset.'</td></tr>';
}
}

/**
* Option to enable/disable AbuseIPDB reporting
* @subpackage core/output
*/
class Hm_Output_abuseipdb_enabled_setting extends Hm_Output_Module {
protected function output() {
$settings = $this->get('user_settings', array());
$enabled = get_setting_value($settings, 'abuseipdb_enabled', false);
$checked = $enabled ? ' checked="checked"' : '';
$reset = $enabled ? '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>' : '';

return '<tr class="report_spam_setting"><td><label class="form-check-label" for="abuseipdb_enabled">'.
'<strong>'.$this->trans('Enable AbuseIPDB reporting').'</strong></label></td>'.
'<td><input class="form-check-input" type="checkbox" '.$checked.' id="abuseipdb_enabled" name="abuseipdb_settings[enabled]" data-default-value="false" value="1" />'.$reset.'</td></tr>';
}
}

/**
* Option to enable/disable APWG phishing reporting
* @subpackage core/output
*/
class Hm_Output_apwg_enabled_setting extends Hm_Output_Module {
protected function output() {
$settings = $this->get('user_settings', array());
$enabled = get_setting_value($settings, 'apwg_enabled', false);
$checked = $enabled ? ' checked="checked"' : '';
$reset = $enabled ? '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_checkbox"></i></span>' : '';

return '<tr class="report_spam_setting"><td><label class="form-check-label" for="apwg_enabled">'.
'<strong>'.$this->trans('Enable APWG phishing reporting').'</strong></label></td>'.
'<td><input class="form-check-input" type="checkbox" '.$checked.' id="apwg_enabled" name="apwg_settings[enabled]" data-default-value="false" value="1" />'.$reset.'</td></tr>';
}
}

/**
* Option for APWG from email address
* @subpackage core/output
*/
class Hm_Output_apwg_from_email_setting extends Hm_Output_Module {
protected function output() {
$settings = $this->get('user_settings', array());
$email = get_setting_value($settings, 'apwg_from_email', '');
$reset = !empty($email) ? '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_input"></i></span>' : '';

return '<tr class="report_spam_setting"><td><label for="apwg_from_email">'.
$this->trans('From email address (optional)').'</label></td>'.
'<td class="d-flex"><input class="form-control form-control-sm" type="email" id="apwg_from_email" name="apwg_settings[from_email]" value="'.$this->html_safe($email).'" placeholder="'.$this->trans('Uses your IMAP email if not set').'" />'.$reset.'</td></tr>';
}
}

/**
* Option for AbuseIPDB API key
* @subpackage core/output
*/
class Hm_Output_abuseipdb_api_key_setting extends Hm_Output_Module {
protected function output() {
$settings = $this->get('user_settings', array());
$api_key = get_setting_value($settings, 'abuseipdb_api_key', '');

// Mask API key if it exists - show empty field with indicator
// The handler will preserve the original key if empty value is submitted
$display_value = '';
$placeholder = $this->trans('Your AbuseIPDB API key');

if (!empty($api_key)) {
$placeholder = $this->trans('API key is set (••••••••) - enter new value to change');
}

$reset = !empty($api_key) ? '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-counterclockwise refresh_list reset_default_value_input"></i></span>' : '';

// Add a hidden field to track if API key was originally set
// This helps the handler know to preserve the key if field is left empty
$hidden_field = !empty($api_key) ? '<input type="hidden" name="abuseipdb_settings[api_key_set]" value="1" />' : '';

return '<tr class="report_spam_setting"><td><label for="abuseipdb_api_key">'.
$this->trans('AbuseIPDB API Key').'</label></td>'.
'<td class="d-flex">'.$hidden_field.
'<input class="form-control form-control-sm" type="password" id="abuseipdb_api_key" name="abuseipdb_settings[api_key]" value="'.$this->html_safe($display_value).'" placeholder="'.$placeholder.'" autocomplete="off" />'.$reset.'</td></tr>';
}
}

/**
* Option to warn user when he has unsaved changes.
* @subpackage imap/output
Expand Down
14 changes: 13 additions & 1 deletion modules/core/setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
add_handler('settings', 'process_trash_source_max_setting', true, 'core', 'date', 'after');
add_handler('settings', 'process_drafts_since_setting', true, 'core', 'date', 'after');
add_handler('settings', 'process_drafts_source_max_setting', true, 'core', 'date', 'after');
add_handler('settings', 'process_spam_report_settings', true, 'core', 'save_user_settings', 'before');
add_handler('settings', 'process_hide_folder_icons', true, 'core', 'date', 'after');
add_handler('settings', 'process_delete_prompt_setting', true, 'core', 'date', 'after');
add_handler('settings', 'process_delete_attachment_setting', true, 'core', 'date', 'after');
Expand Down Expand Up @@ -101,7 +102,15 @@
add_output('settings', 'start_drafts_settings', true, 'core', 'trash_source_max_setting', 'after');
add_output('settings', 'drafts_since_setting', true, 'core', 'start_drafts_settings', 'after');
add_output('settings', 'drafts_source_max_setting', true, 'core', 'drafts_since_setting', 'after');
add_output('settings', 'start_everything_settings', true, 'core', 'drafts_source_max_setting', 'after');
add_output('settings', 'start_report_spam_settings', true, 'core', 'drafts_source_max_setting', 'after');
add_output('settings', 'spamcop_enabled_setting', true, 'core', 'start_report_spam_settings', 'after');
add_output('settings', 'spamcop_submission_email_setting', true, 'core', 'spamcop_enabled_setting', 'after');
add_output('settings', 'spamcop_from_email_setting', true, 'core', 'spamcop_submission_email_setting', 'after');
add_output('settings', 'apwg_enabled_setting', true, 'core', 'spamcop_from_email_setting', 'after');
add_output('settings', 'apwg_from_email_setting', true, 'core', 'apwg_enabled_setting', 'after');
add_output('settings', 'abuseipdb_enabled_setting', true, 'core', 'apwg_from_email_setting', 'after');
add_output('settings', 'abuseipdb_api_key_setting', true, 'core', 'abuseipdb_enabled_setting', 'after');
add_output('settings', 'start_everything_settings', true, 'core', 'abuseipdb_api_key_setting', 'after');
add_output('settings', 'all_since_setting', true, 'core', 'start_everything_settings', 'after');
add_output('settings', 'all_source_max_setting', true, 'core', 'all_since_setting', 'after');
add_output('settings', 'start_all_email_settings', true, 'core', 'all_source_max_setting', 'after');
Expand Down Expand Up @@ -360,5 +369,8 @@
'srv_setup_stepper_imap_hide_from_c_page' => FILTER_VALIDATE_BOOLEAN,
'images_whitelist' => FILTER_UNSAFE_RAW,
'update' => FILTER_VALIDATE_BOOLEAN,
'spamcop_settings' => array('filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FORCE_ARRAY),
'apwg_settings' => array('filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FORCE_ARRAY),
'abuseipdb_settings' => array('filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FORCE_ARRAY),
)
);
Loading