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

Language PHP Lines 157
MD5 Hash f9836299e97644f8fa2aaa8c2c867187
Repository https://github.com/blekkzor/pinetd2.git View Raw File View Project SPDX
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<?php

namespace Daemon\PMaild\MTA;
use pinetd\SQL;

class Mail {
	protected $peer;
	protected $IPC;
	protected $login = null;
	protected $helo = NULL;
	protected $from = null;
	protected $to = array();
	protected $received = array();
	protected $localConfig;
	protected $txn = null;
	protected $txlog = array();

	private $error; // to err is human

	function __construct($peer, $IPC) {
		$this->peer = $peer;
		$this->IPC = $IPC;
		$this->localConfig = $this->IPC->getLocalConfig();
	}

	static public function header($head, $val) {
		$val = $head.': '.$val;
		return wordwrap($val, 76, "\r\n\t", true)."\r\n";
	}

	protected function err($str) { // I err so I am
		$this->error = $str;
		return false;
	}

	function errorMsg() {
		return $this->error;
	}

	function reset() {
		$this->from = null;
		$this->to = array();
		$this->received = array();
	}

	function setHelo($helo) {
		if (strlen($helo) < 3) return false;
		$this->helo = $helo;
		return true;
	}

	function setLogin($login, $pass) {
		// need to check auth
		$class = relativeclass($this, 'Auth');
		$auth = new $class($this->localConfig);
		if (!$auth->login($login, $pass, 'smtp')) return false;
		$this->received[] = 'SMTP authenticated user logged in; '.base64_encode($auth->getLogin()).'; '.date(DATE_RFC2822);
		$this->login = $auth->getLogin();
		return true;
	}

	function setFrom($from) {
		if (!is_null($this->from)) {
			return $this->err('503 5.5.2 Syntax error: MAIL FROM already given');
		}
		$this->from = $from;
		if (!is_null($this->peer)) $this->received[] = 'from '.$this->helo.' ('.$this->peer[2].' ['.$this->peer[0].']) by '.$this->localConfig['Name']['_'].' (pMaild); '.date(DATE_RFC2822);
		return true;
	}

	function addTarget($mail) {
		if (is_null($this->from)) return $this->err('503 5.5.2 Syntax error: Need MAIL FROM before RCPT TO');
		if (strlen($mail) > 128) return $this->err('503 5.1.1 Mail too long');
		// parse mail
		$pos = strrpos($mail, '@');
		if ($pos === false) return $this->err('503 5.1.3 Where did you see an email without @ ??');
		$class = relativeclass($this, 'MailSolver');
		$mail = $class::solve($mail, $this->localConfig);
		if (!is_array($mail)) {
			if (is_string($mail)) return $this->err($mail);
			return $this->err('403 4.3.0 Unknown error in mail solver subsystem');
		}
		if (isset($this->to[$mail['mail']])) {
			return $this->err('403 4.5.3 Already gave this destination email once');
		}
		if ( (is_null($this->login)) && (!is_null($this->peer)) ) {
			if ($mail['type'] == 'remote') {
				// check for global whitelist
				$SQL = SQL::Factory($this->localConfig['Storage']);
				$DAO_hosts = $SQL->DAO('hosts', 'ip');
				$host = $DAO_hosts[$this->peer[0]];
				// not in global whitelist?
				if ((!$host) || ($host->type != 'trust')) {
					if (!$this->allowRemote) return $this->err('503 5.1.2 Relaying denied');
				}
			} else {
				$class = relativeclass($this, 'DNSBL');
				$bl = $class::check($this->peer, $mail, $this->localConfig);
				if ($bl) return $this->err('550 5.1.8 Your host is blacklisted: '.$bl);
			}
		}
		$this->to[$mail['mail']] = $mail;
		return true;
	}

	public function sendMail($stream = null) {
		// prepare sending a mail
		if (!is_null($this->txn)) throw new Exception('Something wrong happened');
		$txn = array(
			'helo' => $this->helo,
			'peer' => $this->peer,
		);
		$txn['fd'] = fopen('php://temp', 'r+'); // php will write mail in memory if <2M
		foreach(array_reverse($this->received) as $msg) fputs($txn['fd'], self::header('Received', $msg));
		$this->txn = &$txn;
		$txn['parent'] = &$this;
		return $txn;
	}

	public function finishMail($IPC) {
		if (is_null($this->txn)) throw new Exception('finishMail() called without email transaction');
		// ok, we got our data in txn, time to spawn a maildelivery class
		$success = 0;
		$total = 0;
		$txn = $this->txn;
		$this->txn = null;
		$this->txlog = array();
		foreach($this->to as $target) {
			$total++;
			$class = relativeclass($this, 'MailTarget');
			$MT = new $class($target, $this->from, $this->localConfig, $IPC);
			$err = $MT->process($txn);
			if (is_null($err)) {
				$success++;
			}
			$this->txlog[$target['mail']] = $err;
		}
		if ($success != $total) {
			if ($total == 1) return $this->err($err);
			return $this->err('450 4.5.3 One or more failure while processing mail (success rate: '.$success.'/'.$total.' - use TXLG for details)');
		}
		return true;
	}

	public function cancelMail() {
		if (is_null($this->txn)) return;
		fclose($this->txn['fd']);
		$this->txn = null;
	}

	public function transmitLog() {
		return $this->txlog;
	}
}
Back to Top