PHP Extract binary data from header - php

I am working on my PHP to extract the binary data that I stored the email header in the mysql database. I need some help with extract the binary data to get the attachment binary data.
Example:
UEsDBBQAAAAIAEpZrEjPyoXURw....etc
I don't know how do you extract the binary data if you are looking for a filename, example: email_example1.zip.
Here is the header:
Return-Path: <sender#domain.com>
Delivered-To: chris#domain.com
Received: from domain.com
------=_Part_4094373_1508330616.1564422111167
Content-Type: application/zip
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="email_example1.zip"
Content-ID: <05063d19-5033-af14-87e2-d2fbf22d5857#yahoo.com>
UEsDBBQAAAAIAEpZrEjPyoXURw....etc
------=_Part_4094373_1508330616.1564422111167
Content-Type: application/zip
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="example2.zip"
Content-ID: <3b2c4fee-2b28-778b-b27f-c63881d64e17#domain.com>
UEsDBBQAAAAIALtk6U5W+XzU7iM....etc
Here is the PHP:
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
//Connect to the database
include('config.php');
$id = $_GET['id'];
$attid = $_GET['attid'];
$message_id = $_GET['msgid'];
$mailbox_sql = 'SELECT * FROM ' . $mailfolder . ' WHERE email_id = ? AND message_id = ?';
$mailbox = $link->prepare($mailbox_sql);
$mailbox->execute([$id,$message_id]);
// set the resulting array to associative
$row = $mailbox->fetch(PDO::FETCH_ASSOC);
if (is_array($row)) {
$attached = $row['attached_files'];
$attached_arr = explode("\n", $attached);
}
foreach ($attached_arr as $files) {
$attached_file = 'attid: ' . $attid . ' filename:';
$attached = '';
if (strpos($files, ' attid: ') !== false) {
$filename = trim(strrchr($files, ':'), ': ');
$files = 'attid: ' . $attid . ' filename: ' . $filename;
}
if (strpos($files, $attached_file) !== false) {
$attached = trim(strrchr($files, ':'), ': ');
}
mailbox = null;
?>
Do you know how I can search the filename in the email header to extract the binary data?
Any advice would be much appreicated.

You need get the input stream directly from the body because your content is application/zip Content-Type: application/zip .
To do that u can use php://input :
$input = file_get_contents('php://input');
$binary = base64ToBinary($input);
function base64ToBinary(string $string): string {
if (strpos($string, ';base64,') !== false) {
$string = explode(';base64,', $string)[1];
}
return base64_decode($string);
}
The file name is exposed via Content-Disposition Header:
In your post you have Content-Disposition: attachment; filename="email_example1.zip" you can just parse it from there.
EDIT
How to parse your file name from Header:
$value = $_SERVER['HTTP_CONTENT_DISPOSITION'];
$filename = 'default_name.zip';
if (preg_match('/filename="-(.*?)-"/', $value, $match) == 1) {
$filename = $match[1];
}
The code I provided works 100% for the Content Disposition Header you provided:
If it doesn't work for you that means the format you posted is wrong and check this Question for an answer on how to get file name from content disposition header .
Otherwise your request has nothing to do about Binary data it's parsing about parsing value from string. Please update your question description to match what you want to achieve.

Related

PHP code to extract certificate from file pdf.p7m

I need to extract the certificate from a signed PDF file with a .p7m extension. I have no problem doing it with xml.p7m file but I can't do it with the pdf.p7m file. Following part of the code (working) to extract the certificate from xml file
// I receive the certified array
function der2smime($file)
{
$to=<<<TXT
MIME-Version: 1.0
Content-Disposition: attachment; filename=“smime.p7m”
Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name=“smime.p7m”
Content-Transfer-Encoding: base64
\n
TXT;
$from=file_get_contents($file);
$to.=chunk_split(base64_encode($from));
return file_put_contents("smime.p7m-b6m",$to);
}
$file = "prova.pdf.p7m";
$filenobom = "pr.pdf.p7m";
$str = file_get_contents($file);
$str = str_replace("\xEF\xBB\xBF",'',$str);
file_put_contents($filenobom, $str);
$response = der2smime($filenobom);
$output = openssl_pkcs7_verify ("smime.p7m-b6m" , 0 , "pippo.crt" ,array("CA.pem"),"CA.pem","prova.pdf");
echo openssl_error_string();
$pemdata="pippo.crt";
$data = openssl_x509_parse(file_get_contents($pemdata));
echo openssl_error_string();
print_r($data);

my CSV attachment is empty! PHP [duplicate]

This question already has an answer here:
PHP attachment in email is empty
(1 answer)
Closed 9 years ago.
Im trying to take data from the database and display it in a CSV file which is both downloadable and emailed to someone. I have managed to get the downloadable file working and it displays all of the correct data. It also sends a CSV file to the necessary person but that CSV file is empty and no data is displayed in it.
Here is my code:
$myroot = "../../";
include($myroot . "inc/functions.php");
// output headers so that the file is downloaded rather than displayed
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=surveys.csv');
$output = fopen('php://output', 'w');
// Create CSV file
fputcsv($output, array('Name', 'Branch', 'Website','Company', 'Question1', 'Question2', 'Question3', 'Question4', 'Question5'));
$mysql_connection = db_connect_enhanced('*****','*****','*****','*****');
$query='SELECT * FROM *****.*****';
$surveys = db_query_into_array_enhanced($mysql_connection, $query);
$count = count($surveys);
$data = array();
for($i=0; $i<=$count; $i++){
$data[] = array($surveys[$i]['FeedbackName'], $surveys[$i]['BranchName'], $surveys[$i]['FeedbackWebsite'], $surveys[$i]['FeedbackCompany'], $surveys[$i]['Question1'], $surveys[$i]['Question2'], $surveys[$i]['Question3'], $surveys[$i]['Question4'], $surveys[$i]['Question5']);
}
foreach( $data as $row )
{
fputcsv($output, $row, ',', '"');
}
fclose($output);
$encoded = chunk_split(base64_encode($output));
// create the email and send it off
$subject = "File you requested from RRWH.com";
$from = "*****#*****.com";
$headers = 'MIME-Version: 1.0' . "\n";
$headers .= 'Content-Type: multipart/mixed;
boundary="----=_NextPart_001_0011_1234ABCD.4321FDAC"' . "\n";
$message = '
This is a multi-part message in MIME format.
------=_NextPart_001_0011_1234ABCD.4321FDAC
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
Hello
We have attached for you the PHP script that you requested from http://rrwh.com/scripts.php
as a zip file.
Regards
------=_NextPart_001_0011_1234ABCD.4321FDAC
Content-Type: application/octet-stream; name="';
$message .= "surveys.csv";
$message .= '"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="';
$message .= "surveys.csv";
$message .= '"
';
$message .= $encoded;
$message .= '
------=_NextPart_001_0011_1234ABCD.4321FDAC--
';
mail("*****#*****.com", $subject, $message, $headers, "-f$from");
I've spent a day and a half on this but I cant see the problem. Could someone please point it out to me as to why the attached CSV file is empty?
i'm getting kind of desperate and stressed out :( please someone help me.
base64_encode() expects the parameter to be a string and you give it a (closed) resource.
Try to read the resource into a string, or use file_get_contents or build your string while you write into the resource.
Update:
Try and replace
foreach( $data as $row )
{
fputcsv($output, $row, ',', '"');
}
fclose($output);
$encoded = chunk_split(base64_encode($output));
by
$myoutput = '"Name","Branch","Website","Company","Question1","Question2","Question3","Question4","Question5"';
foreach( $data as $row )
{
$myoutput .= "\"".implode('","',$row)."\"\n";
fputcsv($output, $row, ',', '"');
}
fclose($output);
$encoded = chunk_split(base64_encode($myoutput));
This way, everything you write into your output you also write into a new variable ($myoutput). Since this is a string you can use it with base64_encode().

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

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

#!/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');

Attachments with php's built-in SoapClient?

Is there a way I can add a soap attachment to a request using PHP's built-in SoapClient classes? It doesn't look like it's supported, but maybe I can manually build the mime boundaries? I know the PEAR SOAP library supports them, but in order to use that I have to rewrite my entire library to use it.
Why don't you just send files using Data URI scheme rather than implement SoapAttachment ? Here is an example :
Client
$client = new SoapClient(null, array(
'location' => "http://localhost/lab/stackoverflow/a.php?h=none",
'uri' => "http://localhost/",
'trace' => 1
));
// Method 1 Array
// File to upload
$file = "golf3.png";
// First Example
$data = array();
$data['name'] = $file;
$data['data'] = getDataURI($file, "image/png");
echo "Example 1: ";
echo ($return = $client->upload($data)) ? "File Uploaded : $return bytes" : "Error Uploading Files";
// Method 2 Objects
// File to upload
$file = "original.png";
// Second Example
$attachment = new ImageObj($file);
$param = new SoapVar($attachment, SOAP_ENC_OBJECT, "ImageObj");
$param = new SoapParam($param, "param");
echo "Example 2: ";
echo ($return = $client->uploadObj($attachment)) ? "File Uploaded : $return bytes" : "Error Uploading Files";
Output
Example 1: File Uploaded : 976182 bytes
Example 2: File Uploaded : 233821 bytes
Server
class UploadService {
public function upload($args) {
$file = __DIR__ . "/test/" . $args['name'];
return file_put_contents($file, file_get_contents($args['data']));
}
public function uploadObj($args) {
$file = __DIR__ . "/test/" . $args->name;
$data = sprintf("data://%s;%s,%s", $args->mime, $args->encoding, $args->data);
return file_put_contents($file, file_get_contents($data));
}
}
try {
$server = new SOAPServer(NULL, array(
'uri' => 'http://localhost/'
));
$server->setClass('UploadService');
$server->handle();
} catch (SOAPFault $f) {
print $f->faultstring;
}
Client Util
// Function Used
function getDataURI($image, $mime = '') {
return 'data: ' . (function_exists('mime_content_type') ?
mime_content_type($image) : $mime) . ';base64,' .
base64_encode(file_get_contents($image));
}
// Simple Image Object
class ImageObj{
function __construct($file, $mime = "") {
$this->file = $file;
$this->name = basename($file);
if (function_exists('mime_content_type')) {
$this->mime = mime_content_type($file);
} elseif (function_exists('finfo_open')) {
$this->mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file);
} else {
$this->mime = $mime;
}
$this->encoding = "base64";
$this->data = base64_encode(file_get_contents($file));
}
}
Yes, you can build the MIME component of the message using something like imap_mail_compose.
You'll need to construct a multipart message as they do in the first example, putting the XML from the $request parameter, from an overridden SoapClient::__doRequest method, into the first part of the MIME message.
Then you can do as others have shown in the first imap_mail_compose example to add one or more messages parts with attachments. These attachements can, but do not have to be base64 encoded, they can just as well be binary. The encoding for each part is specified by part-specific headers.
You'll also need to cook up an appropriate set of HTTP headers, per the SwA Document #Baba linked to earlier.
Once it's all said and done, you should have something looking like the examples from that document:
MIME-Version: 1.0
Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml;
start="<claim061400a.xml#claiming-it.com>"
Content-Description: This is the optional message description.
--MIME_boundary
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: 8bit
Content-ID: <claim061400a.xml#claiming-it.com>
<?xml version='1.0' ?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
..
<theSignedForm href="cid:claim061400a.tiff#claiming-it.com"/>
..
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
--MIME_boundary
Content-Type: image/tiff
Content-Transfer-Encoding: binary
Content-ID: <claim061400a.tiff#claiming-it.com>
...binary TIFF image...
--MIME_boundary--
And you can send that across the wire with the aforementioned overridden SoapClient::__doRequest method. Things I have noticed in trying to implement it myself thus far:
You may need to create an href URI reference from each SOAP node to the corresponding attachment, something like href="cid:claim061400a.tiff#claiming-it.com" above
You will need to extract the boundary component from the MIME content returned by imap_mail_compose for use in an HTTP Content-Type header
Don't forget the start component of the Content-Type header either, it should look something like this:
imap_mail_compose appears fairly minimal (but low hanging fruit), if it proves insufficient, consider Mail_Mime instead
Content-Type: Multipart/Related; boundary=MIME_boundary;
type=text/xml; start=""
Lastly, I'm not sure how evenly the various implementations of SwA are out there on the Internet... Suffice it to say, I've not been able to get an upload to a remote service with a crude implementation of what I've described above yet. It does seem like SwA is the typical SOAP attachment paradigm of choice though, from what I gather reading around on the net.

Categories