I want to forward my bounced emails to a php script to deal with them. I am using.
#!/usr/bin/php -q
<?php
// 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];
}
} else {
// not a header, but message
$message .= $lines[$i]."\n";
}
if (trim($lines[$i])=="") {
// empty line, header section has ended
$splittingheaders = false;
}
}
?>
Works perfect! But how do I collect the "To" field in the bounced message? I've tried just adding a $to variable but it doesn't work.
Any help would be great,
thanks,
EDIT: Actually I need to get the "TO" field within the body of the message. - The email that it bounced back from. How do I pull apart the body of the message to take specific info? Should I create a special header with the person's email so that it is easier to get this info?
If you can create a custom header, that would be easiest. Otherwise, you need to match against your entire body for a particular pattern; and if your body text can vary, it could be difficult to make sure you are always matching the correct text.
Custom headers should begin with X-, so maybe do something like:
if (preg_match("/^X-Originally-To: (.*)/", $lines[$i], $matches)) {
$originallyto = $matches[1];
}
But with X- headers, they are non-standard, so it is best to pick a name that is either
Commonly used exclusively for the same purpose, or
Not likely to be used by anyone else at all
One other thing you should be aware of; lines in a message should always end in "\r\n", so you might want to split on both characters (instead of just "\n") to ensure more consistent behaviour.
Related
I have a strange issue. My SaaS receives leads from various third party companies via email (I know ... its old school). I use the pipe to a program feature in cpanel to send the email to a script that parses out the to from subject and message. In the message is a xml message. Here is the code for this (php):
$lines = explode("\n", $email_content);
// initialize variable which will assigned later on
$from = "";
$subject = "";
$headers = "";
$message = "";
$is_header= true;
//loop through each line
for ($i=0; $i < count($lines); $i++) {
if ($is_header) {
// hear information. instead of main message body, all other information are here.
$headers .= $lines[$i]."\n";
// Split out the subject portion
if (preg_match("/^Subject: (.*)/", $lines[$i], $matches)) {
$subject = $matches[1];
}
//Split out the sender information portion
if (preg_match("/^From: (.*)/", $lines[$i], $matches)) {
$from = $matches[1];
}
} else {
// content/main message body information
//$message .= $lines[$i]."\n";
$message .= $lines[$i];
}
if (trim($lines[$i])=="") {
// empty line, header section has ended
$is_header = false;
}
}
Then I just insert the variables into my MySQL database. For some of the emails this works perfect. But some emails the xml gets odd characters inserted. An example below
<?xml version=3D"1.0" encoding=3D"UTF-8"?>=0D=0A<?adf version=3D"1.0"?>==0D=0A<adf>
Notice the '3D' and '=0D=0A' .... I believe this may have to do with the encoding? Some are UTF 8, UTF 16 and the ones that work do not have a UTF assigned.
I am inserting the xml part into a 'TEXT' type as MySQL does not seem to have a XML option.
I am trying to mask emails. Basically give an email to a client like "RandomName#MyDomain.com" and have it forward "MyRealEmail#MyDomain.com".
I am pipe forwarding the emails to a php script on my server, where I want to use the "To" and "From" to find the real recipient of the message and forward the message to them removing any identifiable information from the Sender (From) section.
I can parse almost all the data right now from the header, but my problem is with the body. The html body portion can vary so much from different origins. Outlook have a <html> and <body> section, while Gmail just has <div>s. Regardless, I get these strange "=" signs in my raw email too, in both text and html sections, like <=div>!
I just want to change the "From" and "To" and keep the rest of the email pretty much exactly as it is so it doesn't have anomalies in its text or html section.
How can I do this? Should I just parse the raw email and change the occurrences of the emails? how can I send it then? or should I remake the email using phpmailer or some other class? how can i get the body correct then?
My hosting provider doesn't have MailParse extension installed, since I have seen some solutions on the site using that extension, so I am having to do this using available extensions in PHP 5.5
UPDATE
I managed to figure out the = issue, it was quoted-printable, so now I am calling quoted_printable_decode() to resolve that issue. Still trying to figure the best way to forward the email after altering the header though.
After a lot of failed attempts, finally have a solution I can live with. The host server didn't want to allow MailParse because it was an issue on their shared hosting environment, so I went with Mail_mimeDecode and Mail_MIME PEAR extensions.
// Read the message from STDIN
$fd = fopen("php://stdin", "r");
$input = "";
while (!feof($fd)) {
$input .= fread($fd, 1024);
}
fclose($fd);
$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$decoder = new Mail_mimeDecode($input);
$structure = $decoder->decode($params);
// get the header From and To email
$From = ExtractEmailAddress($structure->headers['from'])[0];
$To = ExtractEmailAddress($structure->headers['to'])[0];
$Subject = $structure->headers['subject'];
ExtractEmailAddress uses a solution from "In PHP, how do I extract multiple e-mail addresses from a block of text and put them into an array?"
For the Body I used the following to find the text and html portions:
$HTML = "";
$TEXT = "";
// extract email body details
foreach($structure as $K => $V){
if(is_array($V)){
foreach($V as $KK => $VV){
if(is_object($VV)){
$bodyHTML = false;
$bodyPLAIN = false;
foreach($VV as $KKK => $VVV){
if(!is_array($VVV)){
if($KKK === 'ctype_secondary'){
if($VVV === 'html') { $bodyHTML = true; }
if($VVV === 'plain') { $bodyPLAIN = true; }
}
if($KKK === 'body'){
if($bodyHTML){
$bodyHTML = false;
$HTML .= quoted_printable_decode($VVV);
}
if($bodyPLAIN){
$bodyPLAIN = false;
$TEXT .= quoted_printable_decode($VVV);
}
}
}
}
}
}
}
}
Finally, I had the parts I needed so I used Mail_MIME to get the message out. I do my database lookup logic here and find the real destination and masked From email address using the From and To I extracted from the header.
$mime = new Mail_mime(array('eol' => "\r\n"));
$mime->setTXTBody($TEXT);
$mime->setHTMLBody($HTML);
$mail = &Mail::factory('mail');
$hdrs = array(
'From' => $From,
'Subject' => $Subject
);
$mail->send($To, $mime->headers($hdrs), $mime->get());
I don't know if this will cover all cases of email bodies, but since my system is not using attachments I am ok for now.
Take not of quoted_printable_decode(), that how I fixed the issue with the = in the body.
The only issue is the delay in mail I am having now, but I'll deal with that
I'm trying to parse emails sent to "parsethis#mysite.com" with PHP (I will use a cronjob, but for now I am just hitting mysite.com/cronJobs/parseMail in my browser).
This is my first time trying to parse emails .. so I'm just not sure how to go about troubleshooting this at all.
Here is the code I'm using, found it on a site and it seemed to be something I can work from. (Yes I have replaced all the placeholders)
$mailbox = imap_open("{mysite.com:143/notls}INBOX", "parsethis#mysite.com", "123password"); //connects to mailbox on your server
if ($mailbox == false) {
echo "<p>Error: Can't open mailbox!</p>";
echo imap_last_error();
}else{
//Check number of messages
$num = imap_num_msg($mailbox);
//if there is a message in your inbox
if( $num > 0 ) { //this just reads the most recent email. In order to go through all the emails, you'll have to loop through the number of messages
$email = imap_fetchheader($mailbox, $num); //get email header
$lines = explode("\n", $email);
// data we are looking for
$from = "";
$subject = "";
$to = "";
$headers = "";
$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];
}
}
}
//We can just display the relevant information in our browser, like below or write some method, that will put that information in a database
echo "FROM: ".$from."<br>";
echo "TO: ".$to."<br>";
echo "SUBJECT: ".$subject."<br>";
echo "BODY: ".imap_qprint(imap_body($mailbox, $num));
//delete message
// imap_delete($mailbox,$num); // not while testing
// imap_expunge($mailbox); // not while testing
}else{
// echo "No more messages";
}
imap_close($mailbox);
}
Problem is : I get this when I hit it
FROM: "K.K.Smith"
TO: parsethis#mysite.com
SUBJECT: test subject
BODY: --50f9f846_140e0f76_3df1 Content-Type: // etc .. with the body of the email unformatted in a continuous string
// INTERESTING > I get the body twice .. doubled.. once as a long unformatted string, and then again with formatting
--50f9f846_140e0f76_3df1 Content-Type: text/html; // etc.. the rest of the body with formatting .. ie my signature is correctly formatted with spacing and line breaks unlike the first body output
/// ***and then this weird error at the end***
--50f9f846_140e0f76_3df1--
Fatal error: Exception thrown without a stack frame in Unknown on line 0
So I don't know what that means. I have googled and all results seem to indicate it is a "mysterious error". It looks like it is about to output the body for a third time (I don't really know what that weird string is.. an email id maybe?) .. but then it chokes where it didn't choke the two previous times.
.. can anyone give any ideas how I should move forward?
EDIT
so I reduced the parseMail function to just the minimum..
public function parseMail(){
$mailbox = imap_open("{mysite.com:143/notls}INBOX", "parsethis#mysite.com", "123password"); //connects to mailbox on your server
imap_close($mailbox);
}
and I still get the error when I hit it. Ideas?
SOLVED
It looks like it has something to do with Codeigniter interpreting errors/warnings thrown by the IMAP functions as exceptions.
My solution was to put this after my imap_close() function
imap_errors();
This got rid of the error notice for me.
Here is where i found my solution on the Kohana discussion board
i have read the mail in php with imap_fetchbody() and decode it with imap_qprint().
$body = imap_fetchbody($mbox, $no, 1);
$body = imap_qprint($body);
but there is difference between original mail and output mail
Original mail:
Plain Text
This is a testing mail to check
first second third fourth fifth sixth Home
this is yellow background
website: http://bizved.com
testing of quote
Thank you Hiren Manek Bhuved Solutions
output mail
Plain Text
This is a testing mail to check
first second third fourth fifth sixth Home
this is yellow background
website:�http://bizved.com
testing of�quote
Thank you �Hiren Manek Bhuved Solutions
can anybody gives solution?
thanks in advance.
I always had the same problem with imaps. I'dont guarantee for anything but you may want to give this a try:
function utf8_imap_header_decode($str)
{
$tmp = imap_mime_header_decode($str);
if (!mb_check_encoding($tmp[0]->text, 'UTF-8'))
return utf8_encode($tmp[0]->text);
return $tmp[0]->text;
}
function utf8_imap_body_decode($str)
{
return utf8_encode(quoted_printable_decode($str));
}
I have make the following solution as mail headers already its character set.
$st = imap_fetchstructure($mbox, $no);
$part = $st->parts[$partno];
$body = imap_fetchbody($mbox, $no, $partno);
$body = imap_qprint($body);
$charset = 'UTF-8';
if(!empty($part->parameters)){
for ($k = 0, $l = count($part->parameters); $k < $l; $k++) {
$attribute = $part->parameters[$k];
if($attribute->attribute == 'CHARSET'){
$charset = $attribute->value;
}
}
}
//echo $charset;
$bodytext = mb_convert_encoding($body,'UTF-8',$charset);
This not full solution. It's only for character encoding. Mail has different parts for plain text, html text, attachments, etc. for that you should have different handling for each type.
Hi I am wondering if someone could take a look at my code and see what error there is as I cant see any. what is happening is that it is not saving the emails I am sending it to the mysql, and instead I get a bounced email back.
When I run PHP test to see if it saves to the mysql table, it does not.
I have taken out the connect code as it has my username and password.
#!/usr/bin/php -q
<?php
mysql_connect("123.123.123.2", "abc_ard", "5555") or die(mysql_error());
mysql_select_db("55_servermail") or die(mysql_error());
chdir(dirname(__FILE__));
$fd = fopen("php://stdin", "r");
$email = "";
while (!feof($fd)) {
$email .= fread($fd, 1024);
}
fclose($fd);
if(strlen($email)<1) {
die();
}
// handle email
$lines = explode("\n", $email);
// empty vars
$from = "";
$to="";
$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;
}
}
mysql_query("INSERT INTO mail
(`to`,`from`,`header`,`subject`,`msg`,`original`)
VALUES
('{$to}','{$from}', '{$headers}','{$subject}','{$message}','{$email}')") or die(mysql_error());;
?>
When adding information to mysql you have to account for characters used by mysql and PHP and address them as they can cause code to fail, and even allow people to insertand/or execute code on your site. The simplest method I have used is to have PHP "escape" the characters to allow them to insert into mysql properly as follows:
$email = "This isn't my first time 'round here";
This should insert fine as is, but let's presume that your SQL is like this:
$query = "INSERT INTO table (timestamp,email) VALUES (time(),'$email')";
the single quotes in the MySQL query will be jacked up by the single quotes in your data. In order to avoid this set your email variable to be escaped:
$email = "This isn't my first time 'round here";
$email = mysql_escape_string($email);
The caviat to using this is that now your database has extra escape characters (typically a backslash "/") in your data, so when you want to use these from the database you will have to remove them and PHP has a handy function just for this:
$query = "SELECT * FROM table WHERE id='the id you want'";
$result = mysql_query($query) or die(mysql_error());
while ($data = mysql_fetch_array($result)) {
$email = stripslashes($data['email']);
}
echo "This is your email text from the database: <br>\n".$email."<br>\n";
Hope that helps to clarify one possible solution (if that is indeed why the email is not inserting into the database as expected).
the only error that I could possibly see in this current code is the possibility of entering characters like apostrophe(') please verify that if you want it to be included on the message body or other fields make sure to use the backslash() e.g (\') so that mysql will analyze it as a charater and not a closing apostrophe, but do have a security measure on doing that to avoid sql injection. :)