PageRenderTime 144ms CodeModel.GetById 50ms app.highlight 11ms RepoModel.GetById 80ms app.codeStats 0ms

/code/classes/Daemon/PMaild/MTA/Mail.class.php

https://github.com/blekkzor/pinetd2
PHP | 156 lines | 132 code | 18 blank | 6 comment | 28 complexity | f9836299e97644f8fa2aaa8c2c867187 MD5 | raw file
  1<?php
  2
  3namespace Daemon\PMaild\MTA;
  4use pinetd\SQL;
  5
  6class Mail {
  7	protected $peer;
  8	protected $IPC;
  9	protected $login = null;
 10	protected $helo = NULL;
 11	protected $from = null;
 12	protected $to = array();
 13	protected $received = array();
 14	protected $localConfig;
 15	protected $txn = null;
 16	protected $txlog = array();
 17
 18	private $error; // to err is human
 19
 20	function __construct($peer, $IPC) {
 21		$this->peer = $peer;
 22		$this->IPC = $IPC;
 23		$this->localConfig = $this->IPC->getLocalConfig();
 24	}
 25
 26	static public function header($head, $val) {
 27		$val = $head.': '.$val;
 28		return wordwrap($val, 76, "\r\n\t", true)."\r\n";
 29	}
 30
 31	protected function err($str) { // I err so I am
 32		$this->error = $str;
 33		return false;
 34	}
 35
 36	function errorMsg() {
 37		return $this->error;
 38	}
 39
 40	function reset() {
 41		$this->from = null;
 42		$this->to = array();
 43		$this->received = array();
 44	}
 45
 46	function setHelo($helo) {
 47		if (strlen($helo) < 3) return false;
 48		$this->helo = $helo;
 49		return true;
 50	}
 51
 52	function setLogin($login, $pass) {
 53		// need to check auth
 54		$class = relativeclass($this, 'Auth');
 55		$auth = new $class($this->localConfig);
 56		if (!$auth->login($login, $pass, 'smtp')) return false;
 57		$this->received[] = 'SMTP authenticated user logged in; '.base64_encode($auth->getLogin()).'; '.date(DATE_RFC2822);
 58		$this->login = $auth->getLogin();
 59		return true;
 60	}
 61
 62	function setFrom($from) {
 63		if (!is_null($this->from)) {
 64			return $this->err('503 5.5.2 Syntax error: MAIL FROM already given');
 65		}
 66		$this->from = $from;
 67		if (!is_null($this->peer)) $this->received[] = 'from '.$this->helo.' ('.$this->peer[2].' ['.$this->peer[0].']) by '.$this->localConfig['Name']['_'].' (pMaild); '.date(DATE_RFC2822);
 68		return true;
 69	}
 70
 71	function addTarget($mail) {
 72		if (is_null($this->from)) return $this->err('503 5.5.2 Syntax error: Need MAIL FROM before RCPT TO');
 73		if (strlen($mail) > 128) return $this->err('503 5.1.1 Mail too long');
 74		// parse mail
 75		$pos = strrpos($mail, '@');
 76		if ($pos === false) return $this->err('503 5.1.3 Where did you see an email without @ ??');
 77		$class = relativeclass($this, 'MailSolver');
 78		$mail = $class::solve($mail, $this->localConfig);
 79		if (!is_array($mail)) {
 80			if (is_string($mail)) return $this->err($mail);
 81			return $this->err('403 4.3.0 Unknown error in mail solver subsystem');
 82		}
 83		if (isset($this->to[$mail['mail']])) {
 84			return $this->err('403 4.5.3 Already gave this destination email once');
 85		}
 86		if ( (is_null($this->login)) && (!is_null($this->peer)) ) {
 87			if ($mail['type'] == 'remote') {
 88				// check for global whitelist
 89				$SQL = SQL::Factory($this->localConfig['Storage']);
 90				$DAO_hosts = $SQL->DAO('hosts', 'ip');
 91				$host = $DAO_hosts[$this->peer[0]];
 92				// not in global whitelist?
 93				if ((!$host) || ($host->type != 'trust')) {
 94					if (!$this->allowRemote) return $this->err('503 5.1.2 Relaying denied');
 95				}
 96			} else {
 97				$class = relativeclass($this, 'DNSBL');
 98				$bl = $class::check($this->peer, $mail, $this->localConfig);
 99				if ($bl) return $this->err('550 5.1.8 Your host is blacklisted: '.$bl);
100			}
101		}
102		$this->to[$mail['mail']] = $mail;
103		return true;
104	}
105
106	public function sendMail($stream = null) {
107		// prepare sending a mail
108		if (!is_null($this->txn)) throw new Exception('Something wrong happened');
109		$txn = array(
110			'helo' => $this->helo,
111			'peer' => $this->peer,
112		);
113		$txn['fd'] = fopen('php://temp', 'r+'); // php will write mail in memory if <2M
114		foreach(array_reverse($this->received) as $msg) fputs($txn['fd'], self::header('Received', $msg));
115		$this->txn = &$txn;
116		$txn['parent'] = &$this;
117		return $txn;
118	}
119
120	public function finishMail($IPC) {
121		if (is_null($this->txn)) throw new Exception('finishMail() called without email transaction');
122		// ok, we got our data in txn, time to spawn a maildelivery class
123		$success = 0;
124		$total = 0;
125		$txn = $this->txn;
126		$this->txn = null;
127		$this->txlog = array();
128		foreach($this->to as $target) {
129			$total++;
130			$class = relativeclass($this, 'MailTarget');
131			$MT = new $class($target, $this->from, $this->localConfig, $IPC);
132			$err = $MT->process($txn);
133			if (is_null($err)) {
134				$success++;
135			}
136			$this->txlog[$target['mail']] = $err;
137		}
138		if ($success != $total) {
139			if ($total == 1) return $this->err($err);
140			return $this->err('450 4.5.3 One or more failure while processing mail (success rate: '.$success.'/'.$total.' - use TXLG for details)');
141		}
142		return true;
143	}
144
145	public function cancelMail() {
146		if (is_null($this->txn)) return;
147		fclose($this->txn['fd']);
148		$this->txn = null;
149	}
150
151	public function transmitLog() {
152		return $this->txlog;
153	}
154}
155
156