How do I get just the text content from a multipart email? - php

#!/usr/bin/php -q
<?php
$savefile = "savehere.txt";
$sf = fopen($savefile, 'a') or die("can't open file");
ob_start();
// read from stdin
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
$email .= fread($fd, 1024);
}
fclose($fd);
// handle email
$lines = explode("\n", $email);
// empty vars
$from = "";
$subject = "";
$headers = "";
$message = "";
$splittingheaders = true;
for ($i=0; $i < count($lines); $i++) {
if ($splittingheaders) {
// this is a header
$headers .= $lines[$i]."\n";
// look out for special headers
if (preg_match("/^Subject: (.*)/", $lines[$i], $matches)) {
$subject = $matches[1];
}
if (preg_match("/^From: (.*)/", $lines[$i], $matches)) {
$from = $matches[1];
}
if (preg_match("/^To: (.*)/", $lines[$i], $matches)) {
$to = $matches[1];
}
} else {
// not a header, but message
$message .= $lines[$i]."\n";
}
if (trim($lines[$i])=="") {
// empty line, header section has ended
$splittingheaders = false;
}
}
/*$headers is ONLY included in the result at the last section of my question here*/
fwrite($sf,"$message");
ob_end_clean();
fclose($sf);
?>
That is an example of my attempt. The problem is I am getting too much in the file.
Here is what is being written to the file: (I just sent a bunch of garbage to it as you can see)
From xxxxxxxxxxxxx Tue Sep 07 16:26:51 2010
Received: from xxxxxxxxxxxxxxx ([xxxxxxxxxxx]:3184 helo=xxxxxxxxxxx)
by xxxxxxxxxxxxx with esmtpa (Exim 4.69)
(envelope-from <xxxxxxxxxxxxxxxx>)
id 1Ot4kj-000115-SP
for xxxxxxxxxxxxxxxxxxx; Tue, 07 Sep 2010 16:26:50 -0400
Message-ID: <EE3B7E26298140BE8700D9AE77CB339D#xxxxxxxxxxx>
From: "xxxxxxxxxxxxx" <xxxxxxxxxxxxxx>
To: <xxxxxxxxxxxxxxxxxxxxx>
Subject: stackoverflow is helping me
Date: Tue, 7 Sep 2010 16:26:46 -0400
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_NextPart_000_0169_01CB4EA9.773DF5E0"
X-Priority: 3
X-MSMail-Priority: Normal
Importance: Normal
X-Mailer: Microsoft Windows Live Mail 14.0.8089.726
X-MIMEOLE: Produced By Microsoft MimeOLE V14.0.8089.726
This is a multi-part message in MIME format.
------=_NextPart_000_0169_01CB4EA9.773DF5E0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
111
222
333
444
------=_NextPart_000_0169_01CB4EA9.773DF5E0
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META content=3Dtext/html;charset=3Diso-8859-1 =
http-equiv=3DContent-Type>
<META name=3DGENERATOR content=3D"MSHTML 8.00.6001.18939"></HEAD>
<BODY style=3D"PADDING-LEFT: 10px; PADDING-RIGHT: 10px; PADDING-TOP: =
15px"=20
id=3DMailContainerBody leftMargin=3D0 topMargin=3D0 =
CanvasTabStop=3D"true"=20
name=3D"Compose message area">
<DIV><FONT face=3DCalibri>111</FONT></DIV>
<DIV><FONT face=3DCalibri>222</FONT></DIV>
<DIV><FONT face=3DCalibri>333</FONT></DIV>
<DIV><FONT face=3DCalibri>444</FONT></DIV></BODY></HTML>
------=_NextPart_000_0169_01CB4EA9.773DF5E0--
I found this while searching around but have no idea how to implement or where to insert in my code or if it would work.
preg_match("/boundary=\".*?\"/i", $headers, $boundary);
$boundaryfulltext = $boundary[0];
if ($boundaryfulltext!="")
{
$find = array("/boundary=\"/i", "/\"/i");
$boundarytext = preg_replace($find, "", $boundaryfulltext);
$splitmessage = explode("--" . $boundarytext, $message);
$fullmessage = ltrim($splitmessage[1]);
preg_match('/\n\n(.*)/is', $fullmessage, $splitmore);
if (substr(ltrim($splitmore[0]), 0, 2)=="--")
{
$actualmessage = $splitmore[0];
}
else
{
$actualmessage = ltrim($splitmore[0]);
}
}
else
{
$actualmessage = ltrim($message);
}
$clean = array("/\n--.*/is", "/=3D\n.*/s");
$cleanmessage = trim(preg_replace($clean, "", $actualmessage));
So, how can I get just the plain text area of the email into my file or script for furthr handling??
Thanks in advance. stackoverflow is great!

There are four steps that you will have to take in order to isolate the plain text part of your email body:
1. Get the MIME boundary string
We can use a regular expression to search your headers (let's assume they're in a separate variable, $headers):
$matches = array();
preg_match('#Content-Type: multipart\/[^;]+;\s*boundary="([^"]+)"#i', $headers, $matches);
list(, $boundary) = $matches;
The regular expression will search for the Content-Type header that contains the boundary string, and then capture it into the first capture group. We then copy that capture group into variable $boundary.
2. Split the email body into segments
Once we have the boundary, we can split the body into its various parts (in your message body, the body will be prefaced by -- each time it appears). According to the MIME spec, everything before the first boundary should be ignored.
$email_segments = explode('--' . $boundary, $message);
array_shift($email_segments); // drop everything before the first boundary
This will leave us with an array containing all the segments, with everything before the first boundary ignored.
3. Determine which segment is plain text.
The segment that is plain text will have a Content-Type header with the MIME-type text/plain. We can now search each segment for the first segment with that header:
foreach ($email_segments as $segment)
{
if (stristr($segment, "Content-Type: text/plain") !== false)
{
// We found the segment we're looking for!
}
}
Since what we're looking for is a constant, we can use stristr (which finds the first instance of a substring in a string, case insensitively) instead of a regular expression. If the Content-Type header is found, we've got our segment.
4. Remove any headers from the segment
Now we need to remove any headers from the segment we found, as we only want the actual message content. There are four MIME headers that can appear here: Content-Type as we saw before, Content-ID, Content-Disposition and Content-Transfer-Encoding. Headers are terminated by \r\n so we can use that to determine the end of the headers:
$text = preg_replace('/Content-(Type|ID|Disposition|Transfer-Encoding):.*?\r\n/is', "", $segment);
The s modifier at the end of the regular expression makes the dot match any newlines. .*? will collect as few characters as possible (ie. everything up to \r\n); the ? is a lazy modifier on .*.
And after this point, $text will contain your email message content.
So to put it all together with your code:
<?php
// read from stdin
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd))
{
$email .= fread($fd, 1024);
}
fclose($fd);
$matches = array();
preg_match('#Content-Type: multipart\/[^;]+;\s*boundary="([^"]+)"#i', $email, $matches);
list(, $boundary) = $matches;
$text = "";
if (isset($boundary) && !empty($boundary)) // did we find a boundary?
{
$email_segments = explode('--' . $boundary, $email);
foreach ($email_segments as $segment)
{
if (stristr($segment, "Content-Type: text/plain") !== false)
{
$text = trim(preg_replace('/Content-(Type|ID|Disposition|Transfer-Encoding):.*?\r\n/is', "", $segment));
break;
}
}
}
// At this point, $text will either contain your plain text body,
// or be an empty string if a plain text body couldn't be found.
$savefile = "savehere.txt";
$sf = fopen($savefile, 'a') or die("can't open file");
fwrite($sf, $text);
fclose($sf);
?>

There is one answer here:
You need only to change these 2 lines:
require_once('/path/to/class/rfc822_addresses.php');
require_once('/path/to/class/mime_parser.php');

Related

regex returns empty array

