File modules/util/mail/Mail.class.php

Last commit: Wed Oct 27 02:27:59 2021 +0200	Jan Dankert	Refactoring: Splitted the mail client into a.) sendmail and b.) smtp.
1 <?php 2 // OpenRat Content Management System 3 // Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de 4 // 5 // This program is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU General Public License 7 // as published by the Free Software Foundation; either version 2 8 // of the License, or (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with this program; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 namespace util\mail; 20 21 use cms\base\Configuration; 22 use cms\base\Language; 23 use cms\base\Startup; 24 use LogicException; 25 use util\mail\client\SendmailClient; 26 use util\mail\client\SmtpClient; 27 use util\text\TextMessage; 28 use util\text\variables\VariableResolver; 29 30 /** 31 * Erzeugen und Versender einer E-Mail gemaess RFC 822.<br> 32 * <br> 33 * Die E-Mail kann entweder �ber 34 * - die interne PHP-Funktion "mail()" versendet werden oder 35 * - direkt per SMTP-Protokoll an einen SMTP-Server.<br> 36 * Welcher Weg gew�hlt wird, kann konfiguriert werden.<br> 37 * <br> 38 * Prinzipiell spricht nichts gegen die interne PHP-Funktion mail(), wenn diese 39 * aber nicht zu Verf�gung steht oder PHP ungeeignet konfiguriert ist, so kann 40 * SMTP direkt verwendet werden. Hierbei sollte wenn m�glich ein Relay-Host 41 * eingesetzt werden. Die Mail kann zwar auch direkt an Mail-Exchanger (MX) des 42 * Empf�ngers geschickt werden, falls dieser aber Greylisting einsetzt ist eine 43 * Zustellung nicht m�glich.<br> 44 * <br> 45 * 46 * @author Jan Dankert 47 */ 48 class Mail 49 { 50 /** 51 * Newline characters as defined in RFC 822. 52 */ 53 const NL = "\r\n"; 54 55 private $from = ''; 56 private $to = ''; 57 private $bcc = ''; 58 private $cc = ''; 59 private $subject = ''; 60 private $text = ''; 61 private $header = array(); 62 private $vars = []; 63 64 /** 65 * @var string 66 */ 67 private $mailKey; 68 69 70 /** 71 * Konstruktor. 72 * Es werden folgende Parameter erwartet 73 * @param String $to Empf�nger 74 * @param string $subjectKey 75 * @param string $mailKey 76 */ 77 function __construct($to, $subjectKey, $mailKey) 78 { 79 $mailConfig = Configuration::subset('mail'); 80 81 82 if ( $mailConfig->has('from')) 83 $this->from = $this->header_encode($mailConfig->get('from')); 84 85 // Priorit�t definieren (sofern konfiguriert) 86 if ($mailConfig->has('priority')) 87 $this->header[] = 'X-Priority: ' . $mailConfig->get('priority'); 88 89 $this->header[] = 'X-Mailer: ' . $this->header_encode(Startup::TITLE ); 90 $this->header[] = 'Content-Type: text/plain; charset=UTF-8'; 91 $this->subject = $this->header_encode(Language::lang($subjectKey)); 92 $this->to = $this->header_encode($to); 93 94 $this->text = self::NL . wordwrap(Language::lang($mailKey), 70, self::NL) . self::NL; 95 96 // Signatur anhaengen (sofern konfiguriert) 97 $signature = $mailConfig->get('signature',Configuration::subset('application')->get('name',Startup::TITLE) ); 98 99 $this->text .= self::NL . '-- ' . self::NL; 100 $this->text .= self::NL . $signature; 101 $this->text .= self::NL; 102 103 // copy 104 if ( $mailConfig->has('cc')) 105 $this->cc = $this->header_encode( implode(', ',$mailConfig->get('cc',[]))); 106 107 // blind copy 108 if ( $mailConfig->has('bcc')) 109 $this->bcc = $this->header_encode( implode(', ',$mailConfig->get('bcc',[]))); 110 111 $this->mailKey = $mailKey; 112 } 113 114 115 /** 116 * Kodiert einen Text in das Format "Quoted-printable".<br> 117 * See RFC 2045. 118 * @param string $text Eingabe 119 * @return string Text im quoted-printable-Format 120 */ 121 private function quoted_printable_encode($text) 122 { 123 $text = str_replace(' ', '=20', $text); 124 125 for ($i = 128; $i <= 255; $i++) { 126 $text = str_replace(chr($i), '=' . dechex($i), $text); 127 } 128 129 return $text; 130 } 131 132 133 /** 134 * Setzen einer Variablen in den Mail-Inhalt. 135 */ 136 public function setVar($varName, $varInhalt) 137 { 138 $this->vars[ $varName ] = $varInhalt; 139 } 140 141 142 /** 143 * Mail absenden. 144 * Die E-Mail wird versendet. 145 */ 146 public function send() 147 { 148 $mailConfig = Configuration::subset('mail'); 149 150 if (strpos($this->to, '@') === FALSE) 151 throw new LogicException("E-Mail-Adress does not contain a domain name: " . $this->to); 152 153 $to_domain = explode('@', $this->to)[1]; 154 155 // Prüfen gegen die Whitelist 156 $white = $mailConfig->get('whitelist',[]); 157 158 if ($white) { 159 if (!$this->containsDomain($to_domain, $white)) { 160 // Wenn Domain nicht in Whitelist gefunden, dann Mail nicht verschicken. 161 throw new LogicException( TextMessage::create('Mail-Domain ${0} is not whitelisted',[$to_domain])); 162 } 163 } 164 165 // Prüfen gegen die Blacklist 166 $black = $mailConfig->get('blacklist',[]); 167 168 if ($black) { 169 if ($this->containsDomain($to_domain, $black)) { 170 // Wenn Domain in Blacklist gefunden, dann Mail nicht verschicken. 171 throw new LogicException( TextMessage::create('Mail-Domain ${0} is blacklisted',[$to_domain])); 172 } 173 } 174 175 // Header um Adressangaben erg�nzen. 176 if (!empty($this->from)) 177 $this->header[] = 'From: ' . $this->from; 178 179 if (!empty($this->cc)) 180 $this->header[] = 'Cc: ' . $this->cc; 181 182 if (!empty($this->bcc)) 183 $this->header[] = 'Bcc: ' . $this->bcc; 184 185 // Evaluate variables in mail data 186 $resolver = new VariableResolver(); 187 $resolver->addDefaultResolver( function($key) { 188 return $this->vars[$key]; 189 } ); 190 $text = $resolver->resolveVariables( $this->text ); 191 192 // Mail versenden 193 if (in_array(strtolower($mailConfig->get('client','php')),['php','sendmail'])) 194 $client = new SendmailClient(); 195 else 196 $client = new SmtpClient(); 197 198 $client->send( 199 $this->to, // Empf�nger 200 $this->subject, // Betreff 201 $text, // Inhalt 202 $this->header 203 ); 204 205 } 206 207 208 209 210 /** 211 * Umwandlung von 8-bit-Zeichen in MIME-Header gemaess RFC 2047.<br> 212 * Header d�rfen nur 7-bit-Zeichen enthalten. 8-bit-Zeichen m�ssen kodiert werden. 213 * 214 * @param String $text 215 * @return String 216 */ 217 private function header_encode($text) 218 { 219 $mailConfig = Configuration::subset('mail'); 220 221 if (! $mailConfig->has('header_encoding')) 222 return $text; 223 224 $woerter = explode(' ', $text); 225 $neu = array(); 226 227 228 foreach ($woerter as $wort) { 229 $type = strtolower(substr($mailConfig->get('header_encoding','Quoted-printable'), 0, 1)); 230 $neu_wort = ''; 231 232 if ($type == 'b') 233 $neu_wort = base64_encode($wort); 234 elseif ($type == 'q') 235 $neu_wort = $this->quoted_printable_encode($wort); 236 else 237 throw new LogicException('Mail-Configuration broken: UNKNOWN Header-Encoding type: ' . $type); 238 239 if (strlen($wort) == strlen($neu_wort)) 240 $neu[] = $wort; 241 else 242 $neu[] = '=?UTF-8?' . $type . '?' . $neu_wort . '?='; 243 } 244 245 return implode(' ', $neu); 246 } 247 248 249 /** 250 * Stellt fest, ob die E-Mail-Adresse eine gueltige Syntax besitzt. 251 * 252 * Es wird nur die Syntax geprüft. Ob die Adresse wirklich existiert, steht dadurch noch lange 253 * nicht fest. Dazu müsste man die MX-Records auflösen und einen Zustellversuch unternehmen. 254 * 255 * @param $email_address string Adresse 256 * @return true, falls Adresse OK, sonst false 257 */ 258 public static function checkAddress($email_address) 259 { 260 // Source: de.php.net/ereg 261 return \preg_match("/^[_a-z0-9-+]+(\.[_a-z0-9-+]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/i", $email_address); 262 } 263 264 265 /** 266 * Prüft, ob eine Domain in einer List von Domains enthalten ist. 267 * 268 * @param $checkDomain string zu prüfende Domain 269 * @param $domains string Liste von Domains als kommaseparierte Liste 270 * @return true, falls vorhanden, sonst false 271 */ 272 private static function containsDomain($checkDomain, $domains) 273 { 274 foreach ($domains as $domain) { 275 $domain = trim($domain); 276 277 if (empty($domain)) 278 continue; 279 280 if ($domain == substr($checkDomain, -strlen($domain))) { 281 return true; 282 } 283 } 284 return false; 285 } 286 287 public function __toString() 288 { 289 return TextMessage::create('Mail to ${0} with subject key ${1}',[$this->to,$this->mailKey]); 290 } 291 }
Download modules/util/mail/Mail.class.php
History Wed, 27 Oct 2021 02:27:59 +0200 Jan Dankert Refactoring: Splitted the mail client into a.) sendmail and b.) smtp.