Index: src/wp-includes/pluggable.php
===================================================================
--- src/wp-includes/pluggable.php	(revision 38500)
+++ src/wp-includes/pluggable.php	(working copy)
@@ -156,6 +156,11 @@
  * However, you can set the content type of the email by using the
  * {@see 'wp_mail_content_type'} filter.
  *
+ * If $message is an array, the key of each is used to add as an attachment
+ * with the value used as the body. The 'text/plain' element is used as the
+ * text version of the body, with the 'text/html' element used as the HTML
+ * version of the body. All other types are added as attachments.
+ *
  * The default charset is based on the charset used on the blog. The charset can
  * be set using the {@see 'wp_mail_charset'} filter.
  *
@@ -165,7 +170,7 @@
  *
  * @param string|array $to          Array or comma-separated list of email addresses to send message.
  * @param string       $subject     Email subject
- * @param string       $message     Message contents
+ * @param string|array $message     Message contents
  * @param string|array $headers     Optional. Additional headers.
  * @param string|array $attachments Optional. Files to attach.
  * @return bool Whether the email contents were sent successfully.
@@ -270,6 +275,10 @@
 						}
 						break;
 					case 'content-type':
+						if ( is_array( $message ) ) {
+							// Multipart email, ignore the content-type header
+							break;
+						}
 						if ( strpos( $content, ';' ) !== false ) {
 							list( $type, $charset_content ) = explode( ';', $content );
 							$content_type = trim( $type );
@@ -309,6 +318,9 @@
 	$phpmailer->ClearCustomHeaders();
 	$phpmailer->ClearReplyTos();
 
+	$phpmailer->Body= '';
+	$phpmailer->AltBody= '';
+
 	// From email and name
 	// If we don't have a name from the input headers
 	if ( !isset( $from_name ) )
@@ -355,10 +367,6 @@
 	if ( !is_array( $to ) )
 		$to = explode( ',', $to );
 
-	// Set mail's subject and body
-	$phpmailer->Subject = $subject;
-	$phpmailer->Body    = $message;
-
 	// Use appropriate methods for handling addresses, rather than treating them as generic headers
 	$address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' );
 
@@ -399,35 +407,11 @@
 		}
 	}
 
-	// Set to use PHP's mail()
-	$phpmailer->IsMail();
-
-	// Set Content-Type and charset
-	// If we don't have a content-type from the input headers
-	if ( !isset( $content_type ) )
-		$content_type = 'text/plain';
-
-	/**
-	 * Filters the wp_mail() content type.
-	 *
-	 * @since 2.3.0
-	 *
-	 * @param string $content_type Default wp_mail() content type.
-	 */
-	$content_type = apply_filters( 'wp_mail_content_type', $content_type );
-
-	$phpmailer->ContentType = $content_type;
-
-	// Set whether it's plaintext, depending on $content_type
-	if ( 'text/html' == $content_type )
-		$phpmailer->IsHTML( true );
-
+	// Set charset
 	// If we don't have a charset from the input headers
 	if ( !isset( $charset ) )
 		$charset = get_bloginfo( 'charset' );
 
-	// Set the content-type and charset
-
 	/**
 	 * Filters the default wp_mail() charset.
 	 *
@@ -437,14 +421,61 @@
 	 */
 	$phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );
 
