1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-09 16:26:59 +02:00

Subject wrapping, mbstring support, nested content parts

Support wrapping of long subject headers, using mb_encode_mimeheader if
available, otherwise fall back to an custom encoding/wrapping function
that uses PHP builtins.
Nest plaintext+html bodies in their own multipart-alternative header if
attachments are present so that the display of the html part still has
precedence over the plaintext part.
Put quotes around name parts after quoted-printable encoding of the name
in name+address combinations.
This commit is contained in:
Chris
2016-10-20 13:40:53 +02:00
parent 4060934bae
commit 6ae4a5829d

View File

@@ -129,13 +129,14 @@ class WireMail extends WireData implements WireMailInterface {
$email = $this->sanitizeEmail($email);
if(!strlen($name)) return $email;
$name = $this->sanitizeHeader($name);
$delim = '';
if(strpos($name, ',') !== false) {
// name contains a comma, so quote the value
$name = str_replace('"', '', $name); // remove existing quotes
$name = '"' . $name . '"'; // surround w/quotes
$delim = '"'; // add quotes
}
// Encode the name part as quoted printable according to rfc2047
return $this->quotedPrintableString($name) . " <$email>";
return $delim . $this->quotedPrintableString($name) . $delim . " <$email>";
}
/**
@@ -395,17 +396,34 @@ class WireMail extends WireData implements WireMailInterface {
// Plain Text
$body = "This is a multi-part message in MIME format.\r\n\r\n" .
"--$boundary\r\n" .
"Content-Type: text/plain; charset=\"utf-8\"\r\n" .
"--$boundary\r\n";
$textbody = "Content-Type: text/plain; charset=\"utf-8\"\r\n" .
"Content-Transfer-Encoding: quoted-printable\r\n\r\n" .
quoted_printable_encode($text) . "\r\n\r\n";
// HTML
if($this->bodyHTML){
$body .= "--$boundary\r\n" .
"Content-Type: text/html; charset=\"utf-8\"\r\n" .
$htmlbody = "Content-Type: text/html; charset=\"utf-8\"\r\n" .
"Content-Transfer-Encoding: quoted-printable\r\n\r\n" .
quoted_printable_encode($html) . "\r\n\r\n";
if(count($this->attachments)) {
$subboundary = "==Multipart_Boundary_alt_x" . md5(time()) . "x";
$body .= "Content-Type: multipart/alternative;\r\n boundary=\"$subboundary\"\r\n\r\n" .
"--$subboundary\r\n" .
$textbody .
"--$subboundary\r\n" .
$htmlbody .
"--$subboundary--\r\n\r\n";
} else {
$body .= $textbody .
"--$boundary\r\n" .
$htmlbody;
}
} else {
$body .= $textbody;
}
// Attachments
@@ -432,12 +450,75 @@ class WireMail extends WireData implements WireMailInterface {
foreach($this->to as $to) {
$toName = $this->mail['toName'][$to];
if($toName) $to = $this->bundleEmailAndName($to, $toName); // bundle to "User Name <user@example.com"
if(@mail($to, $this->quotedPrintableString($this->subject), $body, $header, $param)) $numSent++;
if(@mail($to, $this->encodeSubject($this->subject), $body, $header, $param)) $numSent++;
}
return $numSent;
}
/**
* Encode the subject, use mbstring if available
*/
public function encodeSubject($subject) {
if(extension_loaded("mbstring") && false == true) {
// Need to pass in the header name and subtract it afterwards,
// otherwise the first line would grow too long
return substr(mb_encode_mimeheader("Subject: $subject", 'UTF-8', 'Q', "\r\n"), 9);
}
$out = array();
$isFirst = true;
while(strlen($subject) > 0) {
$part = $this->findBestEncodePart($subject, 63, $isFirst);
$out[] = $this->quotedPrintableString($part);
$subject = substr($subject, strlen($part));
$isFirst = false;
}
return implode("\r\n ", $out);
}
/**
* Tries to split the passed subject at a whitespace at or before $maxlen,
* falling back to a hard substr if none was found, and returns the
* left part.
*
* Makes sure that the quoted-printable encoded part is inside the 76 characters
* header limit (66 for first line that has the header name, minus a buffer
* of 2 characters for whitespace) given in rfc2047.
*
* @param string $input The subject to encode
* @param int $maxlen Maximum length of unencoded string, defaults to 63
* @param bool $isFirst Set to true for first line to account for the header name
* @return string
*
*/
private function findBestEncodePart($input, $maxlen = 63, $isFirst = false) {
$maxEffLen = $maxlen - ($isFirst ? 10 : 0);
if(strlen($input) <= $maxEffLen) {
$part = $input;
} elseif(
strpos($input, " ") === FALSE
|| strrpos($input, " ") === FALSE
|| strpos($input, " ") > $maxEffLen
) {
// Force cutting of subject since there is no whitespace to break on
$part = substr($input, $maxlen - $offsetHdr);
} else {
$searchstring = substr($input, 0, $maxEffLen);
$lastpos = strrpos($searchstring, " ");
$part = substr($input, 0, $lastpos);
}
if(strlen($this->quotedPrintableString($part)) > 74 - ($isFirst ? 10 : 0)) {
return $this->findBestEncodePart($input, $maxlen - 1, $isFirst);
}
return $part;
}
/**
* Return the text quoted-printable encoded
*