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