Ticket #29513: 29513-3-wpmail.3.diff
File 29513-3-wpmail.3.diff, 19.6 KB (added by , 9 years ago) |
---|
-
new file src/wp-includes/class-wpmailer-factory.php
diff --git src/wp-includes/class-wpmailer-factory.php src/wp-includes/class-wpmailer-factory.php new file mode 100644 index 0000000..8f4917c
- + 1 <?php 2 3 /** 4 * Creates a WPMailer instance, and sets the default values of 5 * the content type, charset and from name/email. 6 * 7 * @see WPMailer 8 * @see wp_mail() 9 */ 10 class WPMailerFactory { 11 12 static function getMailer(){ 13 14 require_once ABSPATH . WPINC . '/class-phpmailer.php'; 15 require_once ABSPATH . WPINC . '/class-wpmailer.php'; 16 require_once ABSPATH . WPINC . '/class-smtp.php'; 17 18 $wpmailer = new WPMailer( true ); 19 $wpmailer = self::reset( $wpmailer ); 20 21 return $wpmailer; 22 } 23 24 static function reset( $wpmailer ){ 25 26 $wpmailer->ClearAllRecipients(); 27 $wpmailer->ClearAttachments(); 28 $wpmailer->ClearCustomHeaders(); 29 $wpmailer->ClearReplyTos(); 30 31 $sitename = strtolower( $_SERVER['SERVER_NAME'] ); 32 if ( substr( $sitename, 0, 4 ) == 'www.' ) { 33 $sitename = substr( $sitename, 4 ); 34 } 35 36 $wpmailer->ContentType = 'text/plain'; 37 $wpmailer->CharSet = get_bloginfo( 'charset' ); 38 39 $wpmailer->setFrom( 'wordpress@' . $sitename, 'WordPress' ); 40 41 return $wpmailer; 42 } 43 44 } 45 ?> -
new file src/wp-includes/class-wpmailer.php
diff --git src/wp-includes/class-wpmailer.php src/wp-includes/class-wpmailer.php new file mode 100644 index 0000000..9d7c136
- + 1 <?php 2 /** 3 * Email creation and transport class. (A child class of PHPMailer) 4 * 5 * This class adds a few 'helper' methods to the PHPMailer class and 6 * overrides the send method to trigger e-mail related filters. 7 * 8 */ 9 class WPMailer extends PHPMailer{ 10 11 /** 12 * Adds the passed e-mails as recipients 13 * 14 * @param string|array $recipients Array or comma-separated list of email addresses to send message. 15 * @type string One of 'to', 'cc', 'bcc', or 'ReplyTo' 16 */ 17 function addRecipients( $addresses, $type ) { 18 19 //Set destination addresses 20 if ( !is_array( $addresses ) ){ 21 $addresses = explode( ',', $addresses ); 22 } 23 24 foreach ( (array) $addresses as $address ) { 25 try { 26 // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>" 27 $recipient_name = ''; 28 29 if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) { 30 if ( count( $matches ) == 3 ) { 31 $recipient_name = $matches[1]; 32 $address = $matches[2]; 33 } 34 } 35 36 switch ( $type ) { 37 case 'to': 38 $this->addAddress( $address, $recipient_name ); 39 break; 40 case 'cc': 41 $this->addCc( $address, $recipient_name ); 42 break; 43 case 'bcc': 44 $this->addBcc( $address, $recipient_name ); 45 break; 46 case 'reply_to': 47 $this->addReplyTo( $address, $recipient_name ); 48 break; 49 } 50 } catch ( phpmailerException $e ) { 51 continue; 52 } 53 } 54 } 55 56 /** 57 * Adds the provided headers to the e-mail. 58 * 59 * Will set the content type, charset and CC/BCC recipients where appropriate 60 * 61 * @param string|array $headers Additional headers. 62 */ 63 function setHeaders( $headers = array() ){ 64 65 $tempheaders = array(); 66 67 // Headers 68 if ( $headers ){ 69 if ( !is_array( $headers ) ) { 70 // Explode the headers out, so this function can take both 71 // string headers and an array of headers. 72 $tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) ); 73 } else { 74 $tempheaders = $headers; 75 } 76 } 77 78 if ( empty( $tempheaders ) ) { 79 return; 80 } 81 82 $headers = array(); 83 $cc = $bcc = $reply_to = array(); 84 85 86 // Iterate through the raw headers 87 foreach ( (array) $tempheaders as $header ) { 88 if ( strpos($header, ':') === false ) { 89 if ( false !== stripos( $header, 'boundary=' ) ) { 90 $parts = preg_split('/boundary=/i', trim( $header ) ); 91 $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) ); 92 } 93 continue; 94 } 95 96 // Explode them out 97 list( $name, $content ) = explode( ':', trim( $header ), 2 ); 98 99 // Cleanup crew 100 $name = trim( $name ); 101 $content = trim( $content ); 102 103 switch ( strtolower( $name ) ): 104 // Mainly for legacy -- process a From: header if it's there 105 case 'from': 106 107 $bracket_pos = strpos( $content, '<' ); 108 if ( $bracket_pos !== false ) { 109 110 // Text before the bracketed email is the "From" name. 111 if ( $bracket_pos > 0 ) { 112 $from_name = substr( $content, 0, $bracket_pos - 1 ); 113 $from_name = str_replace( '"', '', $from_name ); 114 $this->FromName = trim( $from_name ); 115 } 116 $from_email = substr( $content, $bracket_pos + 1 ); 117 $from_email = str_replace( '>', '', $from_email ); 118 $this->From = trim( $from_email ); 119 120 // Avoid setting an empty $from_email. 121 } elseif ( '' !== trim( $content ) ) { 122 $this->From = trim( $content ); 123 } 124 break; 125 126 case 'content-type': 127 128 if ( strpos( $content, ';' ) !== false ) { 129 130 list( $type, $charset_content ) = explode( ';', $content ); 131 $this->ContentType = trim( $type ); 132 133 if ( false !== stripos( $charset_content, 'charset=' ) ) { 134 $this->CharSet = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) ); 135 } elseif ( false !== stripos( $charset_content, 'boundary=' ) ) { 136 $boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) ); 137 $charset = ''; 138 } 139 140 // Avoid setting an empty $content_type. 141 } elseif ( '' !== trim( $content ) ) { 142 $this->ContentType = trim( $content ); 143 } 144 145 if ( false !== stripos( $this->ContentType, 'multipart' ) && ! empty( $boundary ) ){ 146 $this->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $this->ContentType, $boundary ) ); 147 } 148 break; 149 150 case 'cc': 151 $cc = array_merge( (array) $cc, explode( ',', $content ) ); 152 break; 153 154 case 'bcc': 155 $bcc = array_merge( (array) $bcc, explode( ',', $content ) ); 156 break; 157 case 'reply-to': 158 $reply_to = array_merge( (array) $reply_to, explode( ',', $content ) ); 159 break; 160 default: 161 // Add it to our grand headers array 162 $this->AddCustomHeader( sprintf( '%1$s: %2$s', trim( $name ), trim( $content ) ) ); 163 break; 164 endswitch; 165 } 166 167 // Use appropriate methods for handling addresses, rather than treating them as generic headers 168 $address_headers = compact( 'cc', 'bcc', 'reply_to' ); 169 170 foreach ( $address_headers as $address_header => $addresses ) { 171 if ( empty( $addresses ) ) { 172 continue; 173 } 174 175 $this->addRecipients( $addresses, $address_header ); 176 } 177 } 178 179 /** 180 * Helper function for adding attachments. 181 * 182 * @param string|array $attachments Files to attach. 183 */ 184 function addAttachments( $attachments ){ 185 186 if ( !is_array($attachments) ) 187 $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) ); 188 189 190 foreach ( (array) $attachments as $attachment ) { 191 try { 192 $this->AddAttachment( $attachment ); 193 } catch ( phpmailerException $e ) { 194 continue; 195 } 196 } 197 } 198 199 /** 200 * Pre-send routine, for "last minute" filtering of e-mail properties. 201 * 202 * Using the two 'wp_mail_from' and 'wp_mail_from_name' hooks allow from 203 * creating a from address like 'Name <email@address.com>' when both are set. If 204 * just 'wp_mail_from' is set, then just the email address will be used with no 205 * name. 206 * 207 * The default content type is 'text/plain' which does not allow using HTML. 208 * However, you can set the content type of the email by using the 209 * 'wp_mail_content_type' filter. 210 * 211 * The default charset is based on the charset used on the blog. The charset can 212 * be set using the 'wp_mail_charset' filter. 213 * 214 * @see PHPMailer::preSend(); 215 * @param string|array $attachments Files to attach. 216 */ 217 function preSend(){ 218 219 /** 220 * Filters the email address to send from. 221 * 222 * @since 2.2.0 223 * 224 * @param string $from_email Email address to send from. 225 */ 226 $from_email = apply_filters( 'wp_mail_from', $this->From ); 227 228 /** 229 * Filters the name to associate with the "from" email address. 230 * 231 * @since 2.3.0 232 * 233 * @param string $from_name Name associated with the "from" email address. 234 */ 235 $from_name = apply_filters( 'wp_mail_from_name', $this->FromName ); 236 237 $this->setFrom( $from_email, $from_name ); 238 239 // Set to use PHP's mail() 240 $this->IsMail(); 241 242 /** 243 * Filter the wp_mail() content type. 244 * 245 * @since 2.3.0 246 * 247 * @param string $content_type Default wp_mail() content type. 248 */ 249 $this->ContentType = apply_filters( 'wp_mail_content_type', $this->ContentType ); 250 251 // Set whether it's plaintext, depending on $content_type 252 if ( 'text/html' == $this->ContentType ){ 253 $this->IsHTML( true ); 254 } 255 256 /** 257 * Filter the default wp_mail() charset. 258 * 259 * @since 2.3.0 260 * 261 * @param string $charset Default email charset. 262 */ 263 $this->CharSet = apply_filters( 'wp_mail_charset', $this->CharSet ); 264 265 /** 266 * Fires after PHPMailer is initialized. 267 * 268 * @since 2.2.0 269 * 270 * @param PHPMailer &$this The PHPMailer instance, passed by reference. 271 */ 272 do_action_ref_array( 'phpmailer_init', array( &$this ) ); 273 274 return parent::preSend(); 275 276 } 277 278 } -
src/wp-includes/pluggable.php
diff --git src/wp-includes/pluggable.php src/wp-includes/pluggable.php index 511e6ac..3935efa 100644
if ( !function_exists( 'wp_mail' ) ) : 147 147 * email successfully. It just only means that the method used was able to 148 148 * process the request without any errors. 149 149 * 150 * Using the two 'wp_mail_from' and 'wp_mail_from_name' hooks allow from151 * creating a from address like 'Name <email@address.com>' when both are set. If152 * just 'wp_mail_from' is set, then just the email address will be used with no153 * name.154 *155 * The default content type is 'text/plain' which does not allow using HTML.156 * However, you can set the content type of the email by using the157 * {@see 'wp_mail_content_type'} filter.158 *159 * The default charset is based on the charset used on the blog. The charset can160 * be set using the {@see 'wp_mail_charset'} filter.161 *162 150 * @since 1.2.1 163 151 * 164 * @ global PHPMailer $phpmailer152 * @uses WPMailer 165 153 * 166 154 * @param string|array $to Array or comma-separated list of email addresses to send message. 167 155 * @param string $subject Email subject … … function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() 203 191 $attachments = $atts['attachments']; 204 192 } 205 193 206 if ( ! is_array( $attachments ) ) {207 $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );208 }209 194 global $phpmailer; 210 195 211 196 // (Re)create it, if it's gone missing 212 if ( ! ( $phpmailer instanceof PHPMailer ) ) { 213 require_once ABSPATH . WPINC . '/class-phpmailer.php'; 214 require_once ABSPATH . WPINC . '/class-smtp.php'; 215 $phpmailer = new PHPMailer( true ); 216 } 217 218 // Headers 219 $cc = $bcc = $reply_to = array(); 220 221 if ( empty( $headers ) ) { 222 $headers = array(); 197 if ( ! is_object( $phpmailer ) || ! is_a( $phpmailer, 'WPMailer' ) ) { 198 $phpmailer = WPMailerFactory::getMailer(); 223 199 } else { 224 if ( !is_array( $headers ) ) { 225 // Explode the headers out, so this function can take both 226 // string headers and an array of headers. 227 $tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) ); 228 } else { 229 $tempheaders = $headers; 230 } 231 $headers = array(); 232 233 // If it's actually got contents 234 if ( !empty( $tempheaders ) ) { 235 // Iterate through the raw headers 236 foreach ( (array) $tempheaders as $header ) { 237 if ( strpos($header, ':') === false ) { 238 if ( false !== stripos( $header, 'boundary=' ) ) { 239 $parts = preg_split('/boundary=/i', trim( $header ) ); 240 $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) ); 241 } 242 continue; 243 } 244 // Explode them out 245 list( $name, $content ) = explode( ':', trim( $header ), 2 ); 246 247 // Cleanup crew 248 $name = trim( $name ); 249 $content = trim( $content ); 250 251 switch ( strtolower( $name ) ) { 252 // Mainly for legacy -- process a From: header if it's there 253 case 'from': 254 $bracket_pos = strpos( $content, '<' ); 255 if ( $bracket_pos !== false ) { 256 // Text before the bracketed email is the "From" name. 257 if ( $bracket_pos > 0 ) { 258 $from_name = substr( $content, 0, $bracket_pos - 1 ); 259 $from_name = str_replace( '"', '', $from_name ); 260 $from_name = trim( $from_name ); 261 } 262 263 $from_email = substr( $content, $bracket_pos + 1 ); 264 $from_email = str_replace( '>', '', $from_email ); 265 $from_email = trim( $from_email ); 266 267 // Avoid setting an empty $from_email. 268 } elseif ( '' !== trim( $content ) ) { 269 $from_email = trim( $content ); 270 } 271 break; 272 case 'content-type': 273 if ( strpos( $content, ';' ) !== false ) { 274 list( $type, $charset_content ) = explode( ';', $content ); 275 $content_type = trim( $type ); 276 if ( false !== stripos( $charset_content, 'charset=' ) ) { 277 $charset = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) ); 278 } elseif ( false !== stripos( $charset_content, 'boundary=' ) ) { 279 $boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) ); 280 $charset = ''; 281 } 282 283 // Avoid setting an empty $content_type. 284 } elseif ( '' !== trim( $content ) ) { 285 $content_type = trim( $content ); 286 } 287 break; 288 case 'cc': 289 $cc = array_merge( (array) $cc, explode( ',', $content ) ); 290 break; 291 case 'bcc': 292 $bcc = array_merge( (array) $bcc, explode( ',', $content ) ); 293 break; 294 case 'reply-to': 295 $reply_to = array_merge( (array) $reply_to, explode( ',', $content ) ); 296 break; 297 default: 298 // Add it to our grand headers array 299 $headers[trim( $name )] = trim( $content ); 300 break; 301 } 302 } 303 } 304 } 305 306 // Empty out the values that may be set 307 $phpmailer->ClearAllRecipients(); 308 $phpmailer->ClearAttachments(); 309 $phpmailer->ClearCustomHeaders(); 310 $phpmailer->ClearReplyTos(); 311 312 // From email and name 313 // If we don't have a name from the input headers 314 if ( !isset( $from_name ) ) 315 $from_name = 'WordPress'; 316 317 /* If we don't have an email from the input headers default to wordpress@$sitename 318 * Some hosts will block outgoing mail from this address if it doesn't exist but 319 * there's no easy alternative. Defaulting to admin_email might appear to be another 320 * option but some hosts may refuse to relay mail from an unknown domain. See 321 * https://core.trac.wordpress.org/ticket/5007. 322 */ 323 324 if ( !isset( $from_email ) ) { 325 // Get the site domain and get rid of www. 326 $sitename = strtolower( $_SERVER['SERVER_NAME'] ); 327 if ( substr( $sitename, 0, 4 ) == 'www.' ) { 328 $sitename = substr( $sitename, 4 ); 329 } 330 331 $from_email = 'wordpress@' . $sitename; 200 $phpmailer = WPMailerFactory::reset( $phpmailer ); 332 201 } 333 202 334 /**335 * Filters the email address to send from.336 *337 * @since 2.2.0338 *339 * @param string $from_email Email address to send from.340 */341 $from_email = apply_filters( 'wp_mail_from', $from_email );342 343 /**344 * Filters the name to associate with the "from" email address.345 *346 * @since 2.3.0347 *348 * @param string $from_name Name associated with the "from" email address.349 */350 $from_name = apply_filters( 'wp_mail_from_name', $from_name );351 352 $phpmailer->setFrom( $from_email, $from_name );353 354 203 // Set destination addresses 355 if ( !is_array( $to ) ) 356 $to = explode( ',', $to ); 204 $phpmailer->addRecipients( $to, 'to' ); 357 205 358 206 // Set mail's subject and body 359 207 $phpmailer->Subject = $subject; 360 208 $phpmailer->Body = $message; 361 209 362 // Use appropriate methods for handling addresses, rather than treating them as generic headers363 $address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' );364 365 foreach ( $address_headers as $address_header => $addresses ) {366 if ( empty( $addresses ) ) {367 continue;368 }369 370 foreach ( (array) $addresses as $address ) {371 try {372 // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"373 $recipient_name = '';374 375 if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) {376 if ( count( $matches ) == 3 ) {377 $recipient_name = $matches[1];378 $address = $matches[2];379 }380 }381 382 switch ( $address_header ) {383 case 'to':384 $phpmailer->addAddress( $address, $recipient_name );385 break;386 case 'cc':387 $phpmailer->addCc( $address, $recipient_name );388 break;389 case 'bcc':390 $phpmailer->addBcc( $address, $recipient_name );391 break;392 case 'reply_to':393 $phpmailer->addReplyTo( $address, $recipient_name );394 break;395 }396 } catch ( phpmailerException $e ) {397 continue;398 }399 }400 }401 402 // Set to use PHP's mail()403 $phpmailer->IsMail();404 405 // Set Content-Type and charset406 // If we don't have a content-type from the input headers407 if ( !isset( $content_type ) )408 $content_type = 'text/plain';409 410 /**411 * Filters the wp_mail() content type.412 *413 * @since 2.3.0414 *415 * @param string $content_type Default wp_mail() content type.416 */417 $content_type = apply_filters( 'wp_mail_content_type', $content_type );418 419 $phpmailer->ContentType = $content_type;420 421 // Set whether it's plaintext, depending on $content_type422 if ( 'text/html' == $content_type )423 $phpmailer->IsHTML( true );424 425 // If we don't have a charset from the input headers426 if ( !isset( $charset ) )427 $charset = get_bloginfo( 'charset' );428 429 // Set the content-type and charset430 431 /**432 * Filters the default wp_mail() charset.433 *434 * @since 2.3.0435 *436 * @param string $charset Default email charset.437 */438 $phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );439 440 210 // Set custom headers 441 211 if ( !empty( $headers ) ) { 442 foreach ( (array) $headers as $name => $content ) { 443 $phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) ); 444 } 445 446 if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) ) 447 $phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) ); 212 $phpmailer->setHeaders( $headers ); 448 213 } 449 214 450 if ( !empty( $attachments ) ) { 451 foreach ( $attachments as $attachment ) { 452 try { 453 $phpmailer->AddAttachment($attachment); 454 } catch ( phpmailerException $e ) { 455 continue; 456 } 457 } 458 } 459 460 /** 461 * Fires after PHPMailer is initialized. 462 * 463 * @since 2.2.0 464 * 465 * @param PHPMailer &$phpmailer The PHPMailer instance, passed by reference. 466 */ 467 do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) ); 215 //Set attachments 216 $phpmailer->addAttachments( $attachments ); 468 217 469 218 // Send! 470 219 try { -
src/wp-settings.php
diff --git src/wp-settings.php src/wp-settings.php index 37a31c2..8eeb043 100644
require( ABSPATH . WPINC . '/rest-api.php' ); 213 213 require( ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php' ); 214 214 require( ABSPATH . WPINC . '/rest-api/class-wp-rest-response.php' ); 215 215 require( ABSPATH . WPINC . '/rest-api/class-wp-rest-request.php' ); 216 require( ABSPATH . WPINC . '/class-wpmailer-factory.php' ); 216 217 217 218 // Load multisite-specific files. 218 219 if ( is_multisite() ) { -
tests/phpunit/includes/mock-mailer.php
diff --git tests/phpunit/includes/mock-mailer.php tests/phpunit/includes/mock-mailer.php index 7acfd57..940792c 100644
1 1 <?php 2 2 require_once( ABSPATH . '/wp-includes/class-phpmailer.php' ); 3 require_once( ABSPATH . '/wp-includes/class-wpmailer.php' ); 3 4 4 class MockPHPMailer extends PHPMailer {5 class MockPHPMailer extends WPMailer { 5 6 var $mock_sent = array(); 6 7 7 8 function preSend() {