48 protected static function sendWithPear( $mailer, $dest, $headers, $body ) {
49 $mailResult = $mailer->send( $dest, $headers, $body );
52 if ( PEAR::isError( $mailResult ) ) {
53 wfDebug(
"PEAR::Mail failed: " . $mailResult->getMessage() );
54 return Status::newFatal(
'pear-mail-error', $mailResult->getMessage() );
56 return Status::newGood();
98 public static function send( $to, $from, $subject, $body, $options = [] ) {
99 $allowHTMLEmail = MediaWikiServices::getInstance()->getMainConfig()->get(
100 MainConfigNames::AllowHTMLEmail );
102 if ( !isset( $options[
'contentType'] ) ) {
103 $options[
'contentType'] =
'text/plain; charset=UTF-8';
106 if ( !is_array( $to ) ) {
117 !is_array( $body ) &&
118 strlen( $body ) >= $minBodyLen
123 isset( $body[
'text'] ) &&
124 isset( $body[
'html'] ) &&
125 strlen( $body[
'text'] ) >= $minBodyLen &&
126 strlen( $body[
'html'] ) >= $minBodyLen
130 return Status::newFatal(
'user-mail-no-body' );
133 if ( !$allowHTMLEmail && is_array( $body ) ) {
135 $body = $body[
'text'];
138 wfDebug( __METHOD__ .
': sending mail to ' . implode(
', ', $to ) );
141 $has_address =
false;
142 foreach ( $to as $u ) {
148 if ( !$has_address ) {
149 return Status::newFatal(
'user-mail-no-addy' );
154 if ( count( $to ) > 1 ) {
156 Hooks::runner()->onUserMailerSplitTo( $to );
157 if ( $oldTo != $to ) {
158 $splitTo = array_diff( $oldTo, $to );
159 $to = array_diff( $oldTo, $splitTo );
161 $status = Status::newGood();
163 $status->merge( self::sendInternal(
164 $to, $from, $subject, $body, $options ) );
166 foreach ( $splitTo as $newTo ) {
167 $status->merge( self::sendInternal(
168 [ $newTo ], $from, $subject, $body, $options ) );
174 return self::sendInternal( $to, $from, $subject, $body, $options );
229 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
230 $smtp = $mainConfig->get( MainConfigNames::SMTP );
231 $enotifMaxRecips = $mainConfig->get( MainConfigNames::EnotifMaxRecips );
232 $additionalMailParams = $mainConfig->get( MainConfigNames::AdditionalMailParams );
235 $replyto = $options[
'replyTo'] ??
null;
236 $contentType = $options[
'contentType'] ??
'text/plain; charset=UTF-8';
237 $headers = $options[
'headers'] ?? [];
242 if ( !Hooks::runner()->onUserMailerTransformContent( $to, $from, $body, $error ) ) {
244 return Status::newFatal(
'php-mail-error', $error );
246 return Status::newFatal(
'php-mail-error-unknown' );
279 $headers[
'From'] = $from->
toString();
280 $returnPath = $from->address;
281 $extraParams = $additionalMailParams;
284 Hooks::runner()->onUserMailerChangeReturnPath( $to, $returnPath );
294 $returnPathCLI =
'"' . str_replace(
'"',
'', $returnPath ) .
'"';
295 $extraParams .=
' -f ' . $returnPathCLI;
297 $headers[
'Return-Path'] = $returnPath;
300 $headers[
'Reply-To'] = $replyto->toString();
303 $headers[
'Date'] = MWTimestamp::getLocalInstance()->format(
'r' );
304 $headers[
'Message-ID'] = self::makeMsgId();
305 $headers[
'X-Mailer'] =
'MediaWiki mailer';
313 if ( is_array( $body ) ) {
315 wfDebug(
"Assembling multipart mime email" );
316 if ( !self::isMailMimeUsable() ) {
317 wfDebug(
"PEAR Mail_Mime package is not installed. Falling back to text email." );
319 $body = $body[
'text'];
323 $body[
'text'] = str_replace(
"\n",
"\r\n", $body[
'text'] );
324 $body[
'html'] = str_replace(
"\n",
"\r\n", $body[
'html'] );
326 $mime =
new Mail_mime( [
328 'text_charset' =>
'UTF-8',
329 'html_charset' =>
'UTF-8'
331 $mime->setTXTBody( $body[
'text'] );
332 $mime->setHTMLBody( $body[
'html'] );
333 $body =
$mime->get();
334 $headers =
$mime->headers( $headers );
337 if (
$mime ===
null ) {
340 $body = str_replace(
"\n",
"\r\n", $body );
342 $headers[
'MIME-Version'] =
'1.0';
343 $headers[
'Content-type'] = $contentType;
344 $headers[
'Content-transfer-encoding'] =
'8bit';
348 if ( !Hooks::runner()->onUserMailerTransformMessage(
349 $to, $from, $subject, $headers, $body, $error )
352 return Status::newFatal(
'php-mail-error', $error );
354 return Status::newFatal(
'php-mail-error-unknown' );
358 $ret = Hooks::runner()->onAlternateUserMailer( $headers, $to, $from, $subject, $body );
359 if ( $ret ===
false ) {
361 return Status::newGood();
362 } elseif ( $ret !==
true ) {
364 return Status::newFatal(
'php-mail-error', $ret );
367 if ( is_array( $smtp ) ) {
369 if ( !self::isMailUsable() ) {
370 throw new MWException(
'PEAR mail package is not installed' );
373 $recips = array_map(
'strval', $to );
375 AtEase::suppressWarnings();
378 $mail_object = Mail::factory(
'smtp', $smtp );
379 if ( PEAR::isError( $mail_object ) ) {
380 wfDebug(
"PEAR::Mail factory failed: " . $mail_object->getMessage() );
381 AtEase::restoreWarnings();
382 return Status::newFatal(
'pear-mail-error', $mail_object->getMessage() );
384 '@phan-var Mail_smtp $mail_object';
386 wfDebug(
"Sending mail via PEAR::Mail" );
388 $headers[
'Subject'] = self::quotedPrintable( $subject );
391 if ( count( $recips ) == 1 ) {
392 $headers[
'To'] = $recips[0];
397 $chunks = array_chunk( $recips, $enotifMaxRecips );
398 foreach ( $chunks as $chunk ) {
399 $status = self::sendWithPear( $mail_object, $chunk, $headers, $body );
401 if ( !$status->isOK() ) {
402 AtEase::restoreWarnings();
406 AtEase::restoreWarnings();
407 return Status::newGood();
410 if ( count( $to ) > 1 ) {
411 $headers[
'To'] =
'undisclosed-recipients:;';
414 wfDebug(
"Sending mail via internal mail() function" );
416 self::$mErrorString =
'';
417 $html_errors = ini_get(
'html_errors' );
418 ini_set(
'html_errors',
'0' );
419 set_error_handler(
'UserMailer::errorHandler' );
422 foreach ( $to as $recip ) {
425 self::quotedPrintable( $subject ),
431 }
catch ( Exception $e ) {
432 restore_error_handler();
436 restore_error_handler();
437 ini_set(
'html_errors', $html_errors );
439 if ( self::$mErrorString ) {
440 wfDebug(
"Error sending mail: " . self::$mErrorString );
441 return Status::newFatal(
'php-mail-error', self::$mErrorString );
442 } elseif ( !$sent ) {
445 wfDebug(
"Unknown error sending mail" );
446 return Status::newFatal(
'php-mail-error-unknown' );
448 return Status::newGood();