1 <?php
2 3 4 5 6 7 8
9 class Mailer extends Object {
10
11 12 13
14 protected static $fallback_sender = 'trash@mediaweb.ru';
15
16 static function set_fallback_sender($value) {
17 self::$fallback_sender = validEmailAddr($value);
18 }
19
20 static function get_fallback_sender() {
21 return self::$fallback_sender;
22 }
23
24 25 26
27 protected static $encoding ='base64';
28
29 static function set_encoding($val) {
30 if ($val == 'base64' || $val == '8bit' || $val == '') {
31 self::$encoding = $val;
32 }
33 else {
34 die("Unsupported mail encoding: '$val'");
35 }
36 }
37
38 static function get_encoding() {
39 return self::$encoding;
40 }
41
42 43 44 45 46 47 48 49 50 51 52
53 function sendPlain($to, $from, $subject, $plainContent, $attachedFiles = false, $customheaders = false) {
54 return plaintextEmail($to, $from, $subject, $plainContent, $attachedFiles, $customheaders);
55 }
56
57 58 59 60 61
62 function sendHTML($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false, $plainContent = false, $inlineImages = false) {
63 return htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles, $customheaders, $plainContent, $inlineImages);
64 }
65
66 67 68 69 70 71 72 73
74 static function replace_sender($headers, $sender = false) {
75 if (!$sender)
76 $sender = self::get_fallback_sender();
77
78
79
80 if (isset($_SERVER['HTTP_HOST']) && strpos($sender, '<') === false)
81 $sender = sprintf('"%s" <%s>', $_SERVER['HTTP_HOST'], $sender);
82
83 return preg_replace('/(From|Sender): .*/i', '$1: ' . validEmailAddr($sender), $headers);
84 }
85 }
86
87
88
89
90 91 92 93 94 95 96 97
98 function htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false, $plainContent = false, $inlineImages = false) {
99 if ($customheaders && is_array($customheaders) == false) {
100 echo "htmlEmail($to, $from, $subject, ...) could not send mail: improper \$customheaders passed:<BR>";
101 dieprintr($headers);
102 }
103
104 $from = validEmailAddr($from);
105 $to = validEmailAddr($to);
106
107 $subjectIsUnicode = true;
108 $bodyIsUnicode = true;
109 $plainEncoding = Mailer::get_encoding();
110
111
112 if(!$plainContent) {
113 $plainContent = Convert::xml2raw($htmlContent);
114 }
115
116
117
118 $subject = Convert::xml2raw($subject);
119 if(isset($subjectIsUnicode) && $subjectIsUnicode)
120 $subject = "=?UTF-8?B?" . base64_encode($subject) . "?=";
121
122
123
124 $headers["Content-Type"] = "text/plain; charset=\"utf-8\"";
125 $headers["Content-Transfer-Encoding"] = $plainEncoding ? $plainEncoding : "8bit";
126
127 $plainPart = processHeaders($headers, ($plainEncoding == "base64") ? chunk_split(base64_encode($plainContent),60) : wordwrap($plainContent,120));
128
129
130 $headers["Content-Type"] = "text/html; charset=\"utf-8\"";
131
132
133
134 if(stripos($htmlContent, '<body') === false) {
135 $htmlContent =
136 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" .
137 "<HTML><HEAD>\n" .
138 "<META http-equiv=Content-Type content=\"text/html; charset=utf-8\">\n" .
139 "<STYLE type=3Dtext/css></STYLE>\n\n".
140 "</HEAD>\n" .
141 "<BODY bgColor=#ffffff>\n" .
142 $htmlContent .
143 "\n</BODY>\n" .
144 "</HTML>";
145 }
146
147 if($inlineImages) {
148 $htmlPart = wrapImagesInline($htmlContent);
149 } else {
150 $headers["Content-Transfer-Encoding"] = $plainEncoding ? $plainEncoding : "8bit";
151 $htmlPart = processHeaders($headers, ($plainEncoding == "base64") ? chunk_split(base64_encode($htmlContent),120) : wordwrap($htmlContent,120));
152
153
154 }
155
156 list($messageBody, $messageHeaders) = encodeMultipart(array($plainPart,$htmlPart), "multipart/alternative");
157
158
159 if($attachedFiles && is_array($attachedFiles)) {
160
161
162 $fullMessage = processHeaders($messageHeaders, $messageBody);
163 $messageParts = array($fullMessage);
164
165
166 foreach($attachedFiles as $file) {
167 if(isset($file['tmp_name']) && isset($file['name'])) {
168 $messageParts[] = encodeFileForEmail($file['tmp_name'], $file['name']);
169 } else {
170 $messageParts[] = encodeFileForEmail($file);
171 }
172 }
173
174
175 list($fullBody, $headers) = encodeMultipart($messageParts, "multipart/mixed");
176
177
178 } else {
179 $headers = $messageHeaders;
180 $fullBody = $messageBody;
181 }
182
183
184 $headers["From"] = $from;
185 $headers["Reply-To"] = $from;
186
187
188 if(isset($customheaders["X-SilverStripeMessageID"]) && defined('BOUNCE_EMAIL')) {
189 $bounceAddress = BOUNCE_EMAIL;
190 } else {
191 $bounceAddress = $from;
192 }
193
194
195 if(ereg('^([^<>]*)<([^<>]+)> *$', $bounceAddress, $parts)) $bounceAddress = $parts[2];
196
197
198 $headers["X-Mailer"] = X_MAILER;
199 if (!isset($customheaders["X-Priority"])) $headers["X-Priority"] = 3;
200
201 $headers = array_merge((array)$headers, (array)$customheaders);
202
203
204 if (isset($headers['CC'])) { $headers['Cc'] = $headers['CC']; unset($headers['CC']); }
205 if (isset($headers['cc'])) { $headers['Cc'] = $headers['cc']; unset($headers['cc']); }
206
207
208 if (isset($headers['BCC'])) {$headers['Bcc']=$headers['BCC']; unset($headers['BCC']); }
209 if (isset($headers['bcc'])) {$headers['Bcc']=$headers['bcc']; unset($headers['bcc']); }
210
211
212 $headers = processHeaders($headers);
213
214
215 if (!($result = @mail($to, $subject, $fullBody, $headers, "-f$bounceAddress"))) {
216 $result = mail($to, $subject, $fullBody, Mailer::replace_sender($headers));
217 }
218
219 return $result;
220 }
221
222 223 224
225 function plaintextEmail($to, $from, $subject, $plainContent, $attachedFiles, $customheaders = false) {
226 $subjectIsUnicode = false;
227 $plainEncoding = Mailer::get_encoding();
228
229 if ($customheaders && is_array($customheaders) == false) {
230 echo "htmlEmail($to, $from, $subject, ...) could not send mail: improper \$customheaders passed:<BR>";
231 dieprintr($headers);
232 }
233
234 $from = validEmailAddr($from);
235 $to = validEmailAddr($to);
236
237 if(strpos($subject,"&#") !== false) $subjectIsUnicode = true;
238
239
240 $subject = Convert::xml2raw($subject);
241 if($subjectIsUnicode)
242 $subject = "=?UTF-8?B?" . base64_encode($subject) . "?=";
243
244
245
246 $headers["Content-Type"] = "text/plain; charset=\"utf-8\"";
247 $headers["Content-Transfer-Encoding"] = $plainEncoding ? $plainEncoding : '8bit';
248
249 $plainContent = ($plainEncoding == "base64") ? chunk_split(base64_encode($plainContent),60) : wordwrap($plainContent, 120);
250
251
252 if(is_array($attachedFiles)) {
253
254 $fullMessage = processHeaders($headers, $plainContent);
255 $messageParts = array($fullMessage);
256
257
258 foreach($attachedFiles as $file) {
259 if(isset($file['tmp_name']) && isset($file['name'])) {
260 $messageParts[] = encodeFileForEmail($file['tmp_name'], $file['name']);
261 } else {
262 $messageParts[] = encodeFileForEmail($file);
263 }
264 }
265
266
267
268 list($fullBody, $headers) = encodeMultipart($messageParts, "multipart/mixed");
269
270
271 } else {
272 $fullBody = $plainContent;
273 }
274
275
276 $headers["From"] = $from;
277 $headers["Reply-To"] = $from;
278
279
280 if(isset($customheaders["X-SilverStripeMessageID"]) && defined('BOUNCE_EMAIL')) {
281 $bounceAddress = BOUNCE_EMAIL;
282
283 if(ereg('^([^<>]+)<([^<>])> *$', $from, $parts))
284 $bounceAddress = "$parts[1]<$bounceAddress>";
285 } else {
286 $bounceAddress = $from;
287 }
288
289
290 $headers["X-Mailer"] = X_MAILER;
291 if(!isset($customheaders["X-Priority"])) {
292 $headers["X-Priority"] = 3;
293 }
294
295 $headers = array_merge((array)$headers, (array)$customheaders);
296
297
298 if (isset($headers['CC'])) { $headers['Cc'] = $headers['CC']; unset($headers['CC']); }
299 if (isset($headers['cc'])) { $headers['Cc'] = $headers['cc']; unset($headers['cc']); }
300
301
302 $headers = processHeaders($headers);
303
304
305 if(!$result = @mail($to, $subject, $fullBody, $headers, "-f$bounceAddress")) {
306 $result = mail($to, $subject, $fullBody, Mailer::replace_sender($headers));
307 }
308
309 return $result;
310 }
311
312
313 function encodeMultipart($parts, $contentType, $headers = false) {
314 $separator = "----=_NextPart_" . ereg_replace('[^0-9]','',rand() * 10000000000);
315
316
317 $headers["MIME-Version"] = "1.0";
318 $headers["Content-Type"] = "$contentType; boundary=\"$separator\"";
319 $headers["Content-Transfer-Encoding"] = "8bit";
320
321 if($contentType == "multipart/alternative") {
322
323 $baseMessage = "\nThis is a multi-part message in MIME format.";
324 } else {
325
326 $baseMessage = "\nThis is a multi-part message in MIME format.";
327 }
328
329
330 $separator = "\n--$separator\n";
331 $body = "$baseMessage\n" .
332 $separator . implode("\n".$separator, $parts) . "\n" . trim($separator) . "--";
333
334 return array($body, $headers);
335 }
336
337 338 339 340
341 function wrapImagesInline($htmlContent) {
342 global $_INLINED_IMAGES;
343 $_INLINED_IMAGES = null;
344
345 $replacedContent = imageRewriter($htmlContent, 'wrapImagesInline_rewriter($URL)');
346
347
348
349 $headers["Content-Type"] = "text/html; charset=\"utf-8\"";
350 $headers["Content-Transfer-Encoding"] = "quoted-printable";
351 $multiparts[] = processHeaders($headers, QuotedPrintable_encode($replacedContent));
352
353
354 global $_INLINED_IMAGES;
355 foreach($_INLINED_IMAGES as $url => $cid) {
356 $multiparts[] = encodeFileForEmail($url, false, "inline", "Content-ID: <$cid>\n");
357 }
358
359
360 list($body, $headers) = encodeMultipart($multiparts, "multipart/related");
361 return processHeaders($headers, $body);
362 }
363 function wrapImagesInline_rewriter($url) {
364 $url = relativiseURL($url);
365
366 global $_INLINED_IMAGES;
367 if(!$_INLINED_IMAGES[$url]) {
368 $identifier = "automatedmessage." . rand(1000,1000000000) . "@silverstripe.com";
369 $_INLINED_IMAGES[$url] = $identifier;
370 }
371 return "cid:" . $_INLINED_IMAGES[$url];
372
373 }
374
375 376 377
378 function ($headers, $body = false) {
379 $res = '';
380 if(is_array($headers)) while(list($k, $v) = each($headers))
381 $res .= "$k: $v\n";
382 if($body) $res .= "\n$body";
383 return $res;
384 }
385
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
424 function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $extraHeaders = "") {
425 if(!$file) {
426 user_error("encodeFileForEmail: not passed a filename and/or data", E_USER_WARNING);
427 return;
428 }
429
430 if (is_string($file)) {
431 $file = array('filename' => $file);
432 $fh = fopen($file['filename'], "rb");
433 if ($fh) {
434 while(!feof($fh)) $file['contents'] .= fread($fh, 10000);
435 fclose($fh);
436 }
437 }
438
439
440 if(!$destFileName) $base = basename($file['filename']);
441 else $base = $destFileName;
442
443 $mimeType = $file['mimetype'] ? $file['mimetype'] : getMimeType($file['filename']);
444 if(!$mimeType) $mimeType = "application/unknown";
445
446 if (empty($disposition)) $disposition = isset($file['contentLocation']) ? 'inline' : 'attachment';
447
448
449 if (substr($file['mimetype'], 0, 4) != 'text') {
450 $encoding = "base64";
451 $file['contents'] = chunk_split(base64_encode($file['contents']));
452 } else {
453
454 $mimeType = 'application/octet-stream';
455 $encoding = "quoted-printable";
456 $file['contents'] = QuotedPrintable_encode($file['contents']);
457 }
458
459 $headers = "Content-type: $mimeType;\n\tname=\"$base\"\n".
460 "Content-Transfer-Encoding: $encoding\n".
461 "Content-Disposition: $disposition;\n\tfilename=\"$base\"\n" ;
462
463 if ( isset($file['contentLocation']) ) $headers .= 'Content-Location: ' . $file['contentLocation'] . "\n" ;
464
465 $headers .= $extraHeaders . "\n";
466
467
468 return $headers . $file['contents'];
469 }
470
471 function QuotedPrintable_encode($quotprint) {
472 $quotprint = (string) str_replace('\r\n',chr(13).chr(10),$quotprint);
473 $quotprint = (string) str_replace('\n', chr(13).chr(10),$quotprint);
474 $quotprint = (string) preg_replace("~([\x01-\x1F\x3D\x7F-\xFF])~e", "sprintf('=%02X', ord('\\1'))", $quotprint);
475
476 $quotprint = (string) str_replace('=0D=0A',"\n",$quotprint);
477 $quotprint = (string) str_replace('=0A=0D',"\n",$quotprint);
478 $quotprint = (string) str_replace('=0D',"\n",$quotprint);
479 $quotprint = (string) str_replace('=0A',"\n",$quotprint);
480 return (string) $quotprint;
481 }
482
483 function validEmailAddr($emailAddress) {
484 $emailAddress = trim($emailAddress);
485 $angBrack = strpos($emailAddress, '<');
486
487 if($angBrack === 0) {
488 $emailAddress = substr($emailAddress, 1, strpos($emailAddress,'>')-1);
489
490 } else if($angBrack) {
491 $emailAddress = str_replace('@', '', substr($emailAddress, 0, $angBrack))
492 .substr($emailAddress, $angBrack);
493 }
494
495 return $emailAddress;
496 }
497
498 499 500
501 function getMimeType($filename) {
502 global $global_mimetypes;
503 if(!$global_mimetypes) loadMimeTypes();
504 $ext = strtolower(substr($filename,strrpos($filename,'.')+1));
505 return isset($global_mimetypes[$ext]) ? $global_mimetypes[$ext] : 'application/octet-stream';
506 }
507
508 509 510
511 function loadMimeTypes() {
512 $mimetypePathCustom = '/etc/mime.types';
513 $mimetypePathGeneric = Director::baseFolder() . '/sapphire/email/mime.types';
514 $mimeTypes = file_exists($mimetypePathGeneric) ? file($mimetypePathGeneric) : file($mimetypePathCustom);
515 foreach($mimeTypes as $typeSpec) {
516 if(($typeSpec = trim($typeSpec)) && substr($typeSpec,0,1) != "#") {
517 $parts = preg_split("/[ \t\r\n]+/", $typeSpec);
518 if(sizeof($parts) > 1) {
519 $mimeType = array_shift($parts);
520 foreach($parts as $ext) {
521 $ext = strtolower($ext);
522 $mimeData[$ext] = $mimeType;
523 }
524 }
525 }
526 }
527
528 global $global_mimetypes;
529 $global_mimetypes = $mimeData;
530 return $mimeData;
531 }
532
533
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the
Silverstripe Forums to ask development related questions.
-