36 private static $mErrorString;
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() );
65 private static function makeMsgId() {
66 $smtp = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::SMTP );
67 $server = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::Server );
68 $domainId = WikiMap::getCurrentWikiDbDomain()->getId();
69 $msgid = uniqid( $domainId .
".",
true );
70 if ( is_array( $smtp ) && isset( $smtp[
'IDHost'] ) && $smtp[
'IDHost'] ) {
71 $domain = $smtp[
'IDHost'];
74 $domain = $url[
'host'];
76 return "<$msgid@$domain>";
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
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 ) {
154 if ( count( $to ) > 1 ) {
157 if ( $oldTo != $to ) {
158 $splitTo = array_diff( $oldTo, $to );
159 $to = array_diff( $oldTo, $splitTo );
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 ) );
200 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
201 $smtp = $mainConfig->get( MainConfigNames::SMTP );
202 $enotifMaxRecips = $mainConfig->get( MainConfigNames::EnotifMaxRecips );
203 $additionalMailParams = $mainConfig->get( MainConfigNames::AdditionalMailParams );
205 $replyto = $options[
'replyTo'] ??
null;
206 $contentType = $options[
'contentType'] ??
'text/plain; charset=UTF-8';
207 $headers = $options[
'headers'] ?? [];
212 if ( !
Hooks::runner()->onUserMailerTransformContent( $to, $from, $body, $error ) ) {
249 $headers[
'From'] = $from->
toString();
250 $returnPath = $from->address;
251 $extraParams = $additionalMailParams;
254 Hooks::runner()->onUserMailerChangeReturnPath( $to, $returnPath );
264 $returnPathCLI =
'"' . str_replace(
'"',
'', $returnPath ) .
'"';
265 $extraParams .=
' -f ' . $returnPathCLI;
267 $headers[
'Return-Path'] = $returnPath;
270 $headers[
'Reply-To'] = $replyto->toString();
274 $headers[
'Message-ID'] = self::makeMsgId();
275 $headers[
'X-Mailer'] =
'MediaWiki mailer';
283 if ( is_array( $body ) ) {
285 wfDebug(
"Assembling multipart mime email" );
287 $body[
'text'] = str_replace(
"\n",
"\r\n", $body[
'text'] );
288 $body[
'html'] = str_replace(
"\n",
"\r\n", $body[
'html'] );
290 $mime =
new Mail_mime( [
292 'text_charset' =>
'UTF-8',
293 'html_charset' =>
'UTF-8'
295 $mime->setTXTBody( $body[
'text'] );
296 $mime->setHTMLBody( $body[
'html'] );
297 $body =
$mime->get();
298 $headers =
$mime->headers( $headers );
302 $body = str_replace(
"\n",
"\r\n", $body );
304 $headers[
'MIME-Version'] =
'1.0';
305 $headers[
'Content-type'] = $contentType;
306 $headers[
'Content-transfer-encoding'] =
'8bit';
311 $to, $from, $subject, $headers, $body, $error )
320 $ret =
Hooks::runner()->onAlternateUserMailer( $headers, $to, $from, $subject, $body );
321 if ( $ret ===
false ) {
324 } elseif ( $ret !==
true ) {
329 if ( is_array( $smtp ) ) {
330 $recips = array_map(
'strval', $to );
333 $mail_object = Mail::factory(
'smtp', $smtp );
334 if ( PEAR::isError( $mail_object ) ) {
335 wfDebug(
"PEAR::Mail factory failed: " . $mail_object->getMessage() );
338 '@phan-var Mail_smtp $mail_object';
340 wfDebug(
"Sending mail via PEAR::Mail" );
345 if ( count( $recips ) == 1 ) {
346 $headers[
'To'] = $recips[0];
351 $chunks = array_chunk( $recips, $enotifMaxRecips );
352 foreach ( $chunks as $chunk ) {
355 if ( !$status->isOK() ) {
362 if ( count( $to ) > 1 ) {
363 $headers[
'To'] =
'undisclosed-recipients:;';
366 wfDebug(
"Sending mail via internal mail() function" );
368 self::$mErrorString =
'';
369 $html_errors = ini_get(
'html_errors' );
370 ini_set(
'html_errors',
'0' );
371 set_error_handler(
'UserMailer::errorHandler' );
374 foreach ( $to as $recip ) {
377 self::quotedPrintable( $subject ),
383 }
catch ( Exception $e ) {
384 restore_error_handler();
388 restore_error_handler();
389 ini_set(
'html_errors', $html_errors );
391 if ( self::$mErrorString ) {
392 wfDebug(
"Error sending mail: " . self::$mErrorString );
394 } elseif ( !$sent ) {
397 wfDebug(
"Unknown error sending mail" );
411 private static function errorHandler( $code, $string ) {
412 self::$mErrorString = preg_replace(
'/^mail\(\)(\s*\[.*?\])?: /',
'', $string );
421 return strtr( $val, [
"\r" =>
'',
"\n" =>
'' ] );
433 $phrase = str_replace(
'"',
'', $phrase );
434 return '"' . $phrase .
'"';
452 if ( empty( $charset ) ) {
455 $charset = strtoupper( $charset );
456 $charset = str_replace(
'ISO-8859',
'ISO8859', $charset );
458 $illegal =
'\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff=';
459 $replace = $illegal .
'\t ?_';
460 if ( !preg_match(
"/[$illegal]/", $string ) ) {
463 $out =
"=?$charset?Q?";
464 $out .= preg_replace_callback(
"/([$replace])/",
466 return sprintf(
"=%02X", ord(
$matches[1] ) );
wfIsWindows()
Check if the operating system is Windows.
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.
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
static getLocalInstance( $ts=false)
Get a timestamp instance in the server local timezone ($wgLocaltimezone)
Stores a single person's name and email address.
toString()
Return formatted and quoted address to insert into SMTP headers.
A class containing constants representing the names of configuration variables.
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,...
static newFatal( $message,... $parameters)
Factory function for fatal errors.
static newGood( $value=null)
Factory function for good results.
Collection of static functions for sending mail.
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 sendInternal(array $to, MailAddress $from, $subject, $body, $options=[])
Helper function fo UserMailer::send() which does the actual sending.