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)[^"\']*)\1/i', // Square bracket pattern [URL] common in email templates '/\[\s*((?:https?:)?\/\/[^\]]*?(?:unsubscribe)[^\]]*)\s*\]/i', // Bare URLs with unsubscribe/esclick '/((?:https?:)?\/\/[^\s<>\[\]"\']*?(?:unsubscribe)[^\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; $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); $exclude_strings = ['?', 'esputnik', 'unsubscribe/']; foreach ($urls as $uri) { if (str_contains($uri, 'mailto')) continue; $should_continue = false; foreach ($exclude_strings as $exclude_string) { if (str_contains($uri, $exclude_string)) { $should_continue = true; break; } } if (!$should_continue) 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; } }