imap_open() in PHP is not handling UTF-8 - php

I am using the PHP imap functions to read a mailbox that receives UTF-8 encoded plain text email (generated by another server). Accented characters are replaced with question mark (?). Below is my code and following that are two attempts at fixing it. How do I fix the problem? I have no control over the server that generates the messages, but they claim they are encoded UTF-8. mb_detect_encoding says the imap_body function is returning an ASCII string, but I've found mb_detect_encoding to be somewhat buggy in the past.
$connection = imap_open( '{localhost:993/ssl/novalidate-cert}INBOX', 'xxxxxxx', 'xxxxxxx', 0, 1 );
$result = imap_search( $connection, 'UNSEEN' );
if ( $result )
{
foreach ( $result as $msgno )
{
$body = imap_body( $connection, $msgno );
// ... (code to process the message) ...
imap_mail_move( $connection, "$msgno:$msgno", 'INBOX.processed' );
}
imap_expunge( $connection );
imap_close( $connection );
}
}
I tried the following to convert to UTF-8, even though the message was already UTF-8:
$current_encoding = mb_detect_encoding( $body, 'auto' ); // Returns "ASCII"
$body = mb_convert_encoding( $body, $current_encoding, 'UTF-8' );
I also tried:
$body = mb_convert_encoding( $body, 'UTF-8', 'UTF-8' );

After getting the body of the email
$body = imap_body( $connection, $msgno );
Decode the Message Body by using code below
$body = utf8_decode(imap_utf8($body));
Use the same logic for $subject also..
For more details Refer imap_utf8

Here is the solution: I got hold of the people generating the email and they made the following header changes:
Content-type: text/html; charset=UTF-8
Content-transfer-encoding: quoted-printable
That solved it. My PHP program now sees the UTF-8 characters.

Related

Why does imap_fetchbody return cryptic data?

I have a script that is reading mails from a mail account and writing the content into files. This is working well besides there are some mails, that can not be read.
Here is some of the code
$mails = imap_search($imap,'UNSEEN');
foreach($mails as $mail)
{
$content_type = imap_fetchmime($imap,$mail,1);
$message = imap_fetchbody($imap,$mail,1);
//$message = imap_fetchbody($imap,$mail,1.1);
//$message = imap_fetchbody($imap,$mail,2);
$structure = imap_fetchstructure($imap, $mail);
if(isset($structure->parts[1]))
{
$part = $structure->parts[1];
}
$encoding = $part->encoding;
}
The problem is $message is a long cryptic string, that I am not able to decrypt, and all other variables are empty ($content_type, $structure, $encoding). I have tried all decryption methods I know, but had no success. When I change the option of imap_fetchbody to 1.1 or 2 I get nothing back.

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);

utf-8 charset not working on contact form with PEAR Mail_mime

I've found and tried various solutions given in other questions about utf-encoding for special characters and other related problems, but without any success.
I have an html contact form that sends information to my mail address with a simple php script using PEAR Mail mime. I can send information containing special characters from my test site on localhost with no problems, but not after I uploaded to my server.
eg message:
Test special characters: é è ç à ô
after being sent from server becomes:
Test special characters: é è ç à ô
I am guessing it's an encoding problem from my web server, but am kinda stuck at how to resolve the problem.
The meta tags in file containing the form are set to:
<meta charset="utf-8">
and the form is specified to accept charset utf-8:
<form name="contact" method="post" action="assets/send_form.php" accept-charset="UTF-8">
I've also tried sending content from php file with:
header("Content-Type: text/html; charset= UTF-8");
as well as creating $headers for the message with 'Content-Type' = 'text/html; charset="UTF-8".
The relevant php code in my script:
//This section creates the email headers
$headerss=array();
$headerss['From']= $from_address;
$headerss['To']= $siteEmail;
$headerss['Subject']= $email_subject;
$headerss['Return-Path']= $contactEmail;
$headerss['Date']= date("r");
$headerss['Content-Type'] = 'text/html; charset="UTF-8"';
// This section creates the smtp inputs
$auth = array('host' => $host, 'port' => $port, 'auth' => true, 'username' => $username, 'password' => $password);
// create new Mail_mime instance, set utf-8 charset
$mail = new Mail_mime();
$mail -> setHTMLBody($email_message);
//CHECK THIS OUT FOR UTF-8 *****************************
$mimeparams=array();
$mimeparams['text_encoding']="7bit";
$mimeparams['text_charset']="UTF-8";
$mimeparams['html_charset']="UTF-8";
$mimeparams['head_charset']="UTF-8";
$mimeparams['eol']= "\n" ;
$body = $mail->get($mimeparams);
$headers = $mail->headers($headerss);
// This section send the email
$smtp = Mail::factory('smtp', $auth);
$sendmail = $smtp->send($siteEmail, $headers, $body);
Any help will be really appreciated. Thanks.
Try to put before
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
I experienced the same problem and my solution was to save the php file in same format, in UTF-8 as I had saved in ANSI format. That solved my problem anyway.

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);

Problem with character encoding on email sent via PHP?

Having some trouble sending properly formatted HTML e-mail from a PHP script. I am running PHP 5.3.0 and Apache 2.2.11 on Windows XP Professional.
The output looks like this:
Agent Summary for Support on Tuesday April 20 2010=20
Ext. Name Time Volume
137 Agent Name 01:27:25 1
138 =09 00:00:00 0
139 =09 00:00:00 0
You see the =20 and =09 in there? If you look at the HTML you also see = signs being turned into =3D. I figure this is a character encoding issue as I read the following at Wikipedia:
ISO-8859-1 and Windows-1252 confusion
It is very common to mislabel text data with the charset label ISO-8859-1, even though the data is really Windows-1252 encoded. In Windows-1252, codes between 0x80 and 0x9F are used for letters and punctuation, whereas they are control codes in ISO-8859-1. Many web browsers and e-mail clients will interpret ISO-8859-1 control codes as Windows-1252 characters in order to accommodate such mislabeling but it is not standard behaviour and care should be taken to avoid generating these characters in ISO-8859-1 labeled content.
This looks like the problem but I don't know how to fix. My code looks like this:
ob_start();
report_queue_summary($yesterday,$yesterday,$first_extension,$last_extension,$queue);
$body_report = ob_get_contents();
ob_end_clean();
$body_footer = "This is an automatically generated e-mail.";
$message = new Mail_mime();
$html = $body_header.$body_report.$body_footer;
$message->setHTMLBody($html);
$body = $message->get();
$extraheaders = array("From"=>"***redacted***","To"=>$recipient, "Subject"=>"Agent Summary for $yesterday [$queue]", "Content-type"=>"text/html; charset=iso-8859-1");
$headers = $message->headers($extraheaders);
# setup e-mail;
$host = "*********";
$port = "26";
$username = "*****";
$password = "*****";
# Send e-mail
$smtp = Mail::factory('smtp',
array ('host' => $host,
'port' => $port,
'auth' => true,
'username' => $username,
'password' => $password));
$mail = $smtp->send($recipient, $extraheaders, $body);
if (PEAR::isError($mail)) {
echo("" . $mail->getMessage() . "");
} else {
echo("Message successfully sent!");
}
Is the problem that I'm using output buffering?
The problem is that you need the following header:
Content-Transfer-Encoding: quoted-printable

Categories