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