Skip to content

Commit 9007129

Browse files
committed
feat(platform_blocking): implement platform blocking feature in Sieve filters and enhance UI interactions
1 parent 3a24840 commit 9007129

File tree

8 files changed

+339
-16
lines changed

8 files changed

+339
-16
lines changed

config/app.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,13 @@
13571357
*/
13581358
'default_setting_enable_sieve_filter' => env('DEFAULT_SETTING_ENABLE_SIEVE_FILTER', false),
13591359

1360+
/*
1361+
|
1362+
| Enable platform/vendor blocking in sieve block sender
1363+
| Defaults to false
1364+
*/
1365+
'enable_platform_blocking' => env('ENABLE_PLATFORM_BLOCKING', true),
1366+
13601367
/*
13611368
| Fancy Login page
13621369
| Use this setting switch between the legacy login page and the fancy one

modules/imap/output_modules.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,36 @@ protected function output() {
430430
$blocked_senders = get_blocked_senders_array($imap_server, $this->get('site_config'), $this->get('user_config'));
431431
$sender_blocked = in_array($sender, $blocked_senders);
432432
$domain_blocked = in_array($domain, $blocked_senders);
433+
$vendor_detection = $this->get('vendor_detection', array());
434+
$vendor_id = $vendor_detection['vendor_id'] ?? '';
435+
$vendor_name = $vendor_detection['vendor_name'] ?? '';
436+
$platform_blocking_enabled = false;
437+
$site_config = $this->get('site_config');
438+
if ($site_config && method_exists($site_config, 'get')) {
439+
$platform_blocking_enabled = (bool) $site_config->get('enable_platform_blocking', false);
440+
}
441+
$platform_key = $vendor_id ? 'platform:'.$vendor_id : '';
442+
$platform_blocked = $platform_blocking_enabled && $platform_key && in_array($platform_key, $blocked_senders);
443+
$block_target = '';
444+
$block_label = 'Block Sender';
445+
if ($platform_blocked) {
446+
$block_target = 'platform';
447+
$block_label = 'Unblock Platform';
448+
} elseif ($domain_blocked) {
449+
$block_target = 'domain';
450+
$block_label = 'Unblock Domain';
451+
} elseif ($sender_blocked) {
452+
$block_target = 'sender';
453+
$block_label = 'Unblock Sender';
454+
}
433455
if(!in_array($sender, $existing_emails)){
434-
$txt .= '<div class="dropdown d-inline-block"><a class="block_sender_link hlink dropdown-toggle text-decoration-none btn btn-sm btn-outline-danger '.($domain_blocked || $sender_blocked ? '" id="unblock_sender" data-target="'.($domain_blocked? 'domain':'sender').'"' : '"').' href="#" aria-labelledby="dropdownMenuBlockSender" data-bs-toggle="dropdown"><i class="bi bi-lock-fill"></i> <span id="filter_block_txt">'.$this->trans($domain_blocked ? 'Unblock Domain' : ($sender_blocked ? 'Unblock Sender' : 'Block Sender')).'</span></a>';
435-
$txt .= block_filter_dropdown($this);
456+
$txt .= '<div class="dropdown d-inline-block"><a class="block_sender_link hlink dropdown-toggle text-decoration-none btn btn-sm btn-outline-danger '.($block_target ? '" id="unblock_sender" data-target="'.$block_target.'"' : '"').' href="#" aria-labelledby="dropdownMenuBlockSender" data-bs-toggle="dropdown"><i class="bi bi-lock-fill"></i> <span id="filter_block_txt">'.$this->trans($block_label).'</span></a>';
457+
$txt .= block_filter_dropdown($this, null, true, 'block_sender', 'Block', "", array(
458+
'sender' => $sender,
459+
'domain' => $domain,
460+
'vendor_id' => $vendor_id,
461+
'vendor_name' => $vendor_name
462+
));
436463
}
437464
} else {
438465
$txt .= '<span class="text-decoration-none btn btn-sm btn-outline-danger" data-bs-toogle="tooltip" title="This functionality requires the email server support &quot;Sieve&quot; technology which is not provided. Contact your email provider to fix it or enable it if supported."><i class="bi bi-lock-fill"></i> <span id="filter_block_txt">'.$this->trans('Block Sender').'</span></span>';

modules/imap/site.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -746,18 +746,18 @@ var block_unblock_sender = function(msg_uid, detail, scope, action, sender = '',
746746
{'name': 'sender', 'value': sender},
747747
],
748748
function(res) {
749-
if (/^(Sender|Domain) Blocked$/.test(res.router_user_msgs[0].text)) {
749+
if (/^(Sender|Domain|Platform) Blocked$/.test(res.router_user_msgs[0].text)) {
750750
var title = scope == 'domain'
751751
? 'UNBLOCK DOMAIN'
752-
: 'UNBLOCK SENDER';
752+
: (scope == 'platform' ? 'UNBLOCK PLATFORM' : 'UNBLOCK SENDER');
753753
$("#filter_block_txt").html(title);
754754
$("#filter_block_txt")
755755
.parent()
756756
.removeClass('dropdown-toggle')
757757
.attr('id', 'unblock_sender')
758758
.data('target', scope);
759759
}
760-
if (/^(Sender|Domain) Unblocked$/.test(res.router_user_msgs[0].text)) {
760+
if (/^(Sender|Domain|Platform) Unblocked$/.test(res.router_user_msgs[0].text)) {
761761
$("#filter_block_txt").html('BLOCK SENDER');
762762
$("#filter_block_txt")
763763
.parent()
@@ -802,7 +802,9 @@ var imap_message_view_finished = function(msg_uid, detail, listParent, skip_link
802802
e.preventDefault();
803803
var scope = $('[name=scope]').val();
804804
var action = $('[name=block_action]').val();
805-
var sender = $('[name=scope]').data('sender');
805+
var sender = scope == 'platform'
806+
? $('[name=scope]').data('vendor-id')
807+
: $('[name=scope]').data('sender');
806808
var reject_message = action == 'reject_with_message' ? $('#reject_message_textarea').val() : '';
807809

808810
if (action == 'reject_with_message' && ! reject_message) {
@@ -826,6 +828,8 @@ var imap_message_view_finished = function(msg_uid, detail, listParent, skip_link
826828
var sender = '';
827829
if ($(this).data('target') == 'domain') {
828830
sender = $('[name=scope]').data('domain');
831+
} else if ($(this).data('target') == 'platform') {
832+
sender = $('[name=scope]').data('vendor-id');
829833
} else {
830834
sender = $('[name=scope]').data('sender');
831835
}

modules/sievefilters/functions.php

Lines changed: 169 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
if (!defined('DEBUG_MODE')) { die(); }
44

5+
if (!function_exists('vendor_detection_load_registry')) {
6+
require_once APP_PATH.'modules/vendor_detection/functions.php';
7+
}
8+
59
if (!hm_exists('get_script_modal_content')) {
610
function get_script_modal_content()
711
{
@@ -350,6 +354,11 @@ function default_reject_message($user_config, $imap_server_id)
350354
if (!hm_exists('block_filter')) {
351355
function block_filter($filter, $user_config, $action, $imap_server_id, $sender, $custom_reject_message = '')
352356
{
357+
if (strpos($sender, 'platform:') === 0) {
358+
$vendor_id = substr($sender, strlen('platform:'));
359+
return block_filter_platform($filter, $user_config, $action, $imap_server_id, $vendor_id, $custom_reject_message);
360+
}
361+
353362
$ret = ['action' => $action];
354363

355364
if (explode('@', $sender)[0] == '*') {
@@ -394,14 +403,17 @@ function block_filter($filter, $user_config, $action, $imap_server_id, $sender,
394403
}
395404
elseif ($default_behaviour == 'Reject') {
396405
$filter->addRequirement('reject');
406+
if (!isset($reject_message) || $reject_message === '') {
407+
$reject_message = 'Blocked by Cypht';
408+
}
397409
$custom_condition->addAction(
398-
new \PhpSieveManager\Filters\Actions\RejectFilterAction([$reject_message])
410+
new \PhpSieveManager\Filters\Actions\RejectFilterAction(['reason' => $reject_message])
399411
);
400412
}
401413
elseif ($default_behaviour == 'Move') {
402414
$filter->addRequirement('fileinto');
403415
$custom_condition->addAction(
404-
new \PhpSieveManager\Filters\Actions\FileIntoFilterAction(['Blocked'])
416+
new \PhpSieveManager\Filters\Actions\FileIntoFilterAction(['mailbox' => 'Blocked'])
405417
);
406418
}
407419

@@ -415,16 +427,168 @@ function block_filter($filter, $user_config, $action, $imap_server_id, $sender,
415427
}
416428
}
417429

430+
if (!hm_exists('block_filter_platform')) {
431+
function block_filter_platform($filter, $user_config, $action, $imap_server_id, $vendor_id, $custom_reject_message = '') {
432+
$ret = ['action' => $action];
433+
if (!$vendor_id) {
434+
return $ret;
435+
}
436+
$vendor = vendor_detection_get_vendor_by_id($vendor_id);
437+
if (empty($vendor)) {
438+
if (defined('DEBUG_MODE') && DEBUG_MODE) {
439+
error_log('[sieve_block_debug] platform vendor not found: '.$vendor_id);
440+
}
441+
return $ret;
442+
}
443+
444+
$criteria = build_platform_block_criteria($vendor);
445+
if (empty($criteria)) {
446+
if (defined('DEBUG_MODE') && DEBUG_MODE) {
447+
error_log('[sieve_block_debug] platform criteria empty for: '.$vendor_id);
448+
}
449+
return $ret;
450+
}
451+
if (defined('DEBUG_MODE') && DEBUG_MODE) {
452+
error_log('[sieve_block_debug] platform criteria for '.$vendor_id.': '.json_encode($criteria));
453+
}
454+
455+
$custom_condition = new \PhpSieveManager\Filters\Condition(
456+
"", 'anyof'
457+
);
458+
foreach ($criteria as $criterion) {
459+
$cond = \PhpSieveManager\Filters\FilterCriteria::if('header');
460+
$cond->contains($criterion);
461+
$custom_condition->addCriteria($cond);
462+
}
463+
464+
if ($action == 'default') {
465+
$default_behaviour = 'Discard';
466+
if ($user_config->get('sieve_block_default_behaviour')) {
467+
if (array_key_exists($imap_server_id, $user_config->get('sieve_block_default_behaviour'))) {
468+
$default_behaviour = $user_config->get('sieve_block_default_behaviour')[$imap_server_id];
469+
if ($default_behaviour == 'Reject') {
470+
$reject_message = default_reject_message($user_config, $imap_server_id);
471+
}
472+
}
473+
}
474+
} elseif ($action == 'discard') {
475+
$default_behaviour = 'Discard';
476+
} elseif ($action == 'reject_default') {
477+
$default_behaviour = 'Reject';
478+
$reject_message = default_reject_message($user_config, $imap_server_id);
479+
$ret['reject_message'] = $reject_message;
480+
} elseif ($action == 'reject_with_message') {
481+
$default_behaviour = 'Reject';
482+
$reject_message = $custom_reject_message;
483+
$ret['reject_message'] = $custom_reject_message;
484+
} elseif ($action == 'blocked') {
485+
$default_behaviour = 'Move';
486+
}
487+
488+
if ($default_behaviour == 'Discard') {
489+
$custom_condition->addAction(
490+
new \PhpSieveManager\Filters\Actions\DiscardFilterAction()
491+
);
492+
}
493+
elseif ($default_behaviour == 'Reject') {
494+
$filter->addRequirement('reject');
495+
if (!isset($reject_message) || $reject_message === '') {
496+
$reject_message = 'Blocked by Cypht';
497+
}
498+
$custom_condition->addAction(
499+
new \PhpSieveManager\Filters\Actions\RejectFilterAction(['reason' => $reject_message])
500+
);
501+
}
502+
elseif ($default_behaviour == 'Move') {
503+
$filter->addRequirement('fileinto');
504+
$custom_condition->addAction(
505+
new \PhpSieveManager\Filters\Actions\FileIntoFilterAction(['mailbox' => 'Blocked'])
506+
);
507+
}
508+
509+
$custom_condition->addAction(
510+
new \PhpSieveManager\Filters\Actions\StopFilterAction()
511+
);
512+
513+
$filter->setCondition($custom_condition);
514+
515+
return $ret;
516+
}
517+
}
518+
519+
if (!hm_exists('build_platform_block_criteria')) {
520+
function build_platform_block_criteria($vendor) {
521+
$criteria = array();
522+
$header_values = array();
523+
$dkim_domains = vendor_detection_lowercase_list($vendor['dkim_domains'] ?? array());
524+
$return_domains = vendor_detection_lowercase_list($vendor['return_path_domains'] ?? array());
525+
$received_domains = vendor_detection_lowercase_list($vendor['received_domains'] ?? array());
526+
$platform_domains = vendor_detection_lowercase_list($vendor['platform_domains'] ?? array());
527+
$header_names = vendor_detection_lowercase_list($vendor['header_names'] ?? array());
528+
529+
$return_domains = array_unique(array_merge($return_domains, $platform_domains));
530+
$received_domains = array_unique(array_merge($received_domains, $platform_domains));
531+
532+
foreach ($dkim_domains as $domain) {
533+
$header_values[] = array('DKIM-Signature', $domain);
534+
}
535+
foreach ($return_domains as $domain) {
536+
$header_values[] = array('Return-Path', $domain);
537+
}
538+
foreach ($received_domains as $domain) {
539+
$header_values[] = array('Received', $domain);
540+
}
541+
foreach ($header_names as $header_name) {
542+
$header_values[] = array($header_name, '');
543+
}
544+
545+
foreach ($header_values as $pair) {
546+
$header = $pair[0];
547+
$value = $pair[1];
548+
if (!$header) {
549+
continue;
550+
}
551+
$key = $header.'|'.$value;
552+
if (isset($criteria[$key])) {
553+
continue;
554+
}
555+
$criteria[$key] = '"'.$header.'" ["'.$value.'"]';
556+
}
557+
558+
return array_values($criteria);
559+
}
560+
}
561+
418562
if (!hm_exists('block_filter_dropdown')) {
419-
function block_filter_dropdown ($mod, $mailbox_id = null, $with_scope = true, $submit_id = 'block_sender', $submit_title = 'Block', $increment = "") {
563+
function block_filter_dropdown ($mod, $mailbox_id = null, $with_scope = true, $submit_id = 'block_sender', $submit_title = 'Block', $increment = "", $block_data = array()) {
420564
$ret = '<div class="dropdown-menu p-3" id="dropdownMenuBlockSender' .$increment. '">'
421565
.'<form id="block_sender_form' .$increment. '" >';
422566
if ($with_scope) {
567+
$data_sender = $block_data['sender'] ?? '';
568+
$data_domain = $block_data['domain'] ?? '';
569+
$data_vendor_id = $block_data['vendor_id'] ?? '';
570+
$data_vendor_name = $block_data['vendor_name'] ?? '';
571+
$platform_enabled = false;
572+
$site_config = $mod->get('site_config');
573+
if ($site_config && method_exists($site_config, 'get')) {
574+
$platform_enabled = (bool) $site_config->get('enable_platform_blocking', false);
575+
}
576+
577+
$select_attrs = ' data-sender="'.$mod->html_safe($data_sender).'"'.
578+
' data-domain="'.$mod->html_safe($data_domain).'"'.
579+
' data-vendor-id="'.$mod->html_safe($data_vendor_id).'"'.
580+
' data-vendor-name="'.$mod->html_safe($data_vendor_name).'"';
581+
423582
$ret .= '<div class="mb-2">'
424583
. '<label for="blockSenderScope" class="form-label">'.$mod->trans('Who Is Blocked').'</label>'
425-
. '<select name="scope" class="form-select form-select-sm" id="blockSenderScope">'
584+
. '<select name="scope" class="form-select form-select-sm" id="blockSenderScope"'.$select_attrs.'>'
426585
. '<option value="sender">'.$mod->trans('This Sender').'</option>'
427-
. '<option value="domain">'.$mod->trans('Whole domain').'</option></select>'
586+
. '<option value="domain">'.$mod->trans('Whole domain').'</option>';
587+
if ($platform_enabled && $data_vendor_id) {
588+
$label = $data_vendor_name ? $data_vendor_name : $data_vendor_id;
589+
$ret .= '<option value="platform">'.$mod->trans('Platform').': '.$mod->html_safe($label).'</option>';
590+
}
591+
$ret .= '</select>'
428592
.'</div>';
429593
}
430594
$ret .= '<div class="mb-2">'

modules/sievefilters/hm-sieve.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,25 @@
66
class Hm_Sieve_Client_Factory {
77
public function init($user_config = null, $imap_account = null, $is_nux_supported = false)
88
{
9+
if (defined('DEBUG_MODE') && DEBUG_MODE) {
10+
$imap_debug = array();
11+
if (is_array($imap_account)) {
12+
$imap_debug = array_intersect_key($imap_account, array_flip(array(
13+
'name',
14+
'server',
15+
'host',
16+
'port',
17+
'tls',
18+
'sieve_config_host',
19+
'sieve_config_port',
20+
'sieve_config_tls'
21+
)));
22+
}
23+
error_log('[sieve_block_debug] sieve init input: '.json_encode(array(
24+
'has_imap_account' => (bool) $imap_account,
25+
'imap_account' => $imap_debug
26+
)));
27+
}
928
if ($imap_account && ! empty($imap_account['sieve_config_host'])) {
1029
// Check if module nux is enabled and if it is, get the sieve host from the services
1130
if($is_nux_supported && $sieve_config = get_sieve_host_from_services($imap_account['server'])) {
@@ -19,6 +38,9 @@ public function init($user_config = null, $imap_account = null, $is_nux_supporte
1938
$client->connect($imap_account['user'], $imap_account['pass'], $sieve_tls, "", "PLAIN");
2039
return $client;
2140
} else {
41+
if (defined('DEBUG_MODE') && DEBUG_MODE) {
42+
error_log('[sieve_block_debug] sieve init missing sieve_config_host');
43+
}
2244
$errorMsg = 'Invalid config host';
2345
if (isset($imap_account['name'])) {
2446
$errorMsg .= ' for ' . $imap_account['name'];

0 commit comments

Comments
 (0)