im really struggling now and I dont know what im doing wrong.
I have regex for analyzing imap_body. It works perfectly fine in my test case but completly fails in a function.
Heres the test case:
$type = "base64";
$regex_html = "/(?<=Content-Type: text\/html;).*?(Content-Transfer-Encoding:).*?(".$type.").*?(\);).(.*?)(?= --_)/";
$email = "--_004_AM4PR0901MB1219E3152878394D9F48EFB982BC0AM4PR0901MB1219_ Content-Type: multipart/alternative; boundary="_000_AM4PR0901MB1219E3152878394D9F48EFB982BC0AM4PR0901MB1219_" X-Microsoft-Exchange-Diagnostics: 1;DB6P192MB0231;27:wPFlVOQ8iMAXT+N6591mBRfClfXdhMLmsxh/Y7d1LVbWrHPkVIxvj9+3g1ttW81Vi7Xh16wwzAIakEj5LqQ7s3r7Kr3/lVOL94eT9Ky+Tm+IqokcaiD5Q9lLzBrvUIz0CJDh27WR+m3qUDN87HH0jA== X-Microsoft-Antispam-Mailbox-Delivery: abwl:0;wl:0;pcwl:0;kl:0;iwl:0;ijl:0;dwl:0;dkl:0;rwl:0;ex:0;auth:1;dest:I;WIMS-SenderIP:40.92.69.49;WIMS-SPF:windowslive%2ecom;WIMS-DKIM:windowslive%2ecom;WIMS-822:trojak3d%40windowslive%2ecom;WIMS-PRA:trojak3d%40windowslive%2ecom;WIMS-AUTH:PASS;ENG:(400001000128)(400125000095)(5062000261)(5061607266)(5061608174)(4900095)(4921089)(4950112)(4990090)(102400140)(400001001223)(400125100095)(61617095)(400001002128)(400125200095); --_000_AM4PR0901MB1219E3152878394D9F48EFB982BC0AM4PR0901MB1219_ Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 X-Microsoft-Exchange-Diagnostics: 1;DB6P192MB0231;27:wPFlVOQ8iMAXT+N6591mBRfClfXdhMLmsxh/Y7d1LVbWrHPkVIxvj9+3g1ttW81Vi7Xh16wwzAIakEj5LqQ7s3r7Kr3/lVOL94eT9Ky+Tm+IqokcaiD5Q9lLzBrvUIz0CJDh27WR+m3qUDN87HH0jA== X-Microsoft-Antispam-Mailbox-Delivery: abwl:0;wl:0;pcwl:0;kl:0;iwl:0;ijl:0;dwl:0;dkl:0;rwl:0;ex:0;auth:1;dest:I;WIMS-SenderIP:40.92.69.49;WIMS-SPF:windowslive%2ecom;WIMS-DKIM:windowslive%2ecom;WIMS-822:trojak3d%40windowslive%2ecom;WIMS-PRA:trojak3d%40windowslive%2ecom;WIMS-AUTH:PASS;ENG:(400001000128)(400125000095)(5062000261)(5061607266)(5061608174)(4900095)(4921089)(4950112)(4990090)(102400140)(400001001223)(400125100095)(61617095)(400001002128)(400125200095); DQoNCkZyb206IFNlcnZpY2VAcGF5cGFJLmNvbSBbbWFpbHRvOmthaG5paWp1bGVAamlhbnNrb2wu Y29tXQ0KU2VudDogMjggSnVseSAyMDE3IDEwOjE2DQpUbzogdHJvamFrM2RAd2luZG93c2xpdmUu Y29tDQpTdWJqZWN0OiBbUGF5UGFJIGxuY10gU3RhdGVtZW50IGFjY291bnQgdW51c3VhbCBhY3Rp dml0eSBsb2dpbiB0byByZWFjdGl2YXRlZCAwNy8yOC8xNw0KDQpbSW1hZ2UgcmVtb3ZlZCBieSBz ZW5kZXIuIFNpbXBsZUFwcF0NCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQpXZSBuZWVkIHlvdXIgaGVs cCB0byByZXNvbHZlIHVuYXV0aG9yaXplZCBhY3Rpdml0ZXMNCg0KRGVhciBDbGllbnQsDQoNCldl IGp1c3Qgd2FudGVkIHRvIGNvbmZpcm0gdGhhdCB5b3UndmUgY2hhbmdlZCB5b3VyIHBhc3N3b3Jk LiBJZiB5b3UgZGlkbid0IG1ha2UgdGhpcyBjaGFuZ2UsIHBsZWFzZSBjaGVjayBpbmZvcm1hdGlv biBpbiBoZXJlLiBJdCdzIGltcG9ydGFudCB0aGF0IHlvdSBsZXQgdXMga25vdyBiZWNhdXNlIGl0 IGhlbHBzIHVzIHByZXZlbnQgdW5hdXRob3Jpc2VkIHBlcnNvbnMgZnJvbSBhY2Nlc3NpbmcgdGhl IFBheVBhbCBuZXR3b3JrIGFuZCB5b3VyIGFjY291bnQgaW5mb3JtYXRpb24uDQoNCldlJ3ZlIG5v dGljZWQgc29tZSBjaGFuZ2VzIHRvIHlvdXIgdW5zdWFsIHNlbGxpbmcgYWN0aXZpdGllcyBhbmQg d2lsbCBuZWVkIHNvbWUgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB5b3VyIHJlY2VudCBzYWxlcy4N CkNvbmZpcm0geW91ciBlbWFpbDxodHRwOi8vb3cubHkvcWVMMzMwZFl6TzI+DQoNCg0KVGhhbmsg eW91IGZvciB5b3VyIHVuZGVyc3RhbmRpbmcgYW5kIGNvb3BlcmF0aW9uLiBJZiB5b3UgbmVlZCBm dXJ0aGVyIGFzc2lzdGFuY2UsIHBsZWFzZSBjbGljayBDb250YWN0IGF0IHRoZSBib3R0b20gb2Yg YW55IFBheVBhbCBwYWdlLg0KDQpTaW5jZXJlbHksDQoNClBheVBhbA0KDQoNCg0KDQoNCkhlbHAg PGh0dHA6Ly9wYXlwYWwuY29tPiB8IFJlc29sdXRpb24gPGh0dHA6Ly9wYXlwYWwuY29tPiB8IFNl Y3VyaXR5IENlbnRyZTxodHRwOi8vcGF5cGFsLmNvbT4NCg0KUGxlYXNlIGRvIG5vdCByZXBseSB0 byB0aGlzIGVtYWlsLiBUbyBnZXQgaW4gdG91Y2ggd2l0aCB1cywgY2xpY2sgSGVscCAmIENvbnRh Y3QuPGh0dHA6Ly9wYXlwYWwuY29tPg0KDQpDb3B5cmlnaHQgwqkgMTk5OSDigJMgMjAxNyBQYXlQ YWwuIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQoNCkNvbnN1bWVyIGFkdmlzb3J5IC0gUGF5UGFsIFB0 ZS4gTHRkLiB0aGUgaG9sZGVyIG9mIFBheVBhbCdzIHN0b3JlZCB2YWx1ZSBmYWNpbGl0eSwgZG9l cyBub3QgcmVxdWlyZSB0aGUgYXBwcm92YWwgb2YgdGhlIE1vbmV0YXJ5IEF1dGhvcml0eSBvZiBT aW5nYXBvcmUuIFVzZXJzIGFyZSBhZHZpc2VkIHRvIHJlYWQgdGhlIHRlcm1zIGFuZCBjb25kaXRp b25zIGNhcmVmdWxseS4NCg0KUGF5UGFsIFBQWDAwMTg4OToyMTNlZTFiZjYxODdlDQoNCg0KDQoN Cg0KDQo= --_000_AM4PR0901MB1219E3152878394D9F48EFB982BC0AM4PR0901MB1219_ Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: base64 X-Microsoft-Exchange-Diagnostics: 1;DB6P192MB0231;27:wPFlVOQ8iMAXT+N6591mBRfClfXdhMLmsxh/Y7d1LVbWrHPkVIxvj9+3g1ttW81Vi7Xh16wwzAIakEj5LqQ7s3r7Kr3/lVOL94eT9Ky+Tm+IqokcaiD5Q9lLzBrvUIz0CJDh27WR+m3qUDN87HH0jA== X-Microsoft-Antispam-Mailbox-Delivery: abwl:0;wl:0;pcwl:0;kl:0;iwl:0;ijl:0;dwl:0;dkl:0;rwl:0;ex:0;auth:1;dest:I;WIMS-SenderIP:40.92.69.49;WIMS-SPF:windowslive%2ecom;WIMS-DKIM:windowslive%2ecom;WIMS-822:trojak3d%40windowslive%2ecom;WIMS-PRA:trojak3d%40windowslive%2ecom;WIMS-AUTH:PASS;ENG:(400001000128)(400125000095)(5062000261)(5061607266)(5061608174)(4900095)(4921089)(4950112)(4990090)(102400140)(400001001223)(400125100095)(61617095)(400001002128)(400125200095); PGh0bWwgeG1sbnM6dj0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTp2bWwiIHhtbG5zOm89InVy bjpzY2hlbWFzLW1pY3Jvc29mdC1jb206b2ZmaWNlOm9mZmljZSIgeG1sbnM6dz0idXJuOnNjaGVt YXMtbWljcm9zb2Z0LWNvbTpvZmZpY2U6d29yZCIgeG1sbnM6bT0iaHR0cDovL3NjaGVtYXMubWlj cm9zb2Z0LmNvbS9vZmZpY2UvMjAwNC8xMi9vbW1sIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv VFIvUkVDLWh0bWw0MCI+PGhlYWQ+DQo8bWV0YSBodHRwLWVxdWl2PSJDb250ZW50LVR5cGUiIGNv bnRlbnQ9InRleHQvaHRtbDsgY2hhcnNldD11dGYtOCI+DQo8bWV0YSBuYW1lPSJHZW5lcmF0b3Ii IGNvbnRlbnQ9Ik1pY3Jvc29mdCBXb3JkIDE1IChmaWx0ZXJlZCBtZWRpdW0pIj4NCjwhLS1baWYg IW1zb10+PHN0eWxlPnZcOioge2JlaGF2aW9yOnVybCgjZGVmYXVsdCNWTUwpO30NCm9cOioge2Jl aGF2aW9yOnVybCgjZGVmYXVsdCNWTUwpO30NCndcOioge2JlaGF2aW9yOnVybCgjZGVmYXVsdCNW TUwpO30NCi5zaGFwZSB7YmVoYXZpb3I6dXJsKCNkZWZhdWx0I1ZNTCk7fQ0KPC9zdHlsZT48IVtl bmRpZl0tLT48c3R5bGU+PCEtLQ0KLyogRm9udCBEZWZpbml0aW9ucyAqLw0KQGZvbnQtZmFjZQ0K CXtmb250LWZhbWlseTpIZWx2ZXRpY2E7DQoJcGFub3NlLTE6MiAxMSA2IDQgMiAyIDIgMiAyIDQ7 fQ0KQGZvbnQtZmFjZQ0KCXtmb250LWZhbWlseToiQ2FtYnJpYSBNYXRoIjsNCglwYW5vc2UtMToy IDQgNSAzIDUgNCA2IDMgMiA0O30NCkBmb250LWZhY2UNCgl7Zm9udC1mYW1pbHk6Q2FsaWJyaTsN CglwYW5vc2UtMToyIDE1IDUgMiAyIDIgNCAzIDIgNDt9DQovKiBTdHlsZSBEZWZpbml0aW9ucyAq Lw0KcC5Nc29Ob3JtYWwsIGxpLk1zb05vcm1hbCwgZGl2Lk1zb05vcm1hbA0KCXttYXJnaW46MGNt Ow0KCW1hcmdpbi1ib3R0b206LjAwMDFwdDsNCglmb250LXNpemU6MTEuMHB0Ow0KCWZvbnQtZmFt aWx5OiJDYWxpYnJpIixzYW5zLXNlcmlmO30NCmE6bGluaywgc3Bhbi5Nc29IeXBlcmxpbmsNCgl7 bXNvLXN0eWxlLXByaW9yaXR5Ojk5Ow0KCWNvbG9yOmJsdWU7DQoJdGV4dC1kZWNvcmF0aW9uOnVu ZGVybGluZTt9DQphOnZpc2l0ZWQsIHNwYW4uTXNvSHlwZXJsaW5rRm9sbG93ZWQNCgl7bXNvLXN0 eWxlLXByaW9yaXR5Ojk5Ow0KCWNvbG9yOnB1cnBsZTsNCgl0ZXh0LWRlY29yYXRpb246dW5kZXJs aW5lO30NCnAubXNvbm9ybWFsMCwgbGkubXNvbm9ybWFsMCwgZGl2Lm1zb25vcm1hbDANCgl7bXNv LXN0eWxlLW5hbWU6bXNvbm9ybWFsOw0KCW1zby1tYXJnaW4tdG9wLWFsdDphdXRvOw0KCW1hcmdp bi1yaWdodDowY207DQoJbXNvLW1hcmdpbi1ib3R0b20tYWx0OmF1dG87DQoJbWFyZ2luLWxlZnQ6 MGNtOw0KCWZvbnQtc2l6ZToxMS4wcHQ7DQoJZm9udC1mYW1pbHk6IkNhbGlicmkiLHNhbnMtc2Vy aWY7fQ0Kc3Bhbi5FbWFpbFN0eWxlMTkNCgl7bXNvLXN0eWxlLXR5cGU6cGVyc29uYWwtcmVwbHk7 DQoJZm9udC1mYW1pbHk6IkNhbGlicmkiLHNhbnMtc2VyaWY7DQoJY29sb3I6d2luZG93dGV4dDt9 DQouTXNvQ2hwRGVmYXVsdA0KCXttc28tc3R5bGUtdHlwZTpleHBvcnQtb25seTsNCglmb250LWZh bWlseToiQ2FsaWJyaSIsc2Fucy1zZXJpZjsNCgltc28tZmFyZWFzdC1sYW5ndWFnZTpFTi1VUzt9 DQpAcGFnZSBXb3JkU2VjdGlvbjENCgl7c2l6ZTo2MTIuMHB0IDc5Mi4wcHQ7DQoJbWFyZ2luOjcy LjBwdCA3Mi4wcHQgNzIuMHB0IDcyLjBwdDt9DQpkaXYuV29yZFNlY3Rpb24xDQoJe3BhZ2U6V29y ZFNlY3Rpb24xO30NCi0tPjwvc3R5bGU+PCEtLVtpZiBndGUgbXNvIDldPjx4bWw+DQo8bzpzaGFw ZWRlZmF1bHRzIHY6ZXh0PSJlZGl0IiBzcGlkbWF4PSIxMDI2IiAvPg0KPC94bWw+PCFbZW5kaWZd LS0+PCEtLVtpZiBndGUgbXNvIDldPjx4bWw+DQo8bzpzaGFwZWxheW91dCB2OmV4dD0iZWRpdCI+ DQo8bzppZG1hcCB2OmV4dD0iZWRpdCIgZGF0YT0iMSIgLz4NCjwvbzpzaGFwZWxheW91dD48L3ht bD48IVtlbmRpZl0tLT4NCjwvaGVhZD4NCjxib2R5IGxhbmc9IkVOLUdCIiBsaW5rPSJibHVlIiB2 bGluaz0icHVycGxlIj4NCjxkaXYgY2xhc3M9IldvcmRTZWN0aW9uMSI+DQo8cCBjbGFzcz0iTXNv Tm9ybWFsIj48c3BhbiBzdHlsZT0ibXNvLWZhcmVhc3QtbGFuZ3VhZ2U6RU4tVVMiPjxvOnA+Jm5i c3A7PC9vOnA+PC9zcGFuPjwvcD4NCjxwIGNsYXNzPSJNc29Ob3JtYWwiPjxzcGFuIHN0eWxlPSJt c28tZmFyZWFzdC1sYW5ndWFnZTpFTi1VUyI+PG86cD4mbmJzcDs8L286cD48L3NwYW4+PC9wPg0K PHAgY2xhc3M9Ik1zb05vcm1hbCI+PGI+PHNwYW4gbGFuZz0iRU4tVVMiPkZyb206PC9zcGFuPjwv Yj48c3BhbiBsYW5nPSJFTi1VUyI+IFNlcnZpY2VAcGF5cGFJLmNvbSBbbWFpbHRvOmthaG5paWp1 bGVAamlhbnNrb2wuY29tXQ0KPGJyPg0KPGI+U2VudDo8L2I+IDI4IEp1bHkgMjAxNyAxMDoxNjxi cj4NCjxiPlRvOjwvYj4gdHJvamFrM2RAd2luZG93c2xpdmUuY29tPGJyPg0KPGI+U3ViamVjdDo8 L2I+IFtQYXlQYUkgbG5jXSBTdGF0ZW1lbnQgYWNjb3VudCB1bnVzdWFsIGFjdGl2aXR5IGxvZ2lu IHRvIHJlYWN0aXZhdGVkIDA3LzI4LzE3PG86cD48L286cD48L3NwYW4+PC9wPg0KPHAgY2xhc3M9 Ik1zb05vcm1hbCI+PG86cD4mbmJzcDs8L286cD48L3A+DQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0K PHRhYmxlIGNsYXNzPSJNc29Ob3JtYWxUYWJsZSIgYm9yZGVyPSIwIiBjZWxsc3BhY2luZz0iMCIg Y2VsbHBhZGRpbmc9IjAiIHdpZHRoPSI1ODAiIHN0eWxlPSJ3aWR0aDo0MzUuMHB0Ij4NCjx0Ym9k eT4NCjx0cj4NCjx0ZCB3aWR0aD0iNTgwIiB2YWxpZ249InRvcCIgc3R5bGU9IndpZHRoOjQzNS4w cHQ7cGFkZGluZzowY20gMGNtIDBjbSAwY20iPg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCjx0YWJs ZSBjbGFzcz0iTXNvTm9ybWFsVGFibGUiIGJvcmRlcj0iMCIgY2VsbHNwYWNpbmc9IjAiIGNlbGxw YWRkaW5nPSIwIiB3aWR0aD0iMTAwJSIgc3R5bGU9IndpZHRoOjEwMC4wJSI+DQo8dGJvZHk+DQo8 dHI+DQo8dGQgdmFsaWduPSJ0b3AiIHN0eWxlPSJiYWNrZ3JvdW5kOndoaXRlO3BhZGRpbmc6MGNt IDBjbSAxMi4wcHQgMGNtIj4NCjxkaXYgYWxpZ249ImNlbnRlciI+DQo8dGFibGUgY2xhc3M9Ik1z b05vcm1hbFRhYmxlIiBib3JkZXI9IjAiIGNlbGxzcGFjaW5nPSIwIiBjZWxscGFkZGluZz0iMCIg d2lkdGg9IjU4MCIgc3R5bGU9IndpZHRoOjQzNS4wcHQiPg0KPHRib2R5Pg0KPHRyPg0KPHRkIHdp ZHRoPSI1ODAiIHZhbGlnbj0idG9wIiBzdHlsZT0id2lkdGg6NDM1LjBwdDtwYWRkaW5nOjBjbSAw Y20gMGNtIDBjbSI+DQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KPHRhYmxlIGNsYXNzPSJNc29Ob3Jt YWxUYWJsZSIgYm9yZGVyPSIwIiBjZWxsc3BhY2luZz0iMCIgY2VsbHBhZGRpbmc9IjAiIHdpZHRo PSI1ODAiIHN0eWxlPSJ3aWR0aDo0MzUuMHB0Ij4NCjx0Ym9keT4NCjx0cj4NCjx0ZCB3aWR0aD0i NTgwIiB2YWxpZ249InRvcCIgc3R5bGU9IndpZHRoOjQzNS4wcHQ7cGFkZGluZzowY20gMGNtIDBj bSAwY20iPg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCjx0YWJsZSBjbGFzcz0iTXNvTm9ybWFsVGFi bGUiIGJvcmRlcj0iMCIgY2VsbHNwYWNpbmc9IjAiIGNlbGxwYWRkaW5nPSIwIiB3aWR0aD0iMTAw JSIgc3R5bGU9IndpZHRoOjEwMC4wJSI+DQo8dGJvZHk+DQo8dHI+DQo8dGQgdmFsaWduPSJ0b3Ai IHN0eWxlPSJwYWRkaW5nOjEyLjBwdCAxMi4wcHQgMGNtIDEyLjBwdCI+DQo8cCBjbGFzcz0iTXNv Tm9ybWFsIj48Yj48c3BhbiBzdHlsZT0iZm9udC1zaXplOjE1LjBwdDtmb250LWZhbWlseTomcXVv dDtIZWx2ZXRpY2EmcXVvdDssc2Fucy1zZXJpZjtjb2xvcjp3aGl0ZSI+PHNwYW4gc3R5bGU9ImJv cmRlcjpzb2xpZCB3aW5kb3d0ZXh0IDEuMHB0O3BhZGRpbmc6MGNtIj48aW1nIHdpZHRoPSIxMjAi IGhlaWdodD0iMTIwIiBzdHlsZT0id2lkdGg6MS4yNWluO2hlaWdodDoxLjI1aW4iIGlkPSJfeDAw MDBfaTEwMjUiIHNyYz0iY2lkOmltYWdlMDAxLmpwZ0AwMUQzMDhCMi5DOTMxRUU5MCIgYWx0PSJJ bWFnZSByZW1vdmVkIGJ5IHNlbmRlci4gU2ltcGxlQXBwIj48L3NwYW4+PG86cD48L286cD48L3Nw YW4+PC9iPjwvcD4NCjwvdGQ+DQo8L3RyPg0KPC90Ym9keT4NCjwvdGFibGU+DQo8L2Rpdj4NCjwv dGQ+DQo8L3RyPg0KPC90Ym9keT4NCjwvdGFibGU+DQo8L2Rpdj4NCjwvdGQ+DQo8L3RyPg0KPC90 Ym9keT4NCjwvdGFibGU+DQo8L2Rpdj4NCjwvdGQ+DQo8L3RyPg0KPC90Ym9keT4NCjwvdGFibGU+ DQo8L2Rpdj4NCjxwIGNsYXNzPSJNc29Ob3JtYWwiIGFsaWduPSJjZW50ZXIiIHN0eWxlPSJ0ZXh0 LWFsaWduOmNlbnRlcjt2ZXJ0aWNhbC1hbGlnbjp0b3AiPg0KPG86cD4mbmJzcDs8L286cD48L3A+ DQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KPHRhYmxlIGNsYXNzPSJNc29Ob3JtYWxUYWJsZSIgYm9y ZGVyPSIwIiBjZWxsc3BhY2luZz0iMCIgY2VsbHBhZGRpbmc9IjAiIHdpZHRoPSIxMDAlIiBzdHls ZT0id2lkdGg6MTAwLjAlO0JPUkRFUi1TUEFDSU5HOiAwIj4NCjx0Ym9keT4NCjx0cj4NCjx0ZCB2 YWxpZ249InRvcCIgc3R5bGU9ImJhY2tncm91bmQ6I0YyRjJGNTtwYWRkaW5nOjBjbSAwY20gMGNt IDBjbSI+DQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KPHRhYmxlIGNsYXNzPSJNc29Ob3JtYWxUYWJs ZSIgYm9yZGVyPSIwIiBjZWxsc3BhY2luZz0iMCIgY2VsbHBhZGRpbmc9IjAiIHdpZHRoPSI1ODAi IHN0eWxlPSJ3aWR0aDo0MzUuMHB0O0JPUkRFUi1TUEFDSU5HOiAwIj4NCjx0Ym9keT4NCjx0cj4N Cjx0ZCB3aWR0aD0iNTgwIiB2YWxpZ249InRvcCIgc3R5bGU9IndpZHRoOjQzNS4wcHQ7cGFkZGlu ZzowY20gMGNtIDBjbSAwY20iPg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCjx0YWJsZSBjbGFzcz0i TXNvTm9ybWFsVGFibGUiIGJvcmRlcj0iMCIgY2VsbHNwYWNpbmc9IjAiIGNlbGxwYWRkaW5nPSIw IiB3aWR0aD0iNTgwIiBzdHlsZT0id2lkdGg6NDM1LjBwdCI+DQo8dGJvZHk+DQo8dHI+DQo8dGQg d2lkdGg9IjU4MCIgdmFsaWduPSJ0b3AiIHN0eWxlPSJ3aWR0aDo0MzUuMHB0O3BhZGRpbmc6MGNt IDBjbSAwY20gMGNtIj4NCjxkaXYgYWxpZ249ImNlbnRlciI+DQo8dGFibGUgY2xhc3M9Ik1zb05v cm1hbFRhYmxlIiBib3JkZXI9IjAiIGNlbGxzcGFjaW5nPSIwIiBjZWxscGFkZGluZz0iMCIgd2lk dGg9IjEwMCUiIHN0eWxlPSJ3aWR0aDoxMDAuMCUiPg0KPHRib2R5Pg0KPHRyPg0KPHRkIHN0eWxl PSJwYWRkaW5nOjBjbSAwY20gMGNtIDBjbSI+PC90ZD4NCjwvdHI+DQo8L3Rib2R5Pg0KPC90YWJs ZT4NCjwvZGl2Pg0KPC90ZD4NCjwvdHI+DQo8L3Rib2R5Pg0KPC90YWJsZT4NCjwvZGl2Pg0KPC90 ZD4NCjwvdHI+DQo8L3Rib2R5Pg0KPC90YWJsZT4NCjwvZGl2Pg0KPC90ZD4NCjwvdHI+DQo8L3Ri b2R5Pg0KPC90YWJsZT4NCjwvZGl2Pg0KPHAgY2xhc3M9Ik1zb05vcm1hbCIgYWxpZ249ImNlbnRl ciIgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyO3ZlcnRpY2FsLWFsaWduOnRvcCI+DQo8bzpwPiZu YnNwOzwvbzpwPjwvcD4NCjxkaXYgYWxpZ249ImNlbnRlciI+DQo8dGFibGUgY2xhc3M9Ik1zb05v cm1hbFRhYmxlIiBib3JkZXI9IjAiIGNlbGxzcGFjaW5nPSIwIiBjZWxscGFkZGluZz0iMCIgd2lk dGg9IjEwMCUiIHN0eWxlPSJ3aWR0aDoxMDAuMCU7Qk9SREVSLVNQQUNJTkc6IDAiPg0KPHRib2R5 Pg0KPHRyPg0KPHRkIHZhbGlnbj0idG9wIiBzdHlsZT0iYmFja2dyb3VuZDp3aGl0ZTtwYWRkaW5n OjBjbSAwY20gMGNtIDBjbSI+DQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KPHRhYmxlIGNsYXNzPSJN c29Ob3JtYWxUYWJsZSIgYm9yZGVyPSIwIiBjZWxsc3BhY2luZz0iMCIgY2VsbHBhZGRpbmc9IjAi IHdpZHRoPSI1ODAiIHN0eWxlPSJ3aWR0aDo0MzUuMHB0O0JPUkRFUi1TUEFDSU5HOiAwIj4NCjx0 Ym9keT4NCjx0cj4NCjx0ZCB3aWR0aD0iNTgwIiB2YWxpZ249InRvcCIgc3R5bGU9IndpZHRoOjQz NS4wcHQ7cGFkZGluZzowY20gMGNtIDBjbSAwY20iPg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCjx0 YWJsZSBjbGFzcz0iTXNvTm9ybWFsVGFibGUiIGJvcmRlcj0iMCIgY2VsbHNwYWNpbmc9IjAiIGNl bGxwYWRkaW5nPSIwIiB3aWR0aD0iNTgwIiBzdHlsZT0id2lkdGg6NDM1LjBwdCI+DQo8dGJvZHk+ DQo8dHI+DQo8dGQgd2lkdGg9IjU4MCIgdmFsaWduPSJ0b3AiIHN0eWxlPSJ3aWR0aDo0MzUuMHB0 O3BhZGRpbmc6MGNtIDBjbSAwY20gMGNtIj4NCjxkaXYgYWxpZ249ImNlbnRlciI+DQo8dGFibGUg Y2xhc3M9Ik1zb05vcm1hbFRhYmxlIiBib3JkZXI9IjAiIGNlbGxzcGFjaW5nPSIwIiBjZWxscGFk ZGluZz0iMCIgd2lkdGg9IjEwMCUiIHN0eWxlPSJ3aWR0aDoxMDAuMCUiPg0KPHRib2R5Pg0KPHRy Pg0KPHRkIHZhbGlnbj0idG9wIiBzdHlsZT0icGFkZGluZzoxMi4wcHQgMTIuMHB0IDEyLjBwdCAx Mi4wcHQiPg0KPHAgc3R5bGU9Im1zby1tYXJnaW4tdG9wLWFsdDoxLjVwdDttYXJnaW4tcmlnaHQ6 MGNtO21hcmdpbi1ib3R0b206MTguMHB0O21hcmdpbi1sZWZ0OjBjbTttc28tbGluZS1oZWlnaHQt YWx0OjkuMHB0Ij4NCjxzcGFuIHN0eWxlPSJmb250LXNpemU6MTYuNXB0O2ZvbnQtZmFtaWx5OiZx dW90O0hlbHZldGljYSZxdW90OyxzYW5zLXNlcmlmO2NvbG9yOiMwMDlDREUiPldlIG5lZWQgeW91 ciBoZWxwIHRvIHJlc29sdmUgdW5hdXRob3JpemVkIGFjdGl2aXRlczxvOnA+PC9vOnA+PC9zcGFu PjwvcD4NCjxwIHN0eWxlPSJtc28tbWFyZ2luLXRvcC1hbHQ6MTAuNXB0O21hcmdpbi1yaWdodDow Y207bWFyZ2luLWJvdHRvbToxOC4wcHQ7bWFyZ2luLWxlZnQ6MGNtO2xpbmUtaGVpZ2h0OjkuMHB0 Ij4NCjxzcGFuIHN0eWxlPSJmb250LXNpemU6OS4wcHQ7Zm9udC1mYW1pbHk6JnF1b3Q7SGVsdmV0 aWNhJnF1b3Q7LHNhbnMtc2VyaWY7Y29sb3I6Izg4ODg4OCI+RGVhciBDbGllbnQsPG86cD48L286 cD48L3NwYW4+PC9wPg0KPHAgc3R5bGU9Im1zby1tYXJnaW4tdG9wLWFsdDoxMC41cHQ7bWFyZ2lu LXJpZ2h0OjBjbTttYXJnaW4tYm90dG9tOjE1LjBwdDttYXJnaW4tbGVmdDowY207bGluZS1oZWln aHQ6MTMuNXB0Ij4NCjxzcGFuIHN0eWxlPSJmb250LXNpemU6OS4wcHQ7Zm9udC1mYW1pbHk6JnF1 b3Q7SGVsdmV0aWNhJnF1b3Q7LHNhbnMtc2VyaWY7Y29sb3I6Izg4ODg4OCI+V2UganVzdCB3YW50 ZWQgdG8gY29uZmlybSB0aGF0IHlvdSd2ZSBjaGFuZ2VkIHlvdXIgcGFzc3dvcmQuIElmIHlvdSBk aWRuJ3QgbWFrZSB0aGlzIGNoYW5nZSwgcGxlYXNlIGNoZWNrIGluZm9ybWF0aW9uIGluIGhlcmUu IEl0J3MgaW1wb3J0YW50IHRoYXQgeW91IGxldCB1cyBrbm93IGJlY2F1c2UgaXQgaGVscHMNCiB1 cyBwcmV2ZW50IHVuYXV0aG9yaXNlZCBwZXJzb25zIGZyb20gYWNjZXNzaW5nIHRoZSBQYXlQYWwg bmV0d29yayBhbmQgeW91ciBhY2NvdW50IGluZm9ybWF0aW9uLjxvOnA+PC9vOnA+PC9zcGFuPjwv cD4NCjxwIHN0eWxlPSJtc28tbWFyZ2luLXRvcC1hbHQ6OS4wcHQ7bWFyZ2luLXJpZ2h0OjBjbTtt YXJnaW4tYm90dG9tOjE4LjBwdDttYXJnaW4tbGVmdDowY207bGluZS1oZWlnaHQ6MTMuNXB0Ij4N CjxzcGFuIHN0eWxlPSJmb250LXNpemU6OS4wcHQ7Zm9udC1mYW1pbHk6JnF1b3Q7SGVsdmV0aWNh JnF1b3Q7LHNhbnMtc2VyaWY7Y29sb3I6Izg4ODg4OCI+V2UndmUgbm90aWNlZCBzb21lIGNoYW5n ZXMgdG8geW91ciB1bnN1YWwgc2VsbGluZyBhY3Rpdml0aWVzIGFuZCB3aWxsIG5lZWQgc29tZSBt b3JlIGluZm9ybWF0aW9uIGFib3V0IHlvdXIgcmVjZW50IHNhbGVzLjxvOnA+PC9vOnA+PC9zcGFu PjwvcD4NCjxkaXYgYWxpZ249ImNlbnRlciI+DQo8dGFibGUgY2xhc3M9Ik1zb05vcm1hbFRhYmxl IiBib3JkZXI9IjAiIGNlbGxzcGFjaW5nPSIwIiBjZWxscGFkZGluZz0iMCIgc3R5bGU9IkJPUkRF Ui1TUEFDSU5HOiAwIj4NCjx0Ym9keT4NCjx0cj4NCjx0ZCBzdHlsZT0iYmFja2dyb3VuZDojMDA5 Q0RFO3BhZGRpbmc6OS4wcHQgMTguMHB0IDkuMHB0IDE4LjBwdDstd2Via2l0LWJvcmRlci1yYWRp dXM6IDRweDtib3JkZXItcmFkaXVzOiA0cHgiPg0KPHAgY2xhc3M9Ik1zb05vcm1hbCIgYWxpZ249 ImNlbnRlciIgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyIj48c3BhbiBzdHlsZT0iZm9udC1zaXpl OjEwLjBwdDtmb250LWZhbWlseTomcXVvdDtIZWx2ZXRpY2EmcXVvdDssc2Fucy1zZXJpZiI+PGEg aHJlZj0iaHR0cDovL293Lmx5L3FlTDMzMGRZek8yIj48Yj48c3BhbiBzdHlsZT0iY29sb3I6d2hp dGU7dGV4dC1kZWNvcmF0aW9uOm5vbmUiPkNvbmZpcm0geW91ciBlbWFpbDwvc3Bhbj48L2I+PC9h PjxvOnA+PC9vOnA+PC9zcGFuPjwvcD4NCjwvdGQ+DQo8L3RyPg0KPC90Ym9keT4NCjwvdGFibGU+ DQo8L2Rpdj4NCjxwIHN0eWxlPSJtc28tbWFyZ2luLXRvcC1hbHQ6MTIuMHB0O21hcmdpbi1yaWdo dDowY207bWFyZ2luLWJvdHRvbToxOC4wcHQ7bWFyZ2luLWxlZnQ6MGNtO2xpbmUtaGVpZ2h0OjEz LjVwdCI+DQo8c3BhbiBzdHlsZT0iZm9udC1zaXplOjkuMHB0O2ZvbnQtZmFtaWx5OiZxdW90O0hl bHZldGljYSZxdW90OyxzYW5zLXNlcmlmO2NvbG9yOiM4ODg4ODgiPlRoYW5rIHlvdSBmb3IgeW91 ciB1bmRlcnN0YW5kaW5nIGFuZCBjb29wZXJhdGlvbi4gSWYgeW91IG5lZWQgZnVydGhlciBhc3Np c3RhbmNlLCBwbGVhc2UgY2xpY2sgQ29udGFjdCBhdCB0aGUgYm90dG9tIG9mIGFueSBQYXlQYWwg cGFnZS48bzpwPjwvbzpwPjwvc3Bhbj48L3A+DQo8cCBzdHlsZT0ibXNvLW1hcmdpbi10b3AtYWx0 OjEyLjBwdDttYXJnaW4tcmlnaHQ6MGNtO21hcmdpbi1ib3R0b206OS4wcHQ7bWFyZ2luLWxlZnQ6 MGNtO2xpbmUtaGVpZ2h0OjE3LjI1cHQiPg0KPHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTo5LjBwdDtm b250LWZhbWlseTomcXVvdDtIZWx2ZXRpY2EmcXVvdDssc2Fucy1zZXJpZjtjb2xvcjojODg4ODg4 Ij5TaW5jZXJlbHksPG86cD48L286cD48L3NwYW4+PC9wPg0KPHAgc3R5bGU9Im1zby1tYXJnaW4t dG9wLWFsdDo3LjVwdDttYXJnaW4tcmlnaHQ6MGNtO21hcmdpbi1ib3R0b206OS4wcHQ7bWFyZ2lu LWxlZnQ6MGNtO2xpbmUtaGVpZ2h0OjE3LjI1cHQiPg0KPHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTo5 LjBwdDtmb250LWZhbWlseTomcXVvdDtIZWx2ZXRpY2EmcXVvdDssc2Fucy1zZXJpZjtjb2xvcjoj ODg4ODg4Ij5QYXlQYWw8bzpwPjwvbzpwPjwvc3Bhbj48L3A+DQo8L3RkPg0KPC90cj4NCjwvdGJv ZHk+DQo8L3RhYmxlPg0KPC9kaXY+DQo8L3RkPg0KPC90cj4NCjwvdGJvZHk+DQo8L3RhYmxlPg0K PC9kaXY+DQo8L3RkPg0KPC90cj4NCjwvdGJvZHk+DQo8L3RhYmxlPg0KPC9kaXY+DQo8L3RkPg0K PC90cj4NCjwvdGJvZHk+DQo8L3RhYmxlPg0KPC9kaXY+DQo8cCBjbGFzcz0iTXNvTm9ybWFsIiBh bGlnbj0iY2VudGVyIiBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7dmVydGljYWwtYWxpZ246dG9w Ij4NCjxvOnA+Jm5ic3A7PC9vOnA+PC9wPg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCjx0YWJsZSBj bGFzcz0iTXNvTm9ybWFsVGFibGUiIGJvcmRlcj0iMCIgY2VsbHNwYWNpbmc9IjAiIGNlbGxwYWRk aW5nPSIwIiB3aWR0aD0iMTAwJSIgc3R5bGU9IndpZHRoOjEwMC4wJTtCT1JERVItU1BBQ0lORzog MCI+DQo8dGJvZHk+DQo8dHI+DQo8dGQgdmFsaWduPSJ0b3AiIHN0eWxlPSJib3JkZXI6bm9uZTti b3JkZXItdG9wOnNvbGlkICNEOERERTQgMS4wcHQ7YmFja2dyb3VuZDojRjJGMkY1O3BhZGRpbmc6 MGNtIDBjbSAxMi4wcHQgMGNtOy13ZWJraXQtYm9yZGVyLXJhZGl1czogMCAwIDRweCA0cHg7Ym9y ZGVyLXJhZGl1czogMCAwIDRweCA0cHgiPg0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCjx0YWJsZSBj bGFzcz0iTXNvTm9ybWFsVGFibGUiIGJvcmRlcj0iMCIgY2VsbHNwYWNpbmc9IjAiIGNlbGxwYWRk aW5nPSIwIiB3aWR0aD0iNTgwIiBzdHlsZT0id2lkdGg6NDM1LjBwdDstd2Via2l0LWJvcmRlci1y YWRpdXM6IDAgMCA0cHggNHB4O2JvcmRlci1yYWRpdXM6IDAgMCA0cHggNHB4Ij4NCjx0Ym9keT4N Cjx0cj4NCjx0ZCB3aWR0aD0iNTgwIiB2YWxpZ249InRvcCIgc3R5bGU9IndpZHRoOjQzNS4wcHQ7 cGFkZGluZzowY20gMGNtIDBjbSAwY207Qk9SREVSLVNQQUNJTkc6IDAiPg0KPGRpdiBhbGlnbj0i Y2VudGVyIj4NCjx0YWJsZSBjbGFzcz0iTXNvTm9ybWFsVGFibGUiIGJvcmRlcj0iMCIgY2VsbHNw YWNpbmc9IjAiIGNlbGxwYWRkaW5nPSIwIiB3aWR0aD0iNTgwIiBzdHlsZT0id2lkdGg6NDM1LjBw dCI+DQo8dGJvZHk+DQo8dHI+DQo8dGQgd2lkdGg9IjM5MCIgdmFsaWduPSJ0b3AiIHN0eWxlPSJ3 aWR0aDoyOTIuNXB0O3BhZGRpbmc6MGNtIDBjbSAwY20gMGNtIj4NCjxkaXYgYWxpZ249ImNlbnRl ciI+DQo8dGFibGUgY2xhc3M9Ik1zb05vcm1hbFRhYmxlIiBib3JkZXI9IjAiIGNlbGxzcGFjaW5n PSIwIiBjZWxscGFkZGluZz0iMCIgd2lkdGg9IjEwMCUiIHN0eWxlPSJ3aWR0aDoxMDAuMCUiPg0K PHRib2R5Pg0KPHRyPg0KPHRkIHZhbGlnbj0idG9wIiBzdHlsZT0icGFkZGluZzo5LjBwdCAxMi4w cHQgMGNtIDEyLjBwdCI+DQo8cCBjbGFzcz0iTXNvTm9ybWFsIj48c3BhbiBzdHlsZT0iZm9udC1z aXplOjcuNXB0O2ZvbnQtZmFtaWx5OiZxdW90O0hlbHZldGljYSZxdW90OyxzYW5zLXNlcmlmIj48 YSBocmVmPSJodHRwOi8vcGF5cGFsLmNvbSI+PHNwYW4gc3R5bGU9ImNvbG9yOiMwMDlDREUiPkhl bHANCjwvc3Bhbj48L2E+fCA8YSBocmVmPSJodHRwOi8vcGF5cGFsLmNvbSI+PHNwYW4gc3R5bGU9 ImNvbG9yOiMwMDlDREUiPlJlc29sdXRpb24gPC9zcGFuPg0KPC9hPnwgPGEgaHJlZj0iaHR0cDov L3BheXBhbC5jb20iPjxzcGFuIHN0eWxlPSJjb2xvcjojMDA5Q0RFIj5TZWN1cml0eSBDZW50cmU8 L3NwYW4+PC9hPg0KPG86cD48L286cD48L3NwYW4+PC9wPg0KPHA+PHNwYW4gc3R5bGU9ImZvbnQt c2l6ZTo3LjVwdDtmb250LWZhbWlseTomcXVvdDtIZWx2ZXRpY2EmcXVvdDssc2Fucy1zZXJpZiI+ UGxlYXNlIGRvIG5vdCByZXBseSB0byB0aGlzIGVtYWlsLiBUbyBnZXQgaW4gdG91Y2ggd2l0aCB1 cywgY2xpY2sNCjxhIGhyZWY9Imh0dHA6Ly9wYXlwYWwuY29tIj48c3BhbiBzdHlsZT0iY29sb3I6 IzAwOUNERSI+SGVscCAmYW1wOyBDb250YWN0Ljwvc3Bhbj48L2E+PG86cD48L286cD48L3NwYW4+ PC9wPg0KPHAgY2xhc3M9Ik1zb05vcm1hbCI+PHNwYW4gc3R5bGU9ImZvbnQtc2l6ZTo3LjVwdDtm b250LWZhbWlseTomcXVvdDtIZWx2ZXRpY2EmcXVvdDssc2Fucy1zZXJpZiI+PGJyPg0KQ29weXJp Z2h0IMKpIDE5OTkg4oCTIDIwMTcgUGF5UGFsLiBBbGwgcmlnaHRzIHJlc2VydmVkLiA8YnI+DQo8 YnI+DQpDb25zdW1lciBhZHZpc29yeSAtIFBheVBhbCBQdGUuIEx0ZC4gdGhlIGhvbGRlciBvZiBQ YXlQYWwncyBzdG9yZWQgdmFsdWUgZmFjaWxpdHksIGRvZXMgbm90IHJlcXVpcmUgdGhlIGFwcHJv dmFsIG9mIHRoZSBNb25ldGFyeSBBdXRob3JpdHkgb2YgU2luZ2Fwb3JlLiBVc2VycyBhcmUgYWR2 aXNlZCB0byByZWFkIHRoZSB0ZXJtcyBhbmQgY29uZGl0aW9ucyBjYXJlZnVsbHkuPGJyPg0KPGJy Pg0KUGF5UGFsIFBQWDAwMTg4OToyMTNlZTFiZjYxODdlIDxvOnA+PC9vOnA+PC9zcGFuPjwvcD4N CjwvdGQ+DQo8L3RyPg0KPC90Ym9keT4NCjwvdGFibGU+DQo8L2Rpdj4NCjwvdGQ+DQo8dGQgd2lk dGg9IjE5MCIgdmFsaWduPSJ0b3AiIHN0eWxlPSJ3aWR0aDoxNDIuNXB0O3BhZGRpbmc6MGNtIDBj bSAwY20gMGNtIj48L3RkPg0KPC90cj4NCjwvdGJvZHk+DQo8L3RhYmxlPg0KPC9kaXY+DQo8L3Rk Pg0KPC90cj4NCjwvdGJvZHk+DQo8L3RhYmxlPg0KPC9kaXY+DQo8L3RkPg0KPC90cj4NCjwvdGJv ZHk+DQo8L3RhYmxlPg0KPC9kaXY+DQo8L3RkPg0KPC90cj4NCjwvdGJvZHk+DQo8L3RhYmxlPg0K PC9kaXY+DQo8cCBjbGFzcz0iTXNvTm9ybWFsIj48bzpwPiZuYnNwOzwvbzpwPjwvcD4NCjwvZGl2 Pg0KPC9ib2R5Pg0KPC9odG1sPg0K --_000_AM4PR0901MB1219E3152878394D9F48EFB982BC0AM4PR0901MB1219_-- --_004_AM4PR0901MB1219E3152878394D9F48EFB982BC0AM4PR0901MB1219_ Content-Type: image/jpeg; name="image001.jpg" Content-Description: image001.jpg Content-Disposition: inline; filename="image001.jpg"; size=500; creation-date="Sat, 29 Jul 2017 20:36:56 GMT"; modification-date="Sat, 29 Jul 2017 20:36:56 GMT" Content-ID: Content-Transfer-Encoding: base64 X-Microsoft-Exchange-Diagnostics: 1;DB6P192MB0231;27:wPFlVOQ8iMAXT+N6591mBRfClfXdhMLmsxh/Y7d1LVbWrHPkVIxvj9+3g1ttW81Vi7Xh16wwzAIakEj5LqQ7s3r7Kr3/lVOL94eT9Ky+Tm+IqokcaiD5Q9lLzBrvUIz0CJDh27WR+m3qUDN87HH0jA== X-Microsoft-Antispam-Mailbox-Delivery: abwl:0;wl:0;pcwl:0;kl:0;iwl:0;ijl:0;dwl:0;dkl:0;rwl:0;ex:0;auth:1;dest:I;WIMS-SenderIP:40.92.69.49;WIMS-SPF:windowslive%2ecom;WIMS-DKIM:windowslive%2ecom;WIMS-822:trojak3d%40windowslive%2ecom;WIMS-PRA:trojak3d%40windowslive%2ecom;WIMS-AUTH:PASS;ENG:(400001000128)(400125000095)(5062000261)(5061607266)(5061608174)(4900095)(4921089)(4950112)(4990090)(102400140)(400001001223)(400125100095)(61617095)(400001002128)(400125200095); /9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIf IiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9Pjv/wAALCAB4AHgBAREA/8QAHwAAAQUBAQEB AQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1Fh ByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZ WmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APZqKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK/9k= --_004_AM4PR0901MB1219E3152878394D9F48EFB982BC0AM4PR0901MB1219_--";
preg_match($regex_html,$email,$return);
print_r($return);
and heres my function:
function multipart_msg_body($inbox, $email_number){
$structure = imap_fetchstructure($inbox, $email_number);
$encoding_type = array("base64","quoted-printable","8bit","7bit","binary");
$message_body_parts = array();
if (array_key_exists(1,$structure->parameters)){
$type = $structure->parameters[1]->value;
if ($type == "multipart/alternative"){
$message = imap_body($inbox, $email_number);
foreach($encoding_type as $type){
$regex_html = '/(?<=Content-Type: text\/html;).*?(Content-Transfer-Encoding:).*?('.$type.').*?(\);).(.*?)(?= --_)/';
$regex_plain = '/(?<=Content-Type: text\/plain;).*?(Content-Transfer-Encoding:).*?('.$type.').*?(\);).(.*?)(?= --_)/';
$body_html = preg_match($regex_html,$message,$html_match);
$body_plain = preg_match($regex_plain,$message,$plain_match);
print_r($html_match);
if ($body_html){
$message_body_parts["html_".$type] = $html_match;
}
if ($body_plain){
$message_body_parts["plain_".$type] = $body_plain;
}
}
print_r($message_body_parts);
}
}
}
Solved:
Different regex was required as imap_body() and print_r(imap_body()) seem to generate a different string to work with.
function multipart_msg_body($inbox, $email_number){
$structure = imap_fetchstructure($inbox, $email_number);
$encoding_type = array("base64","quoted-printable","8bit","7bit","binary");
$message_body_parts = array();
if (array_key_exists(1,$structure->parameters)){
$type = $structure->parameters[1]->value;
if ($type == "multipart/alternative"){
$message = imap_body($inbox, $email_number, FT_INTERNAL);
foreach($encoding_type as $type){
$regex_html = "/(?<=Content-Type: text\/html;).*?(Content-Transfer-Encoding:).*?(".$type.").*?(\);)(.*?)(--_)/s";
$regex_plain = "/(?<=Content-Type: text\/plain;).*?(Content-Transfer-Encoding:).*?(".$type.").*?(\);)(.*?)(--_)/s";
$body_html = preg_match($regex_html,$message,$html_match);
$body_plain = preg_match($regex_plain,$message,$plain_match);
if ($body_html){
$message_body_parts["html_".$type] = $html_match[4];
}
if ($body_plain){
$message_body_parts["plain_".$type] = $plain_match[4];
}
}
return($message_body_parts);
}
}
}
Right, as I suspected, imap_body() is formed slightly different as a variable on its own to what the result of print_r(imap_body()), I've saved the variable into a file and ammended my regex to work with that, all works sweet now.
New regex:
$regex_html = "/(?<=Content-Type: text\/html;).*?(Content-Transfer-Encoding:).*?(".$type.").*?(\);)(.*?)(--_)/s";
hope this helps, a lot easier and possibly quicker way of finding the plain and html body of a multipart imap message. Thanks for all contributions.

