re: r93415
[lhc/web/wiklou.git] / includes / UserMailer.php
index 14c7d1e..164b8ba 100644 (file)
@@ -31,7 +31,7 @@
  */
 class MailAddress {
        /**
-        * @param $address Mixed: string with an email address, or a User object
+        * @param $address string|User string with an email address, or a User object
         * @param $name String: human-readable name if a string address is given
         * @param $realName String: human-readable real name if a string address is given
         */
@@ -55,16 +55,20 @@ class MailAddress {
                # PHP's mail() implementation under Windows is somewhat shite, and
                # can't handle "Joe Bloggs <joe@bloggs.com>" format email addresses,
                # so don't bother generating them
-               if ( $this->name != '' && !wfIsWindows() ) {
-                       global $wgEnotifUseRealName;
-                       $name = ( $wgEnotifUseRealName && $this->realName ) ? $this->realName : $this->name;
-                       $quoted = UserMailer::quotedPrintable( $name );
-                       if ( strpos( $quoted, '.' ) !== false || strpos( $quoted, ',' ) !== false ) {
-                               $quoted = '"' . $quoted . '"';
+               if ( $this->address ) {
+                       if ( $this->name != '' && !wfIsWindows() ) {
+                               global $wgEnotifUseRealName;
+                               $name = ( $wgEnotifUseRealName && $this->realName ) ? $this->realName : $this->name;
+                               $quoted = UserMailer::quotedPrintable( $name );
+                               if ( strpos( $quoted, '.' ) !== false || strpos( $quoted, ',' ) !== false ) {
+                                       $quoted = '"' . $quoted . '"';
+                               }
+                               return "$quoted <{$this->address}>";
+                       } else {
+                               return $this->address;
                        }
-                       return "$quoted <{$this->address}>";
                } else {
-                       return $this->address;
+                       return "";
                }
        }
 
@@ -82,6 +86,13 @@ class UserMailer {
 
        /**
         * Send mail using a PEAR mailer
+        *
+        * @param $mailer
+        * @param $dest
+        * @param $headers
+        * @param $body
+        *
+        * @return Status
         */
        protected static function sendWithPear( $mailer, $dest, $headers, $body ) {
                $mailResult = $mailer->send( $dest, $headers, $body );
@@ -95,6 +106,33 @@ class UserMailer {
                }
        }
 
+       /**
+        * Creates a single string from an associative array
+        *
+        * @param $header Associative Array: keys are header field names,
+        *                values are ... values.
+        * @param $endl String: The end of line character.  Defaults to "\n"
+        * @return String
+        */
+       static function arrayToHeaderString( $headers, $endl = "\n" ) {
+               foreach( $headers as $name => $value ) {
+                       $string[] = "$name: $value";
+               }
+               return implode( $endl, $string );
+       }
+
+       /**
+        * Create a value suitable for the MessageId Header
+        *
+        * @return String
+        */
+       static function makeMsgId() {
+               global $wgServer;
+
+               $msgid = uniqid( "UserMailer", true ); /* true required for cygwin */
+               return "<$msgid@$wgServer>";
+       }
+
        /**
         * This function will perform a direct (authenticated) login to
         * a SMTP Server to use for mail relaying if 'wgSMTP' specifies an
@@ -106,28 +144,62 @@ class UserMailer {
         * @param $subject String: email's subject.
         * @param $body String: email's text.
         * @param $replyto MailAddress: optional reply-to email (default: null).
-        * @param $contentType String: optional custom Content-Type
+        * @param $contentType String: optional custom Content-Type (default: text/plain; charset=UTF-8)
         * @return Status object
         */
-       public static function send( $to, $from, $subject, $body, $replyto = null, $contentType = null ) {
-               global $wgSMTP, $wgOutputEncoding, $wgEnotifImpersonal;
+       public static function send( $to, $from, $subject, $body, $replyto = null, $contentType = 'text/plain; charset=UTF-8' ) {
+               global $wgSMTP, $wgEnotifImpersonal;
                global $wgEnotifMaxRecips, $wgAdditionalMailParams;
 
+               $emails = '';
+               wfDebug( __METHOD__ . ': sending mail to ' . implode( ',', array( $to->toString() ) ) . "\n" );
+
+               $headers['From'] = $from->toString();
+               $headers['Return-Path'] = $from->toString();
+
+               $dest = array();
                if ( is_array( $to ) ) {
-                       $emails = '';
-                       // This wouldn't be necessary if implode() worked on arrays of
-                       // objects using __toString(). http://bugs.php.net/bug.php?id=36612
-                       foreach ( $to as $t ) {
-                               $emails .= $t->toString() . ",";
+                       foreach ( $to as $u ) {
+                               if ( $u->address ) {
+                                       $dest[] = $u->address;
+                               }
                        }
-                       $emails = rtrim( $emails, ',' );
-                       wfDebug( __METHOD__ . ': sending mail to ' . $emails . "\n" );
-               } else {
-                       wfDebug( __METHOD__ . ': sending mail to ' . implode( ',', array( $to->toString() ) ) . "\n" );
+               } else if( $to->address ) {
+                       $dest[] = $to->address;
+               }
+               if ( count( $dest ) == 0 ) {
+                       return Status::newFatal( 'user-mail-no-addy' );
+               }
+
+               if ( $wgEnotifImpersonal ) {
+                       $headers['To'] = 'undisclosed-recipients:;';
+               }
+               else {
+                       $headers['To'] = implode( ", ", $dest );
+               }
+
+               if ( $replyto ) {
+                       $headers['Reply-To'] = $replyto->toString();
+               }
+               $headers['Subject'] = self::quotedPrintable( $subject );
+               $headers['Date'] = date( 'r' );
+               $headers['MIME-Version'] = '1.0';
+               $headers['Content-type'] = ( is_null( $contentType ) ?
+                       'text/plain; charset=UTF-8' : $contentType );
+               $headers['Content-transfer-encoding'] = '8bit';
+
+               $headers['Message-ID'] = self::makeMsgId();
+               $headers['X-Mailer'] = 'MediaWiki mailer';
+               $headers['From'] = $from->toString();
+
+               $ret = wfRunHooks( 'AlternateUserMailer', array( $headers, $to, $from, $subject, $body ) );
+               if ( $ret === false ) {
+                       return Status::newGood();
+               } else if ( $ret !== true ) {
+                       return Status::newFatal( 'php-mail-error', $ret );
                }
 
                if ( is_array( $wgSMTP ) ) {
-                       $found = false;
                        if ( function_exists( 'stream_resolve_include_path' ) ) {
                                $found = stream_resolve_include_path( 'Mail.php' );
                        } else {
@@ -138,38 +210,6 @@ class UserMailer {
                        }
                        require_once( 'Mail.php' );
 
-                       $msgid = str_replace( " ", "_", microtime() );
-                       if ( function_exists( 'posix_getpid' ) )
-                               $msgid .= '.' . posix_getpid();
-
-                       if ( is_array( $to ) ) {
-                               $dest = array();
-                               foreach ( $to as $u )
-                                       $dest[] = $u->address;
-                       } else
-                               $dest = $to->address;
-
-                       $headers['From'] = $from->toString();
-
-                       if ( $wgEnotifImpersonal ) {
-                               $headers['To'] = 'undisclosed-recipients:;';
-                       }
-                       else {
-                               $headers['To'] = implode( ", ", (array )$dest );
-                       }
-
-                       if ( $replyto ) {
-                               $headers['Reply-To'] = $replyto->toString();
-                       }
-                       $headers['Subject'] = self::quotedPrintable( $subject );
-                       $headers['Date'] = date( 'r' );
-                       $headers['MIME-Version'] = '1.0';
-                       $headers['Content-type'] = ( is_null( $contentType ) ?
-                                       'text/plain; charset=' . $wgOutputEncoding : $contentType );
-                       $headers['Content-transfer-encoding'] = '8bit';
-                       $headers['Message-ID'] = "<$msgid@" . $wgSMTP['IDHost'] . '>'; // FIXME
-                       $headers['X-Mailer'] = 'MediaWiki mailer';
-
                        wfSuppressWarnings();
 
                        // Create the mail object using the Mail::factory method
@@ -180,8 +220,8 @@ class UserMailer {
                                return Status::newFatal( 'pear-mail-error', $mail_object->getMessage() );
                        }
 
-                       wfDebug( "Sending mail via PEAR::Mail to $dest\n" );
-                       $chunks = array_chunk( (array)$dest, $wgEnotifMaxRecips );
+                       wfDebug( "Sending mail via PEAR::Mail\n" );
+                       $chunks = array_chunk( $dest, $wgEnotifMaxRecips );
                        foreach ( $chunks as $chunk ) {
                                $status = self::sendWithPear( $mail_object, $chunk, $headers, $body );
                                if ( !$status->isOK() ) {
@@ -192,9 +232,6 @@ class UserMailer {
                        wfRestoreWarnings();
                        return Status::newGood();
                } else  {
-                       # In the following $headers = expression we removed "Reply-To: {$from}\r\n" , because it is treated differently
-                       # (fifth parameter of the PHP mail function, see some lines below)
-
                        # Line endings need to be different on Unix and Windows due to
                        # the bug described at http://trac.wordpress.org/ticket/2603
                        if ( wfIsWindows() ) {
@@ -203,38 +240,18 @@ class UserMailer {
                        } else {
                                $endl = "\n";
                        }
-                       $ctype = ( is_null( $contentType ) ?
-                                       'text/plain; charset=' . $wgOutputEncoding : $contentType );
-                       $headers =
-                               "MIME-Version: 1.0$endl" .
-                               "Content-type: $ctype$endl" .
-                               "Content-Transfer-Encoding: 8bit$endl" .
-                               "X-Mailer: MediaWiki mailer$endl" .
-                               'From: ' . $from->toString();
-                       if ( $replyto ) {
-                               $headers .= "{$endl}Reply-To: " . $replyto->toString();
-                       }
+
+                       $headers = self::arrayToHeaderString( $headers, $endl );
 
                        wfDebug( "Sending mail via internal mail() function\n" );
 
                        self::$mErrorString = '';
                        $html_errors = ini_get( 'html_errors' );
                        ini_set( 'html_errors', '0' );
-                       set_error_handler( array( 'UserMailer', 'errorHandler' ) );
+                       set_error_handler( 'UserMailer::errorHandler' );
 
-                       // We need to check for safe_mode, because mail() throws an E_NOTICE
-                       // on the 5th parameter when it's turned on
-                       $sm = wfIniGetBool( 'safe_mode' );
-
-                       if ( !is_array( $to ) ) {
-                               $to = array( $to );
-                       }
-                       foreach ( $to as $recip ) {
-                               if( $sm ) {
-                                       $sent = mail( $recip->toString(), self::quotedPrintable( $subject ), $body, $headers );
-                               } else {
-                                       $sent = mail( $recip->toString(), self::quotedPrintable( $subject ), $body, $headers, $wgAdditionalMailParams );
-                               }
+                       foreach ( $dest as $recip ) {
+                               $sent = mail( $recip, self::quotedPrintable( $subject ), $body, $headers, $wgAdditionalMailParams );
                        }
 
                        restore_error_handler();
@@ -245,7 +262,7 @@ class UserMailer {
                                return Status::newFatal( 'php-mail-error', self::$mErrorString );
                        } elseif ( ! $sent ) {
                                // mail function only tells if there's an error
-                               wfDebug( "Error sending mail\n" );
+                               wfDebug( "Unknown error sending mail\n" );
                                return Status::newFatal( 'php-mail-error-unknown' );
                        } else {
                                return Status::newGood();
@@ -265,6 +282,8 @@ class UserMailer {
 
        /**
         * Converts a string into a valid RFC 822 "phrase", such as is used for the sender name
+        * @param $phrase string
+        * @return string
         */
        public static function rfc822Phrase( $phrase ) {
                $phrase = strtr( $phrase, array( "\r" => '', "\n" => '', '"' => '' ) );
@@ -278,8 +297,7 @@ class UserMailer {
        public static function quotedPrintable( $string, $charset = '' ) {
                # Probably incomplete; see RFC 2045
                if( empty( $charset ) ) {
-                       global $wgInputEncoding;
-                       $charset = $wgInputEncoding;
+                       $charset = 'UTF-8';
                }
                $charset = strtoupper( $charset );
                $charset = str_replace( 'ISO-8859', 'ISO8859', $charset ); // ?
@@ -290,7 +308,7 @@ class UserMailer {
                        return $string;
                }
                $out = "=?$charset?Q?";
-               $out .= preg_replace_callback( "/([$replace])/", 
+               $out .= preg_replace_callback( "/([$replace])/",
                        array( __CLASS__, 'quotedPrintableCallback' ), $string );
                $out .= '?=';
                return $out;
@@ -342,8 +360,9 @@ class EmailNotification {
        public function notifyOnPageChange( $editor, $title, $timestamp, $summary, $minorEdit, $oldid = false ) {
                global $wgEnotifUseJobQ, $wgEnotifWatchlist, $wgShowUpdatedMarker;
 
-               if ( $title->getNamespace() < 0 )
+               if ( $title->getNamespace() < 0 ) {
                        return;
+               }
 
                // Build a list of users to notfiy
                $watchers = array();
@@ -380,13 +399,14 @@ class EmailNotification {
 
                if ( $wgEnotifUseJobQ ) {
                        $params = array(
-                               "editor" => $editor->getName(),
-                               "editorID" => $editor->getID(),
-                               "timestamp" => $timestamp,
-                               "summary" => $summary,
-                               "minorEdit" => $minorEdit,
-                               "oldid" => $oldid,
-                               "watchers" => $watchers );
+                               'editor' => $editor->getName(),
+                               'editorID' => $editor->getID(),
+                               'timestamp' => $timestamp,
+                               'summary' => $summary,
+                               'minorEdit' => $minorEdit,
+                               'oldid' => $oldid,
+                               'watchers' => $watchers
+                       );
                        $job = new EnotifNotifyJob( $title, $params );
                        $job->insert();
                } else {
@@ -395,7 +415,7 @@ class EmailNotification {
 
        }
 
-       /*
+       /**
         * Immediate version of notifyOnPageChange().
         *
         * Send emails corresponding to the user $editor editing the page $title.
@@ -439,7 +459,9 @@ class EmailNotification {
                                        wfDebug( __METHOD__ . ": user talk page edited, but user does not exist\n" );
                                } elseif ( $targetUser->getId() == $editor->getId() ) {
                                        wfDebug( __METHOD__ . ": user edited their own talk page, no notification sent\n" );
-                               } elseif ( $targetUser->getOption( 'enotifusertalkpages' ) ) {
+                               } elseif ( $targetUser->getOption( 'enotifusertalkpages' ) &&
+                                       ( !$minorEdit || $targetUser->getOption( 'enotifminoredits' ) ) )
+                               {
                                        if ( $targetUser->isEmailConfirmed() ) {
                                                wfDebug( __METHOD__ . ": sending talk page update notification\n" );
                                                $this->compose( $targetUser );
@@ -513,13 +535,13 @@ class EmailNotification {
                }
 
                if ( $wgEnotifImpersonal && $this->oldid ) {
-                       /*
+                       /**
                         * For impersonal mail, show a diff link to the last
                         * revision.
                         */
                        $keys['$NEWPAGE'] = wfMsgForContent( 'enotif_lastdiff',
                                        $this->title->getFullURL( "oldid={$this->oldid}&diff=next" ) );
-        }
+               }
 
                $body = strtr( $body, $keys );
                $pagetitle = $this->title->getPrefixedText();
@@ -540,8 +562,8 @@ class EmailNotification {
                $adminAddress = new MailAddress( $wgPasswordSender, $wgPasswordSenderName );
                $editorAddress = new MailAddress( $editor );
                if ( $wgEnotifRevealEditorAddress
-                   && ( $editor->getEmail() != '' )
-                   && $editor->getOption( 'enotifrevealaddr' ) ) {
+                       && ( $editor->getEmail() != '' )
+                       && $editor->getOption( 'enotifrevealaddr' ) ) {
                        if ( $wgEnotifFromEditor ) {
                                $from    = $editorAddress;
                        } else {
@@ -553,7 +575,7 @@ class EmailNotification {
                        $replyto = new MailAddress( $wgNoReplyAddress );
                }
 
-               if ( $editor->isIP( $name ) ) {
+               if ( $editor->isAnon() ) {
                        # real anon (user:xxx.xxx.xxx.xxx)
                        $utext = wfMsgForContent( 'enotif_anon_editor', $name );
                        $subject = str_replace( '$PAGEEDITOR', $utext, $subject );
@@ -653,32 +675,14 @@ class EmailNotification {
 
                $body = str_replace(
                                array( '$WATCHINGUSERNAME',
-                                       '$PAGEEDITDATE' ),
+                                       '$PAGEEDITDATE',
+                                       '$PAGEEDITTIME' ),
                                array( wfMsgForContent( 'enotif_impersonal_salutation' ),
-                                       $wgContLang->timeanddate( $this->timestamp, true, false, false ) ),
+                                       $wgContLang->date( $this->timestamp, true, false, false ),
+                                       $wgContLang->time( $this->timestamp, true, false, false ) ),
                                $this->body );
 
                return UserMailer::send( $addresses, $this->from, $this->subject, $body, $this->replyto );
        }
 
 } # end of class EmailNotification
-
-/**@{
- * Backwards compatibility functions
- *
- * @deprecated Use UserMailer method deprecated in 1.18, remove in 1.19.
- */
-function wfRFC822Phrase( $s ) {
-       wfDeprecated( __FUNCTION__ );
-       return UserMailer::rfc822Phrase( $s );
-}
-
-/**
- * @deprecated Use UserMailer method deprecated in 1.18, remove in 1.19.
- */
-function userMailer( $to, $from, $subject, $body, $replyto = null ) {
-       wfDeprecated( __FUNCTION__ );
-       return UserMailer::send( $to, $from, $subject, $body, $replyto );
-}
-
-/**@}*/