* @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Cron * * @since 0.9.32 * */ class MailLogParser { private $startTime; private $domainTraffic = array(); private $myDomains = array(); private $mails = array(); /** * constructor * @param string logFile * @param int startTime * @param string logFileExim */ public function __construct($startTime = 0) { $this->startTime = $startTime; // Get all domains from Database $stmt = Database::prepare("SELECT domain FROM `" . TABLE_PANEL_DOMAINS . "`"); Database::pexecute($stmt, array()); while ($domain_row = $stmt->fetch(PDO::FETCH_ASSOC)) { $this->myDomains[] = $domain_row["domain"]; } // Parse MTA traffic if (Settings::Get("system.mtaserver") == "postfix") { $this->_parsePostfixLog(Settings::Get("system.mtalog")); $this->_parsePostfixLog(Settings::Get("system.mtalog") . ".1"); } elseif (Settings::Get("system.mtaserver") == "exim4") { $this->_parseExim4Log(Settings::Get("system.mtalog")); } // Parse MDA traffic if (Settings::Get("system.mdaserver") == "dovecot") { $this->_parseDovecotLog(Settings::Get("system.mdalog")); $this->_parsePostfixLog(Settings::Get("system.mdalog") . ".1"); } elseif (Settings::Get("system.mdaserver") == "courier") { $this->_parseCourierLog(Settings::Get("system.mdalog")); $this->_parsePostfixLog(Settings::Get("system.mdalog") . ".1"); } } /** * parsePostfixLog * parses the traffic from a postfix logfile * @param logFile */ private function _parsePostfixLog($logFile) { // Check if file exists if (!file_exists($logFile)) { return false; } // Open the log file try { $file_handle = fopen($logFile, "r"); if (!$file_handle) { throw new Exception("Could not open the file!"); } } catch (Exception $e) { echo "Error (File: ".$e->getFile().", line ". $e->getLine()."): ".$e->getMessage(); return false; } while (!feof($file_handle)) { unset($matches); $line = fgets($file_handle); $timestamp = $this->_getLogTimestamp($line); if ($this->startTime < $timestamp) { if (preg_match("/postfix\/qmgr.*(?::|\])\s([A-Z\d]+).*from=?, size=(\d+),/", $line, $matches)) { // Postfix from $this->mails[$matches[1]] = array("domainFrom" => strtolower($matches[2]), "size" => $matches[3]); } elseif (preg_match("/postfix\/(?:pipe|smtp).*(?::|\])\s([A-Z\d]+).*to=?,/", $line, $matches)) { // Postfix to if (array_key_exists($matches[1], $this->mails)) { $this->mails[$matches[1]]["domainTo"] = strtolower($matches[2]); // Only mails from/to outside the system should be added $mail = $this->mails[$matches[1]]; if (in_array($mail["domainFrom"], $this->myDomains) || in_array($mail["domainTo"], $this->myDomains)) { // Outgoing traffic if (array_key_exists("domainFrom", $mail)) { $this->_addDomainTraffic($mail["domainFrom"], $mail["size"], $timestamp); } // Incoming traffic if (array_key_exists("domainTo", $mail) && in_array($mail["domainTo"], $this->myDomains)) { $this->_addDomainTraffic($mail["domainTo"], $mail["size"], $timestamp); } } unset($mail); } } } } fclose($file_handle); return true; } /** * parseExim4Log * parses the smtp traffic from a exim4 logfile * @param logFile */ private function _parseExim4Log($logFile) { // Check if file exists if (!file_exists($logFile)) { return false; } // Open the log file try { $file_handle = fopen($logFile, "r"); if (!$file_handle) { throw new Exception("Could not open the file!"); } } catch (Exception $e) { echo "Error (File: ".$e->getFile().", line ". $e->getLine()."): ".$e->getMessage(); return false; } while (!feof($file_handle)) { unset($matches); $line = fgets($file_handle); $timestamp = $this->_getLogTimestamp($line); if ($this->startTime < $timestamp) { if (preg_match("/<= .*@([a-z0-9.\-]+) .*S=(\d+)/i", $line, $matches)) { // Outgoing traffic $this->_addDomainTraffic($matches[1], $matches[2], $timestamp); } elseif (preg_match("/=> .*? .*S=(\d+)/i" , $line, $matches)) { // Incoming traffic $this->_addDomainTraffic($matches[1], $matches[2], $timestamp); } } } fclose($file_handle); return true; } /** * parseDovecotLog * parses the dovecot imap/pop3 traffic from logfile * @param logFile */ private function _parseDovecotLog($logFile) { // Check if file exists if (!file_exists($logFile)) { return false; } // Open the log file try { $file_handle = fopen($logFile, "r"); if (!$file_handle) { throw new Exception("Could not open the file!"); } } catch (Exception $e) { echo "Error (File: ".$e->getFile().", line ". $e->getLine()."): ".$e->getMessage(); return false; } while (!feof($file_handle)) { unset($matches); $line = fgets($file_handle); $timestamp = $this->_getLogTimestamp($line); if ($this->startTime < $timestamp) { if (preg_match("/dovecot.*(?::|\]) imap\(.*@([a-z0-9\.\-]+)\):.*in=(\d+) out=(\d+)/i", $line, $matches)) { // Dovecot IMAP $this->_addDomainTraffic($matches[1], (int)$matches[2] + (int)$matches[3], $timestamp); } elseif (preg_match("/dovecot.*(?::|\]) pop3\(.*@([a-z0-9\.\-]+)\):.*size=(\d+)/i", $line, $matches)) { // Dovecot POP3 $this->_addDomainTraffic($matches[1], $matches[2], $timestamp); } } } fclose($file_handle); return true; } /** * parseCourierLog * parses the dovecot imap/pop3 traffic from logfile * @param logFile */ private function _parseCourierLog($logFile) { // Check if file exists if (!file_exists($logFile)) { return false; } // Open the log file try { $file_handle = fopen($logFile, "r"); if (!$file_handle) { throw new Exception("Could not open the file!"); } } catch (Exception $e) { echo "Error (File: ".$e->getFile().", line ". $e->getLine()."): ".$e->getMessage(); return false; } while (!feof($file_handle)) { unset($matches); $line = fgets($file_handle); $timestamp = $this->_getLogTimestamp($line); if ($this->startTime < $timestamp) { if (preg_match("/(?:imapd|pop3d)(?:-ssl)?.*(?::|\]).*user=.*@([a-z0-9\.\-]+),.*rcvd=(\d+), sent=(\d+),/i", $line, $matches)) { // Courier IMAP & POP3 $this->_addDomainTraffic($matches[1], (int)$matches[2] + (int)$matches[3], $timestamp); } } } fclose($file_handle); return true; } /** * _addDomainTraffic * adds the traffic to the domain array if we own the domain * @param string domain * @param int traffic */ private function _addDomainTraffic($domain, $traffic, $timestamp) { $date = date("Y-m-d", $timestamp); if (in_array($domain, $this->myDomains)) { if (array_key_exists($domain, $this->domainTraffic) && array_key_exists($date, $this->domainTraffic[$domain])) { $this->domainTraffic[$domain][$date] += (int)$traffic; } else { if (!array_key_exists($domain, $this->domainTraffic)) { $this->domainTraffic[$domain] = array(); } $this->domainTraffic[$domain][$date] = (int)$traffic; } } } /** * getLogTimestamp * @param string line * return int */ private function _getLogTimestamp($line) { if (preg_match("/((?:[A-Z]{3}\s{1,2}\d{1,2}|\d{4}-\d{2}-\d{2}) \d{2}:\d{2}:\d{2})/i", $line, $matches)) { $timestamp = strtotime($matches[1]); if ($timestamp > ($this->startTime + 60 * 60 * 24)) { return strtotime($matches[1] . " -1 year"); } else { return strtotime($matches[1]); } } else { return 0; } } /** * getDomainTraffic * returns the traffic of a given domain or 0 if the domain has no traffic * @param string domain * return array */ public function getDomainTraffic($domain) { if (array_key_exists($domain, $this->domainTraffic)) { return $this->domainTraffic[$domain]; } else { return 0; } } }