PHP imap_fetchbody

I have been trying to fetch message but unsuccessful.
$body = imap_fetchbody($inbox, $email_id, 0);
the messages without attachments are good and I have output but with attachments
gives some complicated outputs out of which both html and plain message are encoded with some (Content-Type) which is a part of gmail messages
You can use the following code to get the plain text part of a multipart email body:
<?php
//get the body
$body = imap_fetchbody($inbox, $email_id, 0);
//parse the boundary separator
$matches = array();
preg_match('#Content-Type: multipart\/[^;]+;\s*boundary="([^"]+)"#i', $body, $matches);
list(, $boundary) = $matches;
$text = '';
if(!empty($boundary)) {
//split the body into the boundary parts
$emailSegments = explode('--' . $boundary, $body);
//get the plain text part
foreach($emailSegments as $segment) {
if(stristr($segment, 'Content-Type: text/plain') !== false) {
$text = trim(preg_replace('/Content-(Type|ID|Disposition|Transfer-Encoding):.*?\r\n/is', '', $segment));
break;
}
}
}
echo $text;
?>
$body = imap_fetchbody($inbox, $email_id, 1.0);
this seems to be the only one working for me. I think the first integer in the last parameter represents the section of the email, so if it starts with a zero it will contain all the header information. If it starts with a one then it contains the message information. Then the second integer followed by the period is the section of that section. So when I put zero it shows information, but when I put one or two it doesn't show anything for some emails.
This helped
$body = imap_fetchbody($inbox, $email_id, 1.1);

