Skip to content

Commit 6c368a4

Browse files
committed
docs(README): update architecture section with detailed component overview and usage examples
1 parent a024f8a commit 6c368a4

File tree

4 files changed

+115
-166
lines changed

4 files changed

+115
-166
lines changed

modules/imap/output_modules.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,8 @@ protected function output() {
407407
$existing_emails = array_column($contact_list, 'email_address');
408408
$sender = addr_parse($headers['From'])['email'];
409409
$domain = '*@'.get_domain($sender);
410-
$blocked_senders = get_blocked_senders_array($imap_server, $this->get('site_config'), $this->get('user_config'));
410+
list($scripts, $current_script) = get_all_scripts($server_id, true);
411+
$blocked_senders = get_blocked_senders_array($current_script, $scripts);
411412
$sender_blocked = in_array($sender, $blocked_senders);
412413
$domain_blocked = in_array($domain, $blocked_senders);
413414
if(!in_array($sender, $existing_emails)){

modules/sievefilters/functions.php

Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,53 @@
22

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

5+
6+
/**
7+
* Initialize SieveService with all configured IMAP accounts if not already initialized
8+
* @param object $user_config User configuration object
9+
* @param object $cache Cache object
10+
* @return bool True if initialized successfully
11+
*/
12+
if (!hm_exists('ensure_sieve_service_initialized')) {
13+
function ensure_sieve_service_initialized($user_config, $cache) {
14+
if (SieveService::hasAccounts()) {
15+
return true;
16+
}
17+
18+
$accounts = $user_config->get('imap_servers');
19+
$sieve_accounts = array_filter($accounts, function ($account) {
20+
return !empty($account['sieve_config_host']);
21+
});
22+
23+
if (empty($sieve_accounts)) {
24+
return false;
25+
}
26+
27+
$sieve_accounts_configs = [];
28+
foreach ($sieve_accounts as $key => $item) {
29+
$parts = explode(':', $item['sieve_config_host']);
30+
$sieveHost = isset($parts[0]) ? $parts[0] : '';
31+
$sievePort = isset($parts[1]) ? (int) $parts[1] : 4190;
32+
$sieve_accounts_configs[$key] = [
33+
'host' => $sieveHost,
34+
'port' => $sievePort,
35+
'username' => $item['user'],
36+
'password' => $item['pass'],
37+
'secure' => $item['sieve_tls'],
38+
'authType' => 'PLAIN',
39+
'id' => $key
40+
];
41+
}
42+
43+
if (!empty($sieve_accounts_configs)) {
44+
SieveService::init($cache, $sieve_accounts_configs);
45+
return true;
46+
}
47+
48+
return false;
49+
}
50+
}
51+
552
if (!hm_exists('get_script_modal_content')) {
653
function get_script_modal_content()
754
{
@@ -104,49 +151,22 @@ function get_classic_filter_modal_content()
104151
if (!hm_exists('get_mailbox_filters')) {
105152
function get_mailbox_filters($mailbox, $site_config, $user_config)
106153
{
154+
$factory = get_sieve_client_factory($site_config);
107155
try {
108-
// Initialize SieveService with proper cache and server config
109-
$servers = [];
110-
foreach ($user_config->get('imap_servers') as $id => $server) {
111-
if (!empty($server['sieve_config_host'])) {
112-
$servers[$id] = $server;
113-
}
114-
}
115-
116-
// Get cache instance safely
117-
$cacheInstance = null;
118-
if ($site_config && method_exists($site_config, 'get')) {
119-
$cacheInstance = $site_config->get('hm_cache_instance');
120-
}
121-
122-
// Only initialize if we have a valid cache instance
123-
if ($cacheInstance && is_object($cacheInstance)) {
124-
SieveService::init($cacheInstance, $servers);
125-
126-
// Get scripts using SieveService
127-
$scripts = SieveService::listScripts($mailbox['id']);
128-
} else {
129-
// Fallback to direct client connection if no cache available
130-
$factory = get_sieve_client_factory($site_config);
131-
$client = $factory->init($user_config, $mailbox, in_array(mb_strtolower('nux'), $site_config->get_modules(true), true));
132-
$scripts = $client->listScripts();
133-
}
134-
135-
// Filter only cypht scripts
136-
$cypht_scripts = [];
137-
foreach ($scripts as $script) {
156+
$client = $factory->init($user_config, $mailbox, in_array(mb_strtolower('nux'), $site_config->get_modules(true), true));
157+
$scripts = [];
158+
foreach ($client->listScripts() as $script) {
138159
if (mb_strstr($script, 'cypht')) {
139-
$cypht_scripts[] = $script;
160+
$scripts[] = $script;
140161
}
141162
}
142-
143163
} catch (Exception $e) {
144164
Hm_Msgs::add("Sieve: {$e->getMessage()}", "danger");
145165
return ['count' => 0, 'list' => ''];
146166
}
147167

148168
$scripts_sorted = [];
149-
foreach ($cypht_scripts as $script_name) {
169+
foreach ($scripts as $script_name) {
150170
$exp_name = explode('-', $script_name);
151171
if (end($exp_name) == 'cypht') {
152172
$base_class = 'script';
@@ -198,7 +218,7 @@ function get_mailbox_filters($mailbox, $site_config, $user_config)
198218
</tr>
199219
';
200220
}
201-
return ['count' => count($cypht_scripts), 'list' => $script_list];
221+
return ['count' => count($scripts), 'list' => $script_list];
202222
}
203223
}
204224

@@ -517,7 +537,6 @@ function get_blocked_senders_array($current_script, $scripts)
517537
}
518538
$blocked_senders = [];
519539
if ($current_script != '') {
520-
// $blocked_list = prepare_sieve_script ($current_script);
521540
$base64_obj = str_replace("# ", "", preg_split('#\r?\n#', $current_script, 0)[1]);
522541
$blocked_list = json_decode(base64_decode($base64_obj));
523542
if (!$blocked_list) {
@@ -642,24 +661,22 @@ function initialize_sieve_client_factory($site_config, $user_config, $imapServer
642661
if (!hm_exists('get_all_scripts')) {
643662
function get_all_scripts($imapServer, $load_current = true, $return_only = null) {
644663
try {
645-
$client = SieveService::getConnection($imapServer);
646-
if(!is_null($client)){
647-
$scripts = $client->listScripts();
648-
if (!is_array($scripts) || array_search('blocked_senders', $scripts, true) === false) {
649-
return '';
650-
}
651-
$current_script = '';
652-
if($load_current) {
653-
$current_script = SieveService::getScript($imapServer, 'blocked_senders');
654-
}
655-
if ($return_only === 'scripts') return $scripts;
656-
if ($return_only === 'current_script') return $current_script;
657-
658-
return [$scripts, $current_script];
664+
$scripts = SieveService::listScripts($imapServer);
665+
if (!is_array($scripts) || array_search('blocked_senders', $scripts, true) === false) {
666+
return '';
659667
}
660-
return null;
668+
$current_script = '';
669+
if($load_current) {
670+
$current_script = SieveService::getScript($imapServer, 'blocked_senders');
671+
}
672+
if ($return_only === 'scripts') return $scripts;
673+
if ($return_only === 'current_script') return $current_script;
674+
675+
return [$scripts, $current_script];
661676
} catch (Exception $e) {
662-
Hm_Msgs::add("Sieve: {$e->getMessage()}", "danger");
677+
// More specific error message to help debugging
678+
$error_msg = "Failed to get Sieve scripts for server '{$imapServer}': " . $e->getMessage();
679+
Hm_Msgs::add("Sieve: {$error_msg}", "danger");
663680
return null;
664681
}
665682
}
@@ -694,7 +711,9 @@ function get_sieve_linked_mailbox ($scripts, $client, $accountKey = null) {
694711
$script = $client->getScript($s);
695712
}
696713
$base64_obj = str_replace("# ", "", preg_split('#\r?\n#', $script, 0)[2]);
697-
$obj = json_decode(base64_decode($base64_obj))[0];
714+
$decoded = json_decode(base64_decode($base64_obj));
715+
716+
$obj = $decoded === null ? null : (is_array($decoded) ? ($decoded[0] ?? null) : (is_object($decoded) ? reset(get_object_vars($decoded)) : null));
698717
if ($obj && in_array($obj->action, ['copy', 'move'])) {
699718
$folders[$s] = $obj->value;
700719
}

modules/sievefilters/hm-sieve-script-cache.php

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ class SieveScriptCache
1717
*/
1818
private static $cache = null;
1919

20-
/**
21-
* Debug: Track cache access count per request
22-
*/
23-
private static $accessCount = [];
24-
2520
private function __construct() {}
2621

2722
/**
@@ -58,12 +53,6 @@ public static function getCachedScript($key, string $scriptName)
5853

5954
$cacheKey = "sieve_script_{$key}_{$scriptName}";
6055

61-
// Track access count
62-
if (!isset(self::$accessCount[$cacheKey])) {
63-
self::$accessCount[$cacheKey] = 0;
64-
}
65-
self::$accessCount[$cacheKey]++;
66-
6756
$cached = self::$cache->get($cacheKey, false, true);
6857

6958
if ($cached && isset($cached['time']) && (time() - $cached['time']) < self::$scriptCacheTTL) {
@@ -109,15 +98,15 @@ public static function invalidateScript($key, string $scriptName)
10998

11099
/**
111100
* Invalidate all scripts cache for a connection key
101+
* We need to track script names to invalidate,
102+
* for now we'll invalidate the list cache and scripts will be re-fetched
112103
*/
113104
public static function invalidateAllScripts($key)
114105
{
115106
if (!self::$cache) {
116107
return false;
117108
}
118109

119-
// We need to track script names to invalidate,
120-
// for now we'll invalidate the list cache and scripts will be re-fetched
121110
$listCacheKey = "sieve_scripts_list_{$key}";
122111
return self::$cache->del($listCacheKey);
123112
}
@@ -132,7 +121,7 @@ public static function cacheScriptsList($key, array $scripts)
132121
}
133122

134123
$cacheKey = "sieve_scripts_list_{$key}";
135-
return self::$cache->set($cacheKey, $scripts, 300); // 5 minutes TTL
124+
return self::$cache->set($cacheKey, $scripts, 300);
136125
}
137126

138127
/**
@@ -189,8 +178,8 @@ public static function clearAllCache($key)
189178
if (!self::$cache) {
190179
return false;
191180
}
192-
// Implementation depends on cache backend
193-
// This is a simplified version
181+
//TODO: Implement a method to clear all cache entries for a given key
182+
self::invalidateAllScripts($key);
194183
return true;
195184
}
196185
}
@@ -211,6 +200,11 @@ class SieveConnectionManager
211200

212201
private function __construct() {}
213202

203+
public static function getConfig()
204+
{
205+
return self::$config;
206+
}
207+
214208
/**
215209
* Set the configuration for all servers
216210
*/
@@ -327,17 +321,14 @@ public static function init($cacheInstance, array $serverConfigs)
327321
*/
328322
public static function getScript($key, string $scriptName)
329323
{
330-
// Try cache first
331324
$cachedScript = SieveScriptCache::getCachedScript($key, $scriptName);
332325
if ($cachedScript !== false) {
333326
return $cachedScript;
334327
}
335328

336-
// Cache miss — fetch from server
337329
$client = SieveConnectionManager::getConnection($key);
338330
$script = $client->getScript($scriptName);
339331

340-
// Cache the result
341332
SieveScriptCache::cacheScript($key, $scriptName, $script);
342333

343334
return $script;
@@ -348,17 +339,14 @@ public static function getScript($key, string $scriptName)
348339
*/
349340
public static function listScripts($key)
350341
{
351-
// Try cache first
352342
$cached = SieveScriptCache::getCachedScriptsList($key);
353343
if ($cached !== false) {
354344
return $cached;
355345
}
356346

357-
// Get from server and cache
358347
$client = SieveConnectionManager::getConnection($key);
359348
$scripts = $client->listScripts();
360349

361-
// Cache the list
362350
if ($scripts !== false) {
363351
SieveScriptCache::cacheScriptsList($key, $scripts);
364352
}
@@ -374,10 +362,8 @@ public static function putScript($key, string $scriptName, string $scriptContent
374362
$client = SieveConnectionManager::getConnection($key);
375363
$result = $client->putScript($scriptName, $scriptContent);
376364

377-
// Clear cache for this script since it's been updated
378365
SieveScriptCache::clearScriptCache($key, $scriptName);
379366

380-
// Invalidate scripts list cache since a new script might have been added
381367
SieveScriptCache::invalidateScriptsList($key);
382368

383369
return $result;
@@ -400,10 +386,8 @@ public static function removeScripts($key, string $scriptName)
400386
$client = SieveConnectionManager::getConnection($key);
401387
$result = $client->removeScripts($scriptName);
402388

403-
// Clear cache for this script since it's been removed
404389
SieveScriptCache::clearScriptCache($key, $scriptName);
405390

406-
// Invalidate scripts list cache since a script has been removed
407391
SieveScriptCache::invalidateScriptsList($key);
408392

409393
return $result;
@@ -420,14 +404,14 @@ public static function closeConnection($key)
420404

421405
/**
422406
* Rename a script on the server
407+
* Also clears cache for old and new names
423408
*/
424409
public static function renameScript($key, string $oldName, string $newName)
425410
{
426411
try {
427412
$connection = self::getConnection($key);
428413
$result = $connection->renameScript($oldName, $newName);
429414

430-
// Invalidate cache for both old and new names
431415
self::clearScriptCache($key, $oldName);
432416
self::clearScriptCache($key, $newName);
433417

@@ -480,4 +464,9 @@ public static function setConnectionTimeout(int $timeout)
480464
{
481465
SieveConnectionManager::setTimeout($timeout);
482466
}
467+
468+
public static function hasAccounts()
469+
{
470+
return !empty(SieveConnectionManager::getConfig());
471+
}
483472
}

0 commit comments

Comments
 (0)