openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

Mail.class.php (8134B)


      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 }