Email decoding doesn't work in zend mail

I have a script that access the specified email and fetches mail. $temp->getContent() echos the following..
----boundary_2710_edfb8b44-71c8-49ff-a8cb-88c83382c4ee
Content-Type: multipart/alternative;
boundary=--boundary_2709_dde0dd0e-ba35-4469-949d-5392aec65750 --boundary_2709_dde0dd0e-ba35-4469-949d-5392aec65750
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: base64
PGZvcm0gbWV0aG9k.........this part is base64 encoded and it works fine if i copy and decode it separately.......AgICAgICAgICAgDQoNCjwvZm9ybT4=
----boundary_2709_dde0dd0e-ba35-4469-949d-5392aec65750-- ----boundary_2710_edfb8b44-71c8-49ff-a8cb-88c83382c4ee
Content-Type: multipart/mixed; boundary=--boundary_2711_eca4cfc3-fc62-43d6-b9fb-e5295abbfbe8 ----boundary_2711_eca4cfc3-fc62-43d6-b9fb-e5295abbfbe8 Content-Type: application/pdf;
name=redBusTicket.pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment Content-ID: JVBERi0xLjIgCiXi48/TIAoxIDAgb2JqIAo8PCAKL1R5cGUgL0NhdGFsb2cgCi9QYWdlcyAy IDAgUiAKL1BhZ2VNb2RlIC9Vc2VOb25lIAovVmlld2VyUHJlZ
Between this content there is base64 encoded part and it works fine if i copy and decode it separately. Also there is a attachment in the mail. How can i get the attached file. The following is my code. when i use the base64_decode directly i get no output.. just a blank page..
$storage = new Zend_Mail_Storage_Imap($imap);
$temp = $storage->getMessage($_GET['mailid']);
echo base64_decode($temp->getContent());
the documentation in zend website is not very good. Need some help!!
It works good for me:
foreach ($mail as $message) {
$content = null;
foreach (new RecursiveIteratorIterator($message) as $part) {
if (strtok($part->contentType, ';') == 'text/plain') {
$content = $part;
break;
}
}
if ($content) {
echo "\n encode: " . $content->contentTransferEncoding;
echo "\n date: " . $message->date;
echo "\n subject: \n" . iconv_mime_decode($message->subject, 0, 'UTF-8');
echo "\n plain text part: \n" . mb_convert_encoding(base64_decode($content), 'UTF-8', 'KOI8-R');
}
}
I have something like this to get the base_64 contents from an email. Try to filter out what you dont need.
if ($email->isMultipart() && $partsCount){
for($i = 1; $i < $email->countParts() +1; $i++) {
$part = $email->getPart($i);
$headers = $part->getHeaders();
if (
array_key_exists('content-description', $headers)
|| array_key_exists('content-disposition', $headers)
){
if (array_key_exists('content-description', $headers)) {
$att = $part->getContent();
$filepath = utf8_encode(DATA_PATH . '/' . $part->getHeader('content-description'));
if (is_file($filepath)) {
unlink($filepath); // deletes previous files with same name
}
$file = fopen($filepath, "w");
fwrite($file, base64_decode($att));
fclose($file);
$attachments[] = $filepath;
}
}
}
}

