testPngNativetZtxt requires zlib extension
[lhc/web/wiklou.git] / includes / UserMailer.php
index b8b6aa8..324e7ce 100644 (file)
@@ -21,6 +21,7 @@
  * @author <brion@pobox.com>
  * @author <mail@tgries.de>
  * @author Tim Starling
+ * @author Luke Welling lwelling@wikimedia.org
  */
 
 
@@ -112,6 +113,10 @@ class UserMailer {
         * @param $headers array Associative Array: keys are header field names,
         *                 values are ... values.
         * @param $endl String: The end of line character.  Defaults to "\n"
+        *
+        * Note RFC2822 says newlines must be CRLF (\r\n)
+        * but php mail naively "corrects" it and requires \n for the "correction" to work
+        *
         * @return String
         */
        static function arrayToHeaderString( $headers, $endl = "\n" ) {
@@ -149,19 +154,47 @@ class UserMailer {
         * @param $to MailAddress: recipient's email (or an array of them)
         * @param $from MailAddress: sender's email
         * @param $subject String: email's subject.
-        * @param $body String: email's text.
+        * @param $body String: email's text or Array of two strings to be the text and html bodies
         * @param $replyto MailAddress: optional reply-to email (default: null).
         * @param $contentType String: optional custom Content-Type (default: text/plain; charset=UTF-8)
         * @throws MWException
         * @return Status object
         */
        public static function send( $to, $from, $subject, $body, $replyto = null, $contentType = 'text/plain; charset=UTF-8' ) {
-               global $wgSMTP, $wgEnotifMaxRecips, $wgAdditionalMailParams;
-
+               global $wgSMTP, $wgEnotifMaxRecips, $wgAdditionalMailParams, $wgAllowHTMLEmail;
+               $mime = null;
                if ( !is_array( $to ) ) {
                        $to = array( $to );
                }
 
+               // mail body must have some content
+               $minBodyLen = 10;
+               // arbitrary but longer than Array or Object to detect casting error
+
+               // body must either be a string or an array with text and body
+               if (
+                       !(
+                               !is_array( $body ) &&
+                               strlen( $body ) >= $minBodyLen
+                       )
+                       &&
+                       !(
+                               is_array( $body ) &&
+                               isset( $body['text'] ) &&
+                               isset( $body['html'] ) &&
+                               strlen( $body['text'] ) >= $minBodyLen &&
+                               strlen( $body['html'] ) >= $minBodyLen
+                       )
+               ) {
+                       // if it is neither we have a problem
+                       return Status::newFatal( 'user-mail-no-body' );
+               }
+
+               if ( !$wgAllowHTMLEmail && is_array( $body ) ) {
+                       // HTML not wanted.  Dump it.
+                       $body = $body['text'];
+               }
+
                wfDebug( __METHOD__ . ': sending mail to ' . implode( ', ', $to ) . "\n" );
 
                # Make sure we have at least one address
@@ -211,18 +244,53 @@ class UserMailer {
                }
 
                $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';
 
+               # Line endings need to be different on Unix and Windows due to
+               # the bug described at http://trac.wordpress.org/ticket/2603
+               if ( wfIsWindows() ) {
+                       $endl = "\r\n";
+               } else {
+                       $endl = "\n";
+               }
+
+               if ( is_array( $body ) ) {
+                       // we are sending a multipart message
+                       wfDebug( "Assembling mulitpart mime email\n" );
+                       if ( !stream_resolve_include_path( 'Mail/mime.php' ) ) {
+                               wfDebug( "PEAR Mail_Mime package is not installed. Falling back to text email.\n" );
+                       }
+                       else {
+                               require_once( 'Mail/mime.php' );
+                               if ( wfIsWindows() ) {
+                                       $body['text'] = str_replace( "\n", "\r\n", $body['text'] );
+                                       $body['html'] = str_replace( "\n", "\r\n", $body['html'] );
+                               }
+                               $mime = new Mail_mime( array( 'eol' => $endl ) );
+                               $mime->setTXTBody( $body['text'] );
+                               $mime->setHTMLBody( $body['html'] );
+                               $body = $mime->get();  // must call get() before headers()
+                               $headers = $mime->headers( $headers );
+                       }
+               }
+               if ( !isset( $mime ) ) {
+                       // sending text only, either deliberately or as a fallback
+                       if ( wfIsWindows() ) {
+                               $body = str_replace( "\n", "\r\n", $body );
+                       }
+                       $headers['MIME-Version'] = '1.0';
+                       $headers['Content-type'] = ( is_null( $contentType ) ?
+                               'text/plain; charset=UTF-8' : $contentType );
+                       $headers['Content-transfer-encoding'] = '8bit';
+               }
+
                $ret = wfRunHooks( 'AlternateUserMailer', array( $headers, $to, $from, $subject, $body ) );
                if ( $ret === false ) {
+                       // the hook implementation will return false to skip regular mail sending
                        return Status::newGood();
                } elseif ( $ret !== true ) {
+                       // the hook implementation will return a string to pass an error message
                        return Status::newFatal( 'php-mail-error', $ret );
                }
 
@@ -231,7 +299,7 @@ class UserMailer {
                        # PEAR MAILER
                        #
 
-                       if ( ! stream_resolve_include_path( 'Mail.php' ) ) {
+                       if ( !stream_resolve_include_path( 'Mail.php' ) ) {
                                throw new MWException( 'PEAR mail package is not installed' );
                        }
                        require_once( 'Mail.php' );
@@ -272,16 +340,6 @@ class UserMailer {
                        #
                        # PHP mail()
                        #
-
-                       # Line endings need to be different on Unix and Windows due to
-                       # the bug described at http://trac.wordpress.org/ticket/2603
-                       if ( wfIsWindows() ) {
-                               $body = str_replace( "\n", "\r\n", $body );
-                               $endl = "\r\n";
-                       } else {
-                               $endl = "\n";
-                       }
-
                        if( count($to) > 1 ) {
                                $headers['To'] = 'undisclosed-recipients:;';
                        }
@@ -295,6 +353,7 @@ class UserMailer {
                        set_error_handler( 'UserMailer::errorHandler' );
 
                        $safeMode = wfIniGetBool( 'safe_mode' );
+
                        foreach ( $to as $recip ) {
                                if ( $safeMode ) {
                                        $sent = mail( $recip, self::quotedPrintable( $subject ), $body, $headers );
@@ -342,6 +401,12 @@ class UserMailer {
        /**
         * Converts a string into quoted-printable format
         * @since 1.17
+        *
+        * From PHP5.3 there is a built in function quoted_printable_encode()
+        * This method does not duplicate that.
+        * This method is doing Q encoding inside encoded-words as defined by RFC 2047
+        * This is for email headers.
+        * The built in quoted_printable_encode() is for email bodies
         * @return string
         */
        public static function quotedPrintable( $string, $charset = '' ) {
@@ -650,10 +715,14 @@ class EmailNotification {
                                        ->inContentLanguage()->text();
                        }
                        $keys['$OLDID']   = $this->oldid;
+                       // @deprecated Remove in MediaWiki 1.23.
+                       $keys['$CHANGEDORCREATED'] = wfMessage( 'changed' )->inContentLanguage()->text();
                } else {
                        # clear $OLDID placeholder in the message template
                        $keys['$OLDID']   = '';
                        $keys['$NEWPAGE'] = '';
+                       // @deprecated Remove in MediaWiki 1.23.
+                       $keys['$CHANGEDORCREATED'] = wfMessage( 'created' )->inContentLanguage()->text();
                }
 
                $keys['$PAGETITLE'] = $this->title->getPrefixedText();
@@ -681,11 +750,11 @@ class EmailNotification {
 
                # Now build message's subject and body
                $this->subject = wfMessage( 'enotif_subject_' . $this->pageStatus )->inContentLanguage()
-                       ->params( $pageTitle, $keys['$PAGEEDITOR'] )->escaped();
+                       ->params( $pageTitle, $keys['$PAGEEDITOR'] )->text();
 
                $keys['$PAGEINTRO'] = wfMessage( 'enotif_body_intro_' . $this->pageStatus )
                        ->inContentLanguage()->params( $pageTitle, $keys['$PAGEEDITOR'], $pageTitleUrl )
-                       ->escaped();
+                       ->text();
 
                $body = wfMessage( 'enotif_body' )->inContentLanguage()->plain();
                $body = strtr( $body, $keys );