43 protected static function sendWithPear( $mailer, $dest, $headers, $body ) {
44 $mailResult = $mailer->send( $dest, $headers, $body );
47 if ( PEAR::isError( $mailResult ) ) {
48 wfDebug(
"PEAR::Mail failed: " . $mailResult->getMessage() );
49 return Status::newFatal(
'pear-mail-error', $mailResult->getMessage() );
51 return Status::newGood();
63 $domainId = WikiMap::getCurrentWikiDbDomain()->getId();
64 $msgid = uniqid( $domainId .
".",
true );
69 $domain = $url[
'host'];
71 return "<$msgid@$domain>";
93 public static function send( $to, $from, $subject, $body, $options = [] ) {
96 if ( !isset( $options[
'contentType'] ) ) {
97 $options[
'contentType'] =
'text/plain; charset=UTF-8';
100 if ( !is_array( $to ) ) {
111 !is_array( $body ) &&
112 strlen( $body ) >= $minBodyLen
117 isset( $body[
'text'] ) &&
118 isset( $body[
'html'] ) &&
119 strlen( $body[
'text'] ) >= $minBodyLen &&
120 strlen( $body[
'html'] ) >= $minBodyLen
124 return Status::newFatal(
'user-mail-no-body' );
129 $body = $body[
'text'];
132 wfDebug( __METHOD__ .
': sending mail to ' . implode(
', ', $to ) );
135 $has_address =
false;
136 foreach ( $to as $u ) {
142 if ( !$has_address ) {
143 return Status::newFatal(
'user-mail-no-addy' );
148 if ( count( $to ) > 1 ) {
150 Hooks::runner()->onUserMailerSplitTo( $to );
151 if ( $oldTo != $to ) {
152 $splitTo = array_diff( $oldTo, $to );
153 $to = array_diff( $oldTo, $splitTo );
155 $status = Status::newGood();
157 $status->merge( self::sendInternal(
158 $to, $from, $subject, $body, $options ) );
160 foreach ( $splitTo as $newTo ) {
161 $status->merge( self::sendInternal(
162 [ $newTo ], $from, $subject, $body, $options ) );
168 return self::sendInternal( $to, $from, $subject, $body, $options );
178 static $usable =
null;
179 if ( $usable ===
null ) {
180 $usable = class_exists(
'Mail_mime' );
192 static $usable =
null;
193 if ( $usable ===
null ) {
194 $usable = class_exists(
'Mail' );
226 $replyto = $options[
'replyTo'] ??
null;
227 $contentType = $options[
'contentType'] ??
'text/plain; charset=UTF-8';
228 $headers = $options[
'headers'] ?? [];
232 if ( !Hooks::runner()->onUserMailerTransformContent( $to, $from, $body, $error ) ) {
234 return Status::newFatal(
'php-mail-error', $error );
236 return Status::newFatal(
'php-mail-error-unknown' );
269 $headers[
'From'] = $from->
toString();
270 $returnPath = $from->address;
274 Hooks::runner()->onUserMailerChangeReturnPath( $to, $returnPath );
284 $returnPathCLI =
'"' . str_replace(
'"',
'', $returnPath ) .
'"';
285 $extraParams .=
' -f ' . $returnPathCLI;
287 $headers[
'Return-Path'] = $returnPath;
290 $headers[
'Reply-To'] = $replyto->toString();
293 $headers[
'Date'] = MWTimestamp::getLocalInstance()->format(
'r' );
294 $headers[
'Message-ID'] = self::makeMsgId();
295 $headers[
'X-Mailer'] =
'MediaWiki mailer';
303 if ( is_array( $body ) ) {
305 wfDebug(
"Assembling multipart mime email" );
306 if ( !self::isMailMimeUsable() ) {
307 wfDebug(
"PEAR Mail_Mime package is not installed. Falling back to text email." );
309 $body = $body[
'text'];
313 $body[
'text'] = str_replace(
"\n",
"\r\n", $body[
'text'] );
314 $body[
'html'] = str_replace(
"\n",
"\r\n", $body[
'html'] );
316 $mime =
new Mail_mime( [
318 'text_charset' =>
'UTF-8',
319 'html_charset' =>
'UTF-8'
321 $mime->setTXTBody( $body[
'text'] );
322 $mime->setHTMLBody( $body[
'html'] );
323 $body =
$mime->get();
324 $headers =
$mime->headers( $headers );
327 if (
$mime ===
null ) {
330 $body = str_replace(
"\n",
"\r\n", $body );
332 $headers[
'MIME-Version'] =
'1.0';
333 $headers[
'Content-type'] = $contentType;
334 $headers[
'Content-transfer-encoding'] =
'8bit';
338 if ( !Hooks::runner()->onUserMailerTransformMessage(
339 $to, $from, $subject, $headers, $body, $error )
342 return Status::newFatal(
'php-mail-error', $error );
344 return Status::newFatal(
'php-mail-error-unknown' );
348 $ret = Hooks::runner()->onAlternateUserMailer( $headers, $to, $from, $subject, $body );
349 if ( $ret ===
false ) {
351 return Status::newGood();
352 } elseif ( $ret !==
true ) {
355 return Status::newFatal(
'php-mail-error', $ret );
360 if ( !self::isMailUsable() ) {
361 throw new MWException(
'PEAR mail package is not installed' );
364 $recips = array_map(
'strval', $to );
366 Wikimedia\suppressWarnings();
369 $mail_object = Mail::factory(
'smtp',
$wgSMTP );
370 if ( PEAR::isError( $mail_object ) ) {
371 wfDebug(
"PEAR::Mail factory failed: " . $mail_object->getMessage() );
372 Wikimedia\restoreWarnings();
373 return Status::newFatal(
'pear-mail-error', $mail_object->getMessage() );
375 '@phan-var Mail_smtp $mail_object';
377 wfDebug(
"Sending mail via PEAR::Mail" );
379 $headers[
'Subject'] = self::quotedPrintable( $subject );
382 if ( count( $recips ) == 1 ) {
383 $headers[
'To'] = $recips[0];
389 foreach ( $chunks as $chunk ) {
390 $status = self::sendWithPear( $mail_object, $chunk, $headers, $body );
392 if ( !$status->isOK() ) {
393 Wikimedia\restoreWarnings();
397 Wikimedia\restoreWarnings();
398 return Status::newGood();
401 if ( count( $to ) > 1 ) {
402 $headers[
'To'] =
'undisclosed-recipients:;';
405 wfDebug(
"Sending mail via internal mail() function" );
407 self::$mErrorString =
'';
408 $html_errors = ini_get(
'html_errors' );
409 ini_set(
'html_errors',
'0' );
410 set_error_handler(
'UserMailer::errorHandler' );
413 foreach ( $to as $recip ) {
416 self::quotedPrintable( $subject ),
422 }
catch ( Exception $e ) {
423 restore_error_handler();
427 restore_error_handler();
428 ini_set(
'html_errors', $html_errors );
430 if ( self::$mErrorString ) {
431 wfDebug(
"Error sending mail: " . self::$mErrorString );
432 return Status::newFatal(
'php-mail-error', self::$mErrorString );
433 } elseif ( !$sent ) {
435 wfDebug(
"Unknown error sending mail" );
436 return Status::newFatal(
'php-mail-error-unknown' );
438 return Status::newGood();
450 self::$mErrorString = preg_replace(
'/^mail\(\)(\s*\[.*?\])?: /',
'', $string );
459 return strtr( $val, [
"\r" =>
'',
"\n" =>
'' ] );
469 $phrase = self::sanitizeHeaderValue( $phrase );
471 $phrase = str_replace(
'"',
'', $phrase );
472 return '"' . $phrase .
'"';
490 if ( empty( $charset ) ) {
493 $charset = strtoupper( $charset );
494 $charset = str_replace(
'ISO-8859',
'ISO8859', $charset );
496 $illegal =
'\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff=';
497 $replace = $illegal .
'\t ?_';
498 if ( !preg_match(
"/[$illegal]/", $string ) ) {
501 $out =
"=?$charset?Q?";
502 $out .= preg_replace_callback(
"/([$replace])/",
504 return sprintf(
"=%02X", ord(
$matches[1] ) );
$wgEnotifMaxRecips
Maximum number of users to mail at once when using impersonal mail.
$wgAdditionalMailParams
Additional email parameters, will be passed as the last argument to mail() call.
$wgAllowHTMLEmail
For parts of the system that have been updated to provide HTML email content, send both text and HTML...
$wgServer
URL of the server.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
wfIsWindows()
Check if the operating system is Windows.
Stores a single person's name and email address.
toString()
Return formatted and quoted address to insert into SMTP headers.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Collection of static functions for sending mail.
static errorHandler( $code, $string)
Set the mail error message in self::$mErrorString.
static isMailUsable()
Whether the PEAR Mail library is usable.
static rfc822Phrase( $phrase)
Converts a string into a valid RFC 822 "phrase", such as is used for the sender name.
static sanitizeHeaderValue( $val)
Strips bad characters from a header value to prevent PHP mail header injection attacks.
static send( $to, $from, $subject, $body, $options=[])
This function will perform a direct (authenticated) login to a SMTP Server to use for mail relaying i...
static sendWithPear( $mailer, $dest, $headers, $body)
Send mail using a PEAR mailer.
static quotedPrintable( $string, $charset='')
Converts a string into quoted-printable format.
static isMailMimeUsable()
Whether the PEAR Mail_mime library is usable.
static sendInternal(array $to, MailAddress $from, $subject, $body, $options=[])
Helper function fo UserMailer::send() which does the actual sending.
static makeMsgId()
Create a value suitable for the MessageId Header.