Send Mail from raw body for testing purposes

I am developing a PHP application that needs to retrieve arbitrary emails from an email server. Then, the message is completely parsed and stored in a database.
Of course, I have to do a lot of tests as this task is not really trivial with all that different mail formats under the sun. Therefore I started to "collect" emails from certain clients and with different contents.
I would like to have a script so that I can send out those emails automatically to my application to test the mail handling.
Therefore, I need a way to send the raw emails - so that the structure is exactly the same as they would come from the respective client. I have the emails stored as .eml files.
Does somebody know how to send emails by supplying the raw body?
Edit:
To be more specific: I am searching for a way to send out multipart emails by using their source code. For example I would like to be able to use something like that (an email with plain and HTML part, HTML part has one inline attachment).
--Apple-Mail-159-396126150
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
The plain text email!
--=20
=20
=20
--Apple-Mail-159-396126150
Content-Type: multipart/related;
type="text/html";
boundary=Apple-Mail-160-396126150
--Apple-Mail-160-396126150
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html;
charset=iso-8859-1
<html><head>
<title>Daisies</title>=20
</head><body style=3D"background-attachment: initial; background-origin: =
initial; background-image: =
url(cid:4BFF075A-09D1-4118-9AE5-2DA8295BDF33/bg_pattern.jpg); =
background-position: 50% 0px; ">
[ - snip - the html email content ]
</body></html>=
--Apple-Mail-160-396126150
Content-Transfer-Encoding: base64
Content-Disposition: inline;
filename=bg_pattern.jpg
Content-Type: image/jpg;
x-apple-mail-type=stationery;
name="bg_pattern.jpg"
Content-Id: <4BFF075A-09D1-4118-9AE5-2DA8295BDF33/tbg.jpg>
/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAASAAA/+IFOElDQ19QUk9GSUxFAAEB
[ - snip - the image content ]
nU4IGsoTr47IczxmCMvPypi6XZOWKYz/AB42mcaD/9k=
--Apple-Mail-159-396126150--
Using PHPMailer, you can set the body of a message directly:
$mail->Body = 'the contents of one of your .eml files here'
If your mails contain any mime attachments, this will most likely not work properly, as some of the MIME stuff has to go into the mail's headers. You'd have to massage the .eml to extract those particular headers and add them to the PHPMailer mail as a customheader
You could just use the telnet program to send those emails:
$ telnet <host> <port> // execute telnet
HELO my.domain.com // enter HELO command
MAIL FROM: sender#address.com // enter MAIL FROM command
RCPT TO: recipient#address.com // enter RCPT TO command
<past here, without adding a newline> // enter the raw content of the message
[ctrl]+d // hit [ctrl] and d simultaneously to end the message
If you really want to do this in PHP, you can use fsockopen() or stream_socket_client() family. Basically you do the same thing: talking to the mailserver directly.
// open connection
$stream = #stream_socket_client($host . ':' . $port);
// write HELO command
fwrite($stream, "HELO my.domain.com\r\n");
// read response
$data = '';
while (!feof($stream)) {
$data += fgets($stream, 1024);
}
// repeat for other steps
// ...
// close connection
fclose($stream);
You can just use the build in PHP function mail for it. The body part doesnt have to be just text, it can also contain mixed part data.
Keep in mind that this is a proof of concept. The sendEmlFile function could use some more checking, like "Does the file exists" and "Does it have a boundry set". As you mentioned it is for testing/development, I have not included it.
<?php
function sendmail($body,$subject,$to, $boundry='') {
define ("CRLF", "\r\n");
//basic settings
$from = "Example mail<info#example.com>";
//define headers
$sHeaders = "From: ".$from.CRLF;
$sHeaders .= "X-Mailer: PHP/".phpversion().CRLF;
$sHeaders .= "MIME-Version: 1.0".CRLF;
//if you supply a boundry, it will be send with your own data
//else it will be send as regular html email
if (strlen($boundry)>0)
$sHeaders .= "Content-Type: multipart/mixed; boundary=\"".$boundry."\"".CRLF;
else
{
$sHeaders .= "Content-type: text/html;".CRLF."\tcharset=\"iso-8859-1\"".CRLF;
$sHeaders .= "Content-Transfer-Encoding: 7bit".CRLF."Content-Disposition: inline";
}
mail($to,$subject,$body,$sHeaders);
}
function sendEmlFile($subject, $to, $filename) {
$body = file_get_contents($filename);
//get first line "--Apple-Mail-159-396126150"
$boundry = $str = strtok($body, "\n");
sendmail($body,$subject,$to, $boundry);
}
?>
Update:
After some more testing I found that all .eml files are different. There might be a standard, but I had tons of options when exporting to .eml. I had to use a seperate tool to create the file, because you cannot save to .eml by default using outlook.
You can download an example of the mail script. It contains two versions.
The simple version has two files, one is the index.php file that sends the test.eml file. This is just a file where i pasted in the example code you posted in your question.
The advanced version sends an email using an actual .eml file I created. it will get the required headers from the file it self. Keep in mind that this also sets the To and From part of the mail, so change it to match your own/server settings.
The advanced code works like this:
<?php
function sendEmlFile($filename) {
//define a clear line
define ("CRLF", "\r\n");
//eml content to array.
$file = file($filename);
//var to store the headers
$headers = "";
$to = "";
$subject = "";
//loop trough each line
//the first part are the headers, until you reach a white line
while(true) {
//get the first line and remove it from the file
$line = array_shift($file);
if (strlen(trim($line))==0) {
//headers are complete
break;
}
//is it the To header
if (substr(strtolower($line), 0,3)=="to:") {
$to = trim(substr($line, 3));
continue;
}
//Is it the subject header
if (substr(strtolower($line), 0,8)=="subject:") {
$subject = trim(substr($line, 8));
continue;
}
$headers .= $line . CRLF;
}
//implode the remaining content into the body and trim it, incase the headers where seperated with multiple white lines
$body = trim(implode('', $file));
//echo content for debugging
/*
echo $headers;
echo '<hr>';
echo $to;
echo '<hr>';
echo $subject;
echo '<hr>';
echo $body;
*/
//send the email
mail($to,$subject,$body,$headers);
}
//initiate a test with the test file
sendEmlFile("Test.eml");
?>
You could start here
http://www.dreamincode.net/forums/topic/36108-send-emails-using-php-smtp-direct/
I have no idea how good that code is, but it would make a starting point.
What you are doing is connecting direct to port 25 on the remote machine, as you would with telnet, and issuing smtp commands. See eg http://www.yuki-onna.co.uk/email/smtp.html for what's going on (or see Jasper N. Brouwer's answer).
Just make a quick shell script which processes a directory and call it when you want e.g. using at crontab etc
for I in ls /mydir/ do cat I | awk .. | sendmail -options
http://www.manpagez.com/man/1/awk/
You could also just talk to the mail server using the script to send the emls with a templated body..
Edit: I have added the code to Github, for ease of use by other people. https://github.com/xrobau/smtphack
I realise I am somewhat necro-answering this question, but it wasn't answered and I needed to do this myself. Here's the code!
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
class SMTPHack
{
private $phpmailer;
private $smtp;
private $from;
private $to;
/**
* #param string $from
* #param string $to
* #param string $smtphost
* #return void
*/
public function __construct(string $from, string $to, string $smtphost = 'mailrx')
{
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->SMTPDebug = SMTP::DEBUG_SERVER;
$mail->SMTPAutoTLS = false;
$mail->Host = $smtphost;
$this->phpmailer = $mail;
$this->from = $from;
$this->to = $to;
}
/**
* #param string $helo
* #return SMTP
*/
public function getSmtp(string $helo = ''): SMTP
{
if (!$this->smtp) {
if ($helo) {
$this->phpmailer->Helo = $helo;
}
$this->phpmailer->smtpConnect();
$this->smtp = $this->phpmailer->getSMTPInstance();
$this->smtp->mail($this->from);
$this->smtp->recipient($this->to);
}
return $this->smtp;
}
/**
* #param string $data
* #param string $helo
* #param boolean $quiet
* #return void
* #throws \PHPMailer\PHPMailer\Exception
*/
public function data(string $data, string $helo = '', bool $quiet = true)
{
$smtp = $this->getSmtp($helo);
$prev = $smtp->do_debug;
if ($quiet) {
$smtp->do_debug = 0;
}
$smtp->data($data);
$smtp->do_debug = $prev;
}
}
Using that, you can simply beat PHPMailer into submission with a few simple commands:
$from = 'xrobau#example.com';
$to = 'fred#example.com';
$hack = new SMTPHack($from, $to);
$smtp = $hack->getSmtp('helo.hostname');
$errors = $smtp->getError();
// Assuming this is running in a phpunit test...
$this->assertEmpty($errors['error']);
$testemail = file_get_contents(__DIR__ . '/TestEmail.eml');
$hack->data($testemail);