+	// Set mail's subject and body
+	$phpmailer->Subject = $subject;
+
+	if ( is_string( $message ) ) {
+		$phpmailer->Body = $message;
+
+		// Set Content-Type
+		// If we don't have a content-type from the input headers
+		if ( ! isset( $content_type ) ) {
+			$content_type = 'text/plain';
+		}
+
+		/**
+		 * Filters the wp_mail() content type.
+		 *
+		 * @since 2.3.0
+		 *
+		 * @param string $content_type Default wp_mail() content type.
+		 */
+		$content_type = apply_filters( 'wp_mail_content_type', $content_type );
+
+		$phpmailer->ContentType = $content_type;
+
+		// Set whether it's plaintext, depending on $content_type
+		if ( 'text/html' === $content_type ) {
+			$phpmailer->IsHTML( true );
+		}
+
+		// For backwards compatibility, new multipart emails should use
+		// the array style $message. This never really worked well anyway
+		if ( false !== stripos( $content_type, 'multipart' ) && ! empty( $boundary ) ) {
+			$phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
+		}
+	} elseif ( is_array( $message ) ) {
+		foreach ( $message as $type => $bodies ) {
+			foreach ( (array) $bodies as $body ) {
+				if ( 'text/html' === $type ) {
+					$phpmailer->Body = $body;
+				} elseif ( 'text/plain' === $type ) {
+					$phpmailer->AltBody = $body;
+				} else {
+					$phpmailer->AddAttachment( $body, '', 'base64', $type );
+				}
+			}
+		}
+	}
+
+	// Set to use PHP's mail()
+	$phpmailer->IsMail();
+
 	// Set custom headers
 	if ( !empty( $headers ) ) {
 		foreach ( (array) $headers as $name => $content ) {
 			$phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
 		}
-
-		if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) )
-			$phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
 	}
 
 	if ( !empty( $attachments ) ) {
Index: tests/phpunit/tests/mail.php
===================================================================
--- tests/phpunit/tests/mail.php	(revision 38500)
+++ tests/phpunit/tests/mail.php	(working copy)
@@ -87,6 +87,71 @@
 	}
 
 	/**
+	 * @ticket 15448
+	 */
+	function test_wp_mail_plain_and_html() {
+		$to = 'user@example.com';
+		$subject = 'Test email with plain text and html versions';
+		$messages = array( 'text/plain' => 'Here is some plain text.',
+					   'text/html' =>'<html><head></head><body>Here is the HTML with UTF-8 γειά σου Κόσμε;-)<body></html>' );
+
+		wp_mail( $to, $subject, $messages );
+
+		preg_match( '/boundary="(.*)"/', $GLOBALS['phpmailer']->mock_sent[0]['header'], $matches );
+		$boundry = $matches[1];
+		$body = 'This is a multi-part message in MIME format.
+
+--' . $boundry . '
+Content-Type: text/plain; charset=us-ascii
+
+Here is some plain text.
+
+
+--' . $boundry . '
+Content-Type: text/html; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+<html><head></head><body>Here is the HTML with UTF-8 γειά σου Κόσμε;-)<body></html>
+
+
+
+--' . $boundry . '--
+';
+		// We need some better assertions here but these test the behaviour for now.
+		$this->assertEquals( $body, $GLOBALS['phpmailer']->mock_sent[0]['body'] );
+		$this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['header'], 'Content-Type: multipart/alternative;' ) );
+		$this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['header'], 'Content-Type:' ) );
+	}
+
+	/*
+	 * 'phpmailer_init' action for test_wp_mail_plain_and_html_workaround().
+	 */
+	function wp_mail_set_alt_body( $mailer ) {
+		$mailer->AltBody = strip_tags( $mailer->Body );
+	}
+
+	/**
+	 * @ticket 15448
+	 *
+	 * Check workarounds using phpmailer_init still work around.
+	 */
+	function test_wp_mail_plain_and_html_workaround() {
+		$to = 'user@example.com';
+		$subject = 'Test email with plain text derived from html version';
+		$message = '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body><p>Hello World! γειά σου Κόσμε</p></body></html>';
+
+		add_action( 'phpmailer_init', array( $this, 'wp_mail_set_alt_body' ) );
+		wp_mail( $to, $subject, $message );
+		remove_action( 'phpmailer_init', array( $this, 'wp_mail_set_alt_body' ) );
+
+		$this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['header'], 'Content-Type: multipart/alternative;' ) );
+		$this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['header'], 'Content-Type:' ) );
+		$this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['body'], 'Content-Type: text/plain; charset=UTF-8' ) );
+		$this->assertSame( 1, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['body'], 'Content-Type: text/html; charset=UTF-8' ) );
+		$this->assertSame( 2, substr_count( $GLOBALS['phpmailer']->mock_sent[0]['body'], 'Content-Type:' ) );
+	}
+
+	/**
 	 * @ticket 17305
 	 */
 	function test_wp_mail_rfc2822_addresses() {
