I'm using the following php script to receive and process emails, putting the various pieces into variables to handle later on.
#!/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;
}
}
Im wondering where would i start in order to accept a picture attachment and isolate that into a variable so i can process it however i needed to.
I would use MimeMailParse (http://code.google.com/p/php-mime-mail-parser/)
Then you could simply say
$parser = new MimeMailParser();
$parser->setStream(STDIN);
// Handle images
$path = '/tmp/';
$filename = '';
$attachments = $parser->getAttachments();
foreach ($attachments as $attachment) {
if (preg_match('/^image/', $attachment->content_type, $matches)) {
$pathinfo = pathinfo($attachment->filename);
$filename = $pathinfo['filename'];
if ($fp = fopen($path.$filename, 'w')) {
while ($bytes = $attachment->read()) {
fwrite($fp, $bytes);
}
fclose($fp);
}
}
}
You would need to do alot more than what you are doing. You have to detect the mime boundaries in the header, then find the multipart boundary and unbase64 the text. You would be much better off using a library for this sort of thing.
Related
I'm trying to use Mailchimp's Export API to generate a CSV file of all members of a given list. Here' the documentation and the example PHP code they give:
$apikey = 'YOUR_API_KEY';
$list_id = 'YOUR_LIST_ID';
$chunk_size = 4096; //in bytes
$url = 'http://us1.api.mailchimp.com/export/1.0/list?apikey='.$apikey.'&id='.$list_id;
/** a more robust client can be built using fsockopen **/
$handle = #fopen($url,'r');
if (!$handle) {
echo "failed to access url\n";
} else {
$i = 0;
$header = array();
while (!feof($handle)) {
$buffer = fgets($handle, $chunk_size);
if (trim($buffer)!=''){
$obj = json_decode($buffer);
if ($i==0){
//store the header row
$header = $obj;
} else {
//echo, write to a file, queue a job, etc.
echo $header[0].': '.$obj[0]."\n";
}
$i++;
}
}
fclose($handle);
}
This works well for me and when I run this file, I end up with a bunch of data in this format:
Email Address: xdf#example.com
Email Address: bob#example.com
Email Address: gerry#example.io
What I want to do is turn this into a CSV (to pass to a place on my server) instead of echoing the data. Is there a library or simple syntax/snippit I can use to make this happen?
If the format simply like:
Email Address, xdf#example.com
Email Address, bob#example.com
Email Address, gerry#example.io
is what you after, then you can do:
$handle = #fopen($url,'r');
$csvOutput = "";
if (!$handle) {
echo "failed to access url\n";
} else {
$i = 0;
$header = array();
while (!feof($handle)) {
$buffer = fgets($handle, $chunk_size);
if (trim($buffer)!=''){
$obj = json_decode($buffer);
if ($i==0){
//store the header row
$header = $obj;
} else {
//echo, write to a file, queue a job, etc.
echo $header[0].', '.$obj[0]."\n";
$csvOutput .= $header[0].', '.$obj[0]."\n";
}
$i++;
}
}
fclose($handle);
}
$filename = "data".date("m.d.y").".csv";
file_put_contents($filename, $csvOutput);
The variable $csvOutput contains the CSV format string.
This ones on me. From now on you might want to actually read some documentation instead of copying and pasting your way through life. other's will not be as nice as i am. here's a list of file system functions from the php website. http://php.net/manual/en/ref.filesystem.php Getting the file output to the desired csv format is an exercise left to the reader.
$apikey = 'YOUR_API_KEY';
$list_id = 'YOUR_LIST_ID';
$chunk_size = 4096; //in bytes
$url = 'http://us1.api.mailchimp.com/export/1.0/list?apikey='.$apikey.'&id='.$list_id;
/** a more robust client can be built using fsockopen **/
$handle = #fopen($url,'r');
if (!$handle) {
echo "failed to access url\n";
} else {
$i = 0;
$header = array();
$output = ''; //output buffer for the file we are going to write.
while (!feof($handle)) {
$buffer = fgets($handle, $chunk_size);
if (trim($buffer)!=''){
$obj = json_decode($buffer);
if ($i==0){
//store the header row
$header = $obj;
} else {
//write data into our output buffer for the file
$output .= $header[0].': '.$obj[0]."\n";
}
$i++;
}
}
fclose($handle);
//now write it to file
$path = '/path/to/where/you/want/to/store/file/';
$file_name = 'somefile.csv';
//create a file resource to write to.
$fh = fopen($path.$file_name,'w+');
//write to the file
fwrite($fh,$output);
//close the file
fclose($fh);
}
I am trying to add certain variables to couple of files which already have some content.
I am using file_get_contents to copy the contents of a particular file and then using file_put_contents to paste variable values along with the existing contents to that file.
The problem is that, on the first instance it works properly but to the second file it pastes everything that has been stored in the memory. It puts all the contents from the first file along with the contents of the second file.
Is there any way that I can clear the memory before the next file_get_contents executes. Or my concept is false here.
Here is my code...
<?php
if ($_POST["submit"]) {
$ip = $_POST['ip'];
$subnet = $_POST['subnet'];
$gateway = $_POST['gateway'];
$hostname = $_POST['hostname'];
$domain = $_POST['domain'];
$netbios = $_POST['netbios'];
$password = $_POST['password'];
$ipfile = 'one.txt';
$file = fopen($ipfile, "r");
$ipfileContents = fread($file, filesize($ipfile));
$ipcontent = "ip='$ip'\n";
$ipcontent .= "netmask='$subnet'\n";
$ipcontent .= "gw='$gateway'\n";
$conten = $ipcontent . $ipfileContents;
$file = fopen($ipfile, "w");
fwrite($file, $ipfileContents);
fclose($file);
$ipsh = shell_exec('sh path/to/CHANGE_IP.sh');
$hostfile = 'two.txt';
$fileh = fopen($hostfile, "r");
$hostfileContents = fread($fileh, filesize($hostfile));
$hostcontent = "ip='$ip'\n";
$hostcontent .= "m_name='$hostname'\n";
$hostcontent .= "fqdn='$domain'\n";
$conten = $hostcontent . $hostfileContents;
$fileh = fopen($hostfile, "w");
fwrite($fileh, $hostfileContents);
fclose($fileh);
$hostsh = shell_exec('sh path/to/MODIFY_HOSTS.sh');
}
?>
I have tried unset, but didn't work
$ipfilecontents->__destruct();
unset($ipfilecontents);
UPDATE:
file_get_contents & file_put_contents has some concurrency problems. So I had to change my method to fopen/fwrite/fclose and it worked flawlessly. Thanks for your help Jacinto.
if ($_POST["submit"]) {
$ip = $_POST['ip'];
$subnet = $_POST['subnet'];
$gateway = $_POST['gateway'];
$hostname = $_POST['hostname'];
$domain = $_POST['domain'];
$netbios = $_POST['netbios'];
$password = $_POST['password'];
$ipfile = 'one.txt';
$file = fopen($ipfile, "r");
$ipfileContents = fread($file, filesize($ipfile));
$ipcontent = "ip='$ip'\n";
$ipcontent .= "netmask='$subnet'\n";
$ipcontent .= "gw='$gateway'\n";
$content = $ipcontent . $ipfileContents;
$file = fopen($ipfile, "w");
fwrite($file, $content);
fclose($file);
$ipsh = shell_exec('sh path/to/CHANGE_IP.sh');
//do the same to the next file
}
This isn't an answer - I'll delete it in a minute. It's just a convenient place to show how to do trace statements:
$ipfile = 'one.txt';
$ipfileContents = file_get_contents($ipfile);
$ipcontent = "ip='$ip'\n";
$ipcontent .= "netmask='$subnet'\n";
$ipcontent .= "gw='$gateway'\n";
echo "DEBUG: hostcontent=<pre>$ipcontent</pre><br />====<br />hostfileContents=<pre>$ipfileContents</pre><br />\n";
file_put_contents($ipfile, $ipcontent . $ipfileContents, LOCK_EX);
$ipsh = shell_exec('sh path/to/CHANGE_IP.sh');
$hostfile = 'two.txt';
$hostfileContents = file_get_contents($hostfile);
$hostcontent = "ip='$ip'\n";
$hostcontent .= "m_name='$hostname'\n";
$hostcontent .= "fqdn='$domain'\n";
echo "DEBUG: hostcontent=<pre>$hostcontent</pre><br />====<br />hostfileContents=<pre>$hostfileContents</pre><br />\n";
file_put_contents($hostfile, $hostcontent . $hostfileContents, LOCK_EX);
$hostsh = shell_exec('sh path/to/MODIFY_HOSTS.sh');
The most efficient would be to read and write the file in chunks simultaneously:
open the file in read-write mode
read the data chunk that will be overwritten by the new data
reset pointer to begin of read chunk
write new data
make the read data the new data to be written
repeat until there is no data to write
Example:
$bufw = "ip=''\n";
$bufw .= "netmask=''\n";
$bufw .= "gw=''\n";
$bufw_len = strlen($bufw);
$file = fopen($ipfile, 'c+');
while ($bufw_len > 0) {
// read next chunk
$bufr = fread($file, $bufw_len);
$bufr_len = strlen($bufr);
// reset pointer to begin of chunk
fseek($file, -$bufr_len, SEEK_CUR);
// write previous chunk
fwrite($file, $bufw);
// update variables
$bufw = $bufr;
$bufw_len = strlen($bufw);
}
fclose($file);
With this the total memory usage is only up to the length of the new data to be written.
You can make it a function like:
function file_prepend_contents($filename, $data, $flags = 0, $context = null) {
if (!is_null($context)) {
$handle = fopen($filename, 'c+', ($flags & FILE_USE_INCLUDE_PATH) === FILE_USE_INCLUDE_PATH, $context);
} else {
$handle = fopen($filename, 'c+', ($flags & FILE_USE_INCLUDE_PATH) === FILE_USE_INCLUDE_PATH);
}
if (!$handle) return false;
if (($flags & LOCK_EX) === LOCK_EX) {
flock($handle, LOCK_EX);
}
$bytes_written = 0;
$bufw = $data;
$bufw_len = strlen($bufw);
while ($bufw_len > 0) {
// read next chunk
$bufr = fread($handle, $bufw_len);
$bufr_len = strlen($bufr);
// reset pointer
fseek($handle, -$bufr_len, SEEK_CUR);
// write current chunk
if (ftell($handle) === 0) {
$bytes_written = fwrite($handle, $bufw);
} else {
fwrite($handle, $bufw);
}
// update variables
$bufw = $bufr;
$bufw_len = strlen($bufw);
}
fclose($handle);
return $bytes_written;
}
I was looking for a practical way to detect file system changes. Than I found this pretty simple script from "Jonathan Franzone". But my problem is, it doesn't scan sub folders. Since I'm just a newbie in PHP, I would like to ask in here to have robust offers to solve.
Note: I made an extended search on site before writing. Many questions asked about this aproach to secure website. But no complete reply at all.
<?php
/**
* File : ftpMonitor.php
* Monitors a remote directory via FTP and emails a list of changes if any are
* found.
*
* #version June 4, 2008
* #author Jonathan Franzone
*/
// Configuration ///////////////////////////////////////////////////////////////
$host = 'ftp.domain.com';
$port = 21;
$user = 'username';
$pass = 'password';
$remote_dir = '/public_html';
$cache_file = 'ftp_cache';
$email_notify = 'your.email#gmail.com';
$email_from = 'email.from#gmail.com';
// Main Run Program ////////////////////////////////////////////////////////////
// Connect to FTP Host
$conn = ftp_connect($host, $port) or die("Could not connect to {$host}\n");
// Login
if(ftp_login($conn, $user, $pass)) {
// Retrieve File List
$files = ftp_nlist($conn, $remote_dir);
// Filter out . and .. listings
$ftpFiles = array();
foreach($files as $file)
{
$thisFile = basename($file);
if($thisFile != '.' && $thisFile != '..') {
$ftpFiles[] = $thisFile;
}
}
// Retrieve the current listing from the cache file
$currentFiles = array();
if(file_exists($cache_file))
{
// Read contents of file
$handle = fopen($cache_file, "r");
if($handle)
{
$contents = fread($handle, filesize($cache_file));
fclose($handle);
// Unserialize the contents
$currentFiles = unserialize($contents);
}
}
// Sort arrays before comparison
sort($currentFiles, SORT_STRING);
sort($ftpFiles, SORT_STRING);
// Perform an array diff to see if there are changes
$diff = array_diff($ftpFiles, $currentFiles);
if(count($diff) > 0)
{
// Email the changes
$msg = "<html><head><title>ftpMonitor Changes</title></head><body>" .
"<h1>ftpMonitor Found Changes:</h1><ul>";
foreach($diff as $file)
{
$msg .= "<li>{$file}</li>";
}
$msg .= "</ul>";
$msg .= '<em>Script by Jonathan Franzone</em>';
$msg .= "</body></html>";
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type: text/html; charset=iso-8859-1\r\n";
$headers .= "To: {$email_notify}\r\n";
$headers .= "From: {$email_from}\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
mail($email_notify, "ftpMonitor Changes Found", $msg, $headers);
}
// Write new file list out to cache
$handle = fopen($cache_file, "w");
fwrite($handle, serialize($ftpFiles));
fflush($handle);
fclose($handle);
}
else {
echo "Could not login to {$host}\n";
}
// Close Connection
ftp_close($conn);
?>
Thanks for anyone have a solution or at least try to help.
EDIT: Actually I was willing to ask for deleting automatically but, I dont want to be too much demanding. Why not if it will not be a pain.
I am piping an email to a program and running some code.
**
I know how to get the "From:" and the "Subject:" but how do I get only the body of the email?
**
#!/usr/bin/php -q
<?
$fd = fopen("php://stdin", "r");
while (!feof($fd)) {
$email .= fread($fd, 1024);
}
fclose($fd);
$lines = explode("\n", $email);
for ($i=0; $i < count($lines); $i++)
{
// look out for special headers
if (preg_match("/Subject:/", $lines[$i], $matches))
{
list($One,$Subject) = explode("Subject:", $lines[$i]);
list($Subject,$Gone) = explode("<", $Subject);
}
etc... HOW DO I GET THE BODY CONTENT OF THE EMAIL?
Basically, you want where the headers end, and to know if it's multipart or not so you can get the right portion(s) of the email.
Here is some information:
parsing raw email in php
Which says that the first double newline should be the beginning of the body of the email.
This page might give you some other ideas (see script below):
http://thedrupalblog.com/configuring-server-parse-email-php-script
#!/usr/bin/php
<?php
// fetch data from stdin
$data = file_get_contents("php://stdin");
// extract the body
// NOTE: a properly formatted email's first empty line defines the separation between the headers and the message body
list($data, $body) = explode("\n\n", $data, 2);
// explode on new line
$data = explode("\n", $data);
// define a variable map of known headers
$patterns = array(
'Return-Path',
'X-Original-To',
'Delivered-To',
'Received',
'To',
'Message-Id',
'Date',
'From',
'Subject',
);
// define a variable to hold parsed headers
$headers = array();
// loop through data
foreach ($data as $data_line) {
// for each line, assume a match does not exist yet
$pattern_match_exists = false;
// check for lines that start with white space
// NOTE: if a line starts with a white space, it signifies a continuation of the previous header
if ((substr($data_line,0,1)==' ' || substr($data_line,0,1)=="\t") && $last_match) {
// append to last header
$headers[$last_match][] = $data_line;
continue;
}
// loop through patterns
foreach ($patterns as $key => $pattern) {
// create preg regex
$preg_pattern = '/^' . $pattern .': (.*)$/';
// execute preg
preg_match($preg_pattern, $data_line, $matches);
// check if preg matches exist
if (count($matches)) {
$headers[$pattern][] = $matches[1];
$pattern_match_exists = true;
$last_match = $pattern;
}
}
// check if a pattern did not match for this line
if (!$pattern_match_exists) {
$headers['UNMATCHED'][] = $data_line;
}
}
?>
EDIT
Here is a PHP extension called MailParse:
http://pecl.php.net/package/mailparse
Somebody has built a class around it called MimeMailParse:
http://code.google.com/p/php-mime-mail-parser/
And here is a blog entry discussing how to use it:
http://www.bucabay.com/web-development/a-php-mime-mail-parser-using-mailparse-extension/
I have set email interception on my server.
following is my email forwarder set on server
testemail#my.server.com,"/home/server/php_pipe_mail.php"
following is my code for php_pipe_mail.php
#!/usr/bin/php -q
<?php
require_once('mimeDecode.php');
include('sql-connect.php');
error_reporting(E_ALL);
ob_start();
$raw_email = '';
if (!$stdin = fopen("php://stdin", "R"))
{
echo "ERROR: UNABLE TO OPEN php://stdin \n";
}
// ABLE TO READ THE MAIL
else
{
while (!feof($stdin))
{
$raw_email .= fread($stdin, 4096);
}
fclose($stdin);
}
$raw_email = preg_replace('/ +/', ' ', $raw_email);
var_dump($raw_email);
$buf = ob_get_contents();
$params['include_bodies'] = true;
$params['decode_bodies'] = true;
$params['decode_headers'] = true;
$params['input'] = $buf;
$params['crlf'] = "\r\n";
//Creating temp file on server
$myFile = "amail.txt";
$fh = fopen($myFile, 'w') or die("can't open file");
fwrite($fh, $buf);
fclose($fh);
//Generating mail structure in object format
$structure = Mail_mimeDecode::decode($params);
$attachment = array();
$mail_date= date( 'Y-m-d H:i:s', strtotime($structure->headers['date']) );
$from = $structure->headers['from'];
$to = $structure->headers['to'];
$subject = htmlentities($structure->headers['subject'],ENT_QUOTES);
if($structure->ctype_primary == "multipart")
{
$body_text = $structure->parts[0]->parts[0]->body;
$body_html = $structure->parts[0]->parts[1]->body;
$x = 0;
//fetch attachment
foreach ($structure->parts as $part) {
// only save if an attachment
if (isset($part->disposition) and ($part->disposition=='attachment')) {
$attachment[$x]["filename"] = $part->d_parameters['filename'];
$attachment[$x]["content_type"] = $part->ctype_primary . "/" . $part->ctype_secondary;
$attachment[$x]["body"] = addslashes($part->body);
$x++;
}
}
}
else
{
$body_text = $structure->parts[0]->body;
$body_html = $structure->parts[1]->body;
}
$qry1 = "insert into mail_buffer(mail_date,mail_from, mail_to,mail_subject,mail_text_body,mail_html_body) Values('". $mail_date ."','".$from."','".$to."','".$subject."','".$body_text."','".$body_html."')";
mysql_query($qry1) or die(mysql_error($con));
$last_id = mysql_insert_id();
if(count($attachment) > 0)
{
for($i=0; $i < count($attachment); $i++)
{
$qry = "insert into mail_attachment(email_id,content_type, file_name,body) Values('". $last_id ."','".$attachment[$i]['content_type']."','".$attachment[$i]['filename']."','".$attachment[$i]['body']."')";
mysql_query($qry) or die(mysql_error($con));
}
}
mysql_close($con);
ob_end_clean();
?>
Now above script works perfectly fine.
I am able to fetch message header, body and attachments and can store them in database without any problems.
When email without attachments come everything works fine and email is delivered to email address I am intercepting.
But following is not working.
When email with attachments comes than email content is being stored in database but email is not delivering to email address I am intercepting and I am getting following error message in bounce back email.
A message that you sent could not be delivered to one or more of its
recipients. This is a permanent error. The following address(es) failed:
pipe to |/home/server/php_pipe_mail.php
generated by testemail#my.server.com
Can anyone help me regarding the matter.
Thanks.
Could it be that, when an attachment is present, your script is echoing something? I have had problems piping emails before, and seen failure messages returned to senders, and they have been due to the piping script producing some kind of output. Maybe your error_reporting(E_ALL); is allowing the script to produce an output - try error_reporting(0);