Files
wevads-platform/app/webservices/MailboxExtractorNative.php
2026-02-26 03:06:17 +01:00

496 lines
18 KiB
PHP

<?php declare(strict_types=1); namespace IR\App\Webservices; if (!defined('IR_START')) exit('<pre>No direct script access allowed</pre>');
/**
* Native PHP Mailbox Extractor - Replaces Java JAR
* Supports ALL ISPs with auto-detection
*/
class MailboxExtractorNative
{
// Known IMAP servers for popular ISPs
private static $imapServers = [
// Major providers
'gmail' => ['host' => 'imap.gmail.com', 'port' => 993],
'googlemail' => ['host' => 'imap.gmail.com', 'port' => 993],
'yahoo' => ['host' => 'imap.mail.yahoo.com', 'port' => 993],
'ymail' => ['host' => 'imap.mail.yahoo.com', 'port' => 993],
'hotmail' => ['host' => 'outlook.office365.com', 'port' => 993],
'outlook' => ['host' => 'outlook.office365.com', 'port' => 993],
'live' => ['host' => 'outlook.office365.com', 'port' => 993],
'msn' => ['host' => 'outlook.office365.com', 'port' => 993],
'onmicrosoft' => ['host' => 'outlook.office365.com', 'port' => 993],
'windowslive' => ['host' => 'outlook.office365.com', 'port' => 993],
'aol' => ['host' => 'imap.aol.com', 'port' => 993],
// European providers
'gmx' => ['host' => 'imap.gmx.net', 'port' => 993],
'web' => ['host' => 'imap.web.de', 'port' => 993],
't-online' => ['host' => 'secureimap.t-online.de', 'port' => 993],
'freenet' => ['host' => 'mx.freenet.de', 'port' => 993],
'orange' => ['host' => 'imap.orange.fr', 'port' => 993],
'wanadoo' => ['host' => 'imap.orange.fr', 'port' => 993],
'free' => ['host' => 'imap.free.fr', 'port' => 993],
'sfr' => ['host' => 'imap.sfr.fr', 'port' => 993],
'laposte' => ['host' => 'imap.laposte.net', 'port' => 993],
'libero' => ['host' => 'imapmail.libero.it', 'port' => 993],
'virgilio' => ['host' => 'in.virgilio.it', 'port' => 993],
'alice' => ['host' => 'in.alice.it', 'port' => 993],
'tiscali' => ['host' => 'imap.tiscali.it', 'port' => 993],
'bluewin' => ['host' => 'imaps.bluewin.ch', 'port' => 993],
'mail' => ['host' => 'imap.mail.ru', 'port' => 993],
'yandex' => ['host' => 'imap.yandex.com', 'port' => 993],
'rambler' => ['host' => 'imap.rambler.ru', 'port' => 993],
// Canadian providers
'videotron' => ['host' => 'imap.videotron.ca', 'port' => 993],
'bell' => ['host' => 'imap.bell.net', 'port' => 993],
'sympatico' => ['host' => 'imap.bell.net', 'port' => 993],
'rogers' => ['host' => 'imap.rogers.com', 'port' => 993],
'shaw' => ['host' => 'imap.shaw.ca', 'port' => 993],
'telus' => ['host' => 'imap.telus.net', 'port' => 993],
// US providers
'comcast' => ['host' => 'imap.comcast.net', 'port' => 993],
'xfinity' => ['host' => 'imap.comcast.net', 'port' => 993],
'att' => ['host' => 'imap.mail.att.net', 'port' => 993],
'sbcglobal' => ['host' => 'imap.mail.att.net', 'port' => 993],
'verizon' => ['host' => 'incoming.verizon.net', 'port' => 993],
'charter' => ['host' => 'mobile.charter.net', 'port' => 993],
'spectrum' => ['host' => 'mobile.charter.net', 'port' => 993],
'cox' => ['host' => 'imap.cox.net', 'port' => 993],
'earthlink' => ['host' => 'imap.earthlink.net', 'port' => 993],
'frontier' => ['host' => 'mail.frontier.com', 'port' => 993],
// Apple
'icloud' => ['host' => 'imap.mail.me.com', 'port' => 993],
'me' => ['host' => 'imap.mail.me.com', 'port' => 993],
'mac' => ['host' => 'imap.mail.me.com', 'port' => 993],
// Business
'zoho' => ['host' => 'imap.zoho.com', 'port' => 993],
'protonmail' => ['host' => 'imap.protonmail.ch', 'port' => 993],
'fastmail' => ['host' => 'imap.fastmail.com', 'port' => 993],
'mailbox' => ['host' => 'imap.mailbox.org', 'port' => 993],
'tutanota' => ['host' => 'imap.tutanota.com', 'port' => 993],
// UK
'btinternet' => ['host' => 'mail.btinternet.com', 'port' => 993],
'virginmedia' => ['host' => 'imap.virginmedia.com', 'port' => 993],
'sky' => ['host' => 'imap.tools.sky.com', 'port' => 993],
'talktalk' => ['host' => 'imap.talktalk.net', 'port' => 993],
// Australia
'bigpond' => ['host' => 'imap.telstra.com', 'port' => 993],
'optusnet' => ['host' => 'mail.optusnet.com.au', 'port' => 993],
// Internal (Plesk/cPanel)
'internal' => ['host' => 'mail.{domain}', 'port' => 993],
];
// Folder mappings per ISP type
private static $folderMappings = [
'gmail' => [
'inbox' => 'INBOX',
'spam' => '[Gmail]/Spam',
'draft' => '[Gmail]/Drafts',
'sent' => '[Gmail]/Sent Mail',
'trash' => '[Gmail]/Trash',
'archive' => '[Gmail]/All Mail',
],
'yahoo' => [
'inbox' => 'INBOX',
'spam' => 'Bulk Mail',
'draft' => 'Draft',
'sent' => 'Sent',
'trash' => 'Trash',
'archive' => 'Archive',
],
'outlook' => [
'inbox' => 'INBOX',
'spam' => 'Junk',
'draft' => 'Drafts',
'sent' => 'Sent',
'trash' => 'Deleted',
'archive' => 'Archive',
],
'default' => [
'inbox' => 'INBOX',
'spam' => 'Spam',
'draft' => 'Drafts',
'sent' => 'Sent',
'trash' => 'Trash',
'archive' => 'Archive',
],
];
/**
* Extract emails from mailboxes
*/
public static function extract(array $params): array
{
$mailboxes = $params['mailboxes'] ?? [];
$folder = $params['folder'] ?? 'inbox';
$maxEmails = intval($params['max-emails'] ?? 10);
$order = $params['order'] ?? 'new-to-old';
$startDate = $params['start-date'] ?? '';
$endDate = $params['end-date'] ?? '';
$separator = $params['separator'] ?? '_SEPARATOR_';
$returnType = $params['return-type'] ?? 'full-source';
$returnHeaderKey = $params['return-header-key'] ?? 'none';
$filters = $params['filters'] ?? [];
$filterType = $params['filter-type'] ?? 'and';
$results = [];
foreach ($mailboxes as $mailboxLine) {
$parts = preg_split('/\s+/', trim($mailboxLine), 4);
if (count($parts) < 2) continue;
$email = strtolower(trim($parts[0]));
$password = trim($parts[1]);
$proxyHost = $parts[2] ?? null;
$proxyPort = $parts[3] ?? null;
try {
$emailResults = self::extractFromMailbox(
$email,
$password,
$folder,
$maxEmails,
$order,
$startDate,
$endDate,
$separator,
$returnType,
$returnHeaderKey,
$filters,
$filterType
);
if (!empty($emailResults)) {
$results[$email] = implode("\n" . $separator . "\n", $emailResults);
}
} catch (\Exception $e) {
$results[$email] = "Error: " . $e->getMessage();
}
}
return $results;
}
/**
* Extract from a single mailbox
*/
private static function extractFromMailbox(
string $email,
string $password,
string $folder,
int $maxEmails,
string $order,
string $startDate,
string $endDate,
string $separator,
string $returnType,
string $returnHeaderKey,
array $filters,
string $filterType
): array {
// Get domain and ISP
$domain = substr($email, strpos($email, '@') + 1);
$ispParts = explode('.', $domain);
$isp = strtolower($ispParts[0]);
// Handle t-online special case
if (strpos($domain, 't-online') !== false) {
$isp = 't-online';
}
// Get IMAP settings
$imapSettings = self::getImapSettings($isp, $domain);
$folderName = self::getFolderName($isp, $folder);
// Build connection string
$host = $imapSettings['host'];
$port = $imapSettings['port'];
$connectionString = "{{$host}:{$port}/imap/ssl/novalidate-cert}{$folderName}";
// Connect
$mailbox = @imap_open($connectionString, $email, $password);
if (!$mailbox) {
$error = imap_last_error();
throw new \Exception("Connection failed: " . $error);
}
try {
// Build search criteria
$searchCriteria = 'ALL';
if (!empty($startDate) && !empty($endDate)) {
$start = date('d-M-Y', strtotime($startDate));
$end = date('d-M-Y', strtotime($endDate));
$searchCriteria = "SINCE \"$start\" BEFORE \"$end\"";
} elseif (!empty($startDate)) {
$start = date('d-M-Y', strtotime($startDate));
$searchCriteria = "SINCE \"$start\"";
} elseif (!empty($endDate)) {
$end = date('d-M-Y', strtotime($endDate));
$searchCriteria = "BEFORE \"$end\"";
}
// Search messages
$messageIds = imap_search($mailbox, $searchCriteria);
if (!$messageIds || count($messageIds) === 0) {
imap_close($mailbox);
return [];
}
// Sort by order
if ($order === 'new-to-old') {
rsort($messageIds);
} else {
sort($messageIds);
}
// Limit
$messageIds = array_slice($messageIds, 0, $maxEmails);
// Extract messages
$results = [];
foreach ($messageIds as $msgId) {
$content = self::extractMessage($mailbox, $msgId, $returnType, $returnHeaderKey);
// Apply filters if any
if (!empty($filters) && !self::passesFilters($content, $filters, $filterType)) {
continue;
}
if (!empty($content)) {
$results[] = $content;
}
}
imap_close($mailbox);
return $results;
} catch (\Exception $e) {
@imap_close($mailbox);
throw $e;
}
}
/**
* Extract a single message
*/
private static function extractMessage($mailbox, int $msgId, string $returnType, string $returnHeaderKey): string
{
switch ($returnType) {
case 'full-source':
// Get complete raw source
$header = imap_fetchheader($mailbox, $msgId);
$body = imap_body($mailbox, $msgId);
return $header . "\r\n" . $body;
case 'full-header':
// Get all headers
$headerInfo = imap_fetchheader($mailbox, $msgId);
return $headerInfo;
case 'full-body':
// Get body text only
return self::getBodyText($mailbox, $msgId);
case 'header-value':
// Get specific header values
if ($returnHeaderKey === 'none' || empty($returnHeaderKey)) {
return '';
}
$keys = explode('|', $returnHeaderKey);
$headers = imap_rfc822_parse_headers(imap_fetchheader($mailbox, $msgId));
$result = [];
foreach ($keys as $key) {
$key = strtolower(trim($key));
$headerObj = (array)$headers;
// Map common header names
$headerMap = [
'from' => 'fromaddress',
'to' => 'toaddress',
'subject' => 'subject',
'date' => 'date',
'reply-to' => 'reply_toaddress',
'cc' => 'ccaddress',
'bcc' => 'bccaddress',
];
if (isset($headerMap[$key]) && isset($headerObj[$headerMap[$key]])) {
$result[] = ucfirst($key) . ":" . $headerObj[$headerMap[$key]];
} else {
// Try to get from raw headers
$rawHeaders = imap_fetchheader($mailbox, $msgId);
if (preg_match('/^' . preg_quote($key, '/') . ':\s*(.*)$/mi', $rawHeaders, $matches)) {
$result[] = ucfirst($key) . ":" . trim($matches[1]);
}
}
}
return implode("\n", $result);
default:
return '';
}
}
/**
* Get body text from message
*/
private static function getBodyText($mailbox, int $msgId): string
{
$structure = imap_fetchstructure($mailbox, $msgId);
if (!$structure->parts) {
// Simple message
$body = imap_body($mailbox, $msgId);
if ($structure->encoding == 3) {
$body = base64_decode($body);
} elseif ($structure->encoding == 4) {
$body = quoted_printable_decode($body);
}
return $body;
}
// Multipart message
return self::getPartText($mailbox, $msgId, $structure);
}
/**
* Recursively get text parts
*/
private static function getPartText($mailbox, int $msgId, $structure, string $partNumber = ''): string
{
$text = '';
if (!isset($structure->parts) || !$structure->parts) {
if ($structure->subtype === 'PLAIN') {
$body = $partNumber ? imap_fetchbody($mailbox, $msgId, $partNumber) : imap_body($mailbox, $msgId);
if ($structure->encoding == 3) {
$body = base64_decode($body);
} elseif ($structure->encoding == 4) {
$body = quoted_printable_decode($body);
}
return $body;
}
return '';
}
foreach ($structure->parts as $index => $part) {
$subPartNumber = $partNumber ? $partNumber . '.' . ($index + 1) : (string)($index + 1);
$text .= self::getPartText($mailbox, $msgId, $part, $subPartNumber) . "\n";
}
return $text;
}
/**
* Check if message passes filters
*/
private static function passesFilters(string $content, array $filters, string $filterType): bool
{
if (empty($filters)) return true;
$results = [];
foreach ($filters as $filter) {
$key = $filter['key'] ?? '';
$condition = $filter['condition'] ?? 'contains';
$value = $filter['value'] ?? '';
if (empty($key) || empty($value)) continue;
// Search for the header in content
$pattern = '/^' . preg_quote($key, '/') . ':\s*(.*)$/mi';
if (preg_match($pattern, $content, $matches)) {
$headerValue = trim($matches[1]);
switch ($condition) {
case 'equals':
$results[] = (strtolower($headerValue) === strtolower($value));
break;
case 'not-equals':
$results[] = (strtolower($headerValue) !== strtolower($value));
break;
case 'contains':
$results[] = (stripos($headerValue, $value) !== false);
break;
case 'not-contains':
$results[] = (stripos($headerValue, $value) === false);
break;
default:
$results[] = true;
}
} else {
$results[] = false;
}
}
if (empty($results)) return true;
if ($filterType === 'and') {
return !in_array(false, $results, true);
} else {
return in_array(true, $results, true);
}
}
/**
* Get IMAP settings for an ISP
*/
private static function getImapSettings(string $isp, string $domain): array
{
// Check if known ISP
if (isset(self::$imapServers[$isp])) {
$settings = self::$imapServers[$isp];
// Replace {domain} placeholder for internal/custom domains
$settings['host'] = str_replace('{domain}', $domain, $settings['host']);
return $settings;
}
// Try common patterns for unknown ISPs
$commonPatterns = [
'imap.' . $domain,
'mail.' . $domain,
'imap.mail.' . $domain,
$domain,
];
foreach ($commonPatterns as $host) {
if (@fsockopen($host, 993, $errno, $errstr, 3)) {
return ['host' => $host, 'port' => 993];
}
}
// Default fallback - try imap.domain
return ['host' => 'imap.' . $domain, 'port' => 993];
}
/**
* Get folder name for ISP
*/
private static function getFolderName(string $isp, string $folder): string
{
// Determine ISP type for folder mapping
$ispType = 'default';
if (in_array($isp, ['gmail', 'googlemail'])) {
$ispType = 'gmail';
} elseif (in_array($isp, ['yahoo', 'ymail'])) {
$ispType = 'yahoo';
} elseif (in_array($isp, ['hotmail', 'outlook', 'live', 'msn', 'onmicrosoft', 'windowslive'])) {
$ispType = 'outlook';
}
$mappings = self::$folderMappings[$ispType] ?? self::$folderMappings['default'];
return $mappings[$folder] ?? 'INBOX';
}
}