accented words in email subject break spacing - how do I stop this?

We have a custom php email marketing app, and an interesting problem:
If the subject line of the message contains a word with accents, it 'swallows' the spaces between it and the following word.
An example: the phrase
Ángel Ríos escucha y sorprende
is shown (by at least gmail and lotus notes) as
ÁngelRíos escucha y sorprende
The particular line in the message source shows:
Subject: =?ISO-8859-1?Q?=C1ngel?= =?ISO-8859-1?Q?R=EDos?= escucha y sorprende
(semi-full headers):
Delivered-To: me#gmail.com
Received: {elided}
Return-Path: <return#path>
Received: {elided}
Received: (qmail 23734 invoked by uid 48); 18 Aug 2009 13:51:14 -0000
Date: 18 Aug 2009 13:51:14 -0000
To: "Adriano" <me#gmail.com>
Subject: =?ISO-8859-1?Q?=C1ngel?= =?ISO-8859-1?Q?R=EDos?= escucha y sorprende
MIME-Version: 1.0
From: {elided}
X-Mailer: PHP
X-Lista: 1290
X-ID: 48163
Content-Type: text/html; charset="ISO-8859-1"
Content-Transfer-Encoding: quoted-printable
Message-ID: <kokrte.rpq06m#example.com>
EDIT:
The app uses an old version of Html Mime Mail to prepare messages, I'll try to upgrade to a newer version. Anyway, this is the function that encodes the subject:
/**
* Function to encode a header if necessary
* according to RFC2047
*/
function _encodeHeader($input, $charset = 'ISO-8859-1')
{
preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $input, $matches);
foreach ($matches[1] as $value) {
$replacement = preg_replace('/([\x80-\xFF])/e', '"=" . strtoupper(dechex(ord("\1")))', $value);
$input = str_replace($value, '=?' . $charset . '?Q?' . $replacement . '?=', $input);
}
return $input;
}
And here it's the code where the subject is encoded:
if (!empty($this->headers['Subject'])) {
$subject = $this->_encodeHeader($this->headers['Subject'],
$this->build_params['head_charset']);
unset($this->headers['Subject']);
}
Wrap-up
The problem was that, indeed, the program wasn't encoding the space in the case mentioned. The accepted answer solved my problem, after a slight modification (mentioned in the comments to that answer) because the installed version of PHP didn't support a particular implementation detail.
Final answer
Although the accepted answer did solve the problem, we found that it, combined with many thousands of emails, was chewing all the available memory on the server. I checked the website of the original developer of this email framework, and found that the function had been updated to the following:
function _encodeHeader($input, $charset = 'ISO-8859-1') {
preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $input, $matches);
foreach ($matches[1] as $value) {
$replacement = preg_replace('/([\x80-\xFF])/e', '"=" . strtoupper(dechex(ord("\1")))', $value);
$input = str_replace($value, $replacement , $input);
}
if (!empty($matches[1])) {
$input = str_replace(' ', '=20', $input);
$input = '=?' . $charset . '?Q?' .$input . '?=';
}
return $input;
}
which neatly solved the problem and stayed under the mem limit.
You need to encode the space in between as well (see RFC 2047):
(=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=) (ab)
White space between adjacent 'encoded-word's is not displayed.
[…]
(=?ISO-8859-1?Q?a_b?=) (a b)
In order to cause a SPACE to be displayed within a portion of encoded text, the SPACE MUST be encoded as part of the 'encoded-word'.
(=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=) (a b)
In order to cause a SPACE to be displayed between two strings of encoded text, the SPACE MAY be encoded as part of one of the 'encoded-word's.
So this should do it:
Subject: =?ISO-8859-1?Q?=C1ngel=20R=EDos?= escucha y sorprende
Edit    Try this function:
function _encodeHeader($str, $charset='ISO-8859-1')
{
$words = preg_split('/(\s+)/', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$func = create_function('$match', 'return $match[0] === " " ? "_" : sprintf("=%02X", ord($match[0]));');
$encoded = false;
foreach ($words as $key => &$word) {
if (!ctype_space($word)) {
$tmp = preg_replace_callback('/[^\x21-\x3C\x3E-\x5E\x60-\x7E]/', $func, $word);
if ($tmp !== $word) {
if (!$encoded) {
$word = '=?'.$charset.'?Q?'.$tmp;
} else {
$word = $tmp;
if ($key > 0) {
$words[$key-1] = preg_replace_callback('/[^\x21-\x3C\x3E-\x5E\x60-\x7E]/', $func, $words[$key-1]);
}
}
$encoded = true;
} else {
if ($encoded) {
$words[$key-2] .= '?=';
}
$encoded = false;
}
}
}
if ($encoded) {
$words[$key] .= '?=';
}
return implode('', $words);
}
add
$input = str_replace('?', '=3F', $input);
in this fragment:
if (!empty($matches[1])) {
$input = str_replace('?', '=3F', $input);
$input = str_replace(' ', '=20', $input);
$input = '=?' . $charset . '?Q?' .$input . '?=';
}
Look up mbstring and UTF conversions. Many of the special characters in non-English languages are dealt with in the UTF8 character set.
Converting your subject string to UTF8 and ensuring that the email is sent as such should render the subject lines correctly.
At least it did for us when we had a similar issue sending email
It would appear you'd better send Subject: =?ISO-8859-1?Q?=C1ngel R=EDos escucha y sorprende?= , as the problem appears near the ?= encoding end.

Categories