config->get('layout'); $this->add_hook('message_headers_output', array($this, 'message_headers')); $this->add_hook('storage_init', array($this, 'storage_init')); $this->include_stylesheet('easy_unsubscribe.css'); $this->include_script('easy_unsubscribe.js'); $this->add_texts('localization/'); $rcmail->output->add_label('easy_unsubscribe.confirm'); } public function storage_init($p) { $p['fetch_raw_body'] = true; $p['fetch_headers'] = trim($p['fetch_headers'] . ' ' . strtoupper('List-Unsubscribe')); return $p; } public function decodeMimeHeader($header) { $elements = imap_mime_header_decode($header); return array_reduce($elements, function ($carry, $element) { return $carry . $element->text; }, ''); } public function extractUnsubscribeUrls($content) { // Array to store all patterns we want to match $patterns = [ // Standard tag pattern '/]*?\s+)?href=(["\'])((?:https?:)?\/\/[^"\']*?(?:unsubscribe|dummy-esclick)[^"\']*)\1/i', // Square bracket pattern [URL] common in email templates '/\[\s*((?:https?:)?\/\/[^\]]*?(?:unsubscribe|dummy-esclick)[^\]]*)\s*\]/i', // Bare URLs with unsubscribe/esclick '/((?:https?:)?\/\/[^\s<>\[\]"\']*?(?:unsubscribe|dummy-esclick)[^\s<>\[\]"\']*)/i' ]; $allUrls = []; foreach ($patterns as $pattern) { $matches = []; preg_match_all($pattern, $content, $matches); // For the first pattern, we want group 2, for others group 1 $groupIndex = (strpos($pattern, 'href') !== false) ? 2 : 1; if (!empty($matches[$groupIndex])) { $allUrls = array_merge($allUrls, $matches[$groupIndex]); } } // Remove duplicates and clean URLs $allUrls = array_unique($allUrls); $cleanUrls = array_map(function($url) { return trim($url); }, $allUrls); return array_values(array_filter($cleanUrls)); } public function message_headers($p) { if($this->message_headers_done === false) { $this->message_headers_done = true; if(!isset($p['headers']->others['list-unsubscribe'])) return $p; $urls = []; $rcmail = rcmail::get_instance(); $body = quoted_printable_decode($rcmail->storage->get_raw_body($p['uid'])); $urls = array_merge($urls, $this->extractUnsubscribeUrls($body)); $ListUnsubscribe = $this->decodeMimeHeader($p['headers']->others['list-unsubscribe']); if (preg_match_all('/<(.+)>/mU', $ListUnsubscribe, $items, PREG_PATTERN_ORDER)) { foreach ( $items[1] as $uri ) { $urls[] = $uri; } } $urls = array_unique($urls); foreach ($urls as $uri) { if (str_contains($uri, 'mailto')) continue; if (!str_contains($uri, '?') && !str_contains($uri, 'esputnik')) continue; $this->unsubscribe_img .= '' . $this->gettext('unsubscribe') . ''; } } if(isset($p['output']['subject'])) { $p['output']['subject']['value'] = $p['output']['subject']['value'] . $this->unsubscribe_img; $p['output']['subject']['html'] = 1; } return $p; } }