base64 in PHP Otp Library - php

I trying to make some simple library for encrypting files in PHP with OTP method. My problem is that some chars in decrypted code are different than original. I worked on it almost one week but without result. Is there problem with base64 chars or with encoding/decoding mechanism ?
Many thanks for the answers.
final class Otp
{
private static $charSet = array('+','/','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L',
'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r',
's','t','u','v','w','x','y','z');
public static function encryptFile($originalFilePath, $encryptedFilePath, $keyFilePath)
{
if(!self::existsFile($keyFilePath) || !self::existsFile($encryptedFilePath)) {
if($originalFileData = self::existsFile($originalFilePath)) {
$originalFileBase64Data = base64_encode($originalFileData);
$originalFileBase64DataLength = strlen($originalFileBase64Data) - 1;
$originalFileBase64DataArray = str_split($originalFileBase64Data);
$encryptedData = NULL;
$encryptedDataKey = NULL;
for ($i = 0; $i <= $originalFileBase64DataLength; $i++) {
$randKey = rand(0, sizeOf(self::$charSet) - 1);
$arrayKey = array_search($originalFileBase64DataArray[$i], self::$charSet);
if($randKey > $arrayKey) {
$str = '-' . ($randKey - $arrayKey);
} elseif($randKey < $arrayKey) {
$str = ($randKey + $arrayKey);
} else {
$str = $randKey;
}
$encryptedData .= self::$charSet[$randKey];
$encryptedDataKey .= $str. ';';
}
$encryptedDataString = $encryptedData;
$encryptedDataKeyString = $encryptedDataKey;
if(!self::existsFile($keyFilePath)) {
file_put_contents($keyFilePath, $encryptedDataKeyString);
}
if(!self::existsFile($encryptedFilePath)) {
file_put_contents($encryptedFilePath, $encryptedDataString);
}
return 'OK';
} else {
return 'Source file not exists';
}
} else {
return 'Encrypted data already exists';
}
}
public static function decryptFile($encryptedFilePath, $keyFilePath, $decryptedFilePath)
{
$keyFileData = self::existsFile($keyFilePath);
$encryptedFileData = self::existsFile($encryptedFilePath);
$encryptedFileDataLength = strlen($encryptedFileData) - 1;
if($encryptedFileData && $keyFileData) {
$encryptedFileDataArray = str_split($encryptedFileData);
$keyFileDataArray = explode(';', $keyFileData);
$decryptedData = NULL;
for ($i = 0; $i <= $encryptedFileDataLength; $i++) {
$poziciaaktualneho = array_search($encryptedFileDataArray[$i], self::$charSet);
$poziciasifrovana = $keyFileDataArray[$i];
if($poziciasifrovana < 0) {
$move = $poziciasifrovana + $poziciaaktualneho;
} elseif($poziciasifrovana > 0) {
$move = $poziciasifrovana - $poziciaaktualneho;
} else {
$move = '0';
}
$decryptedData .= self::$charSet[$move];
}
if(!self::existsFile($decryptedFilePath)) {
file_put_contents($decryptedFilePath, base64_decode($decryptedData));
return 'OK';
} else {
return 'Decrypted data already exists';
}
}
}
private static function existsFile($filePath)
{
$fileData = #file_get_contents($filePath);
if($fileData) {
return $fileData;
}
return FALSE;
}
}
$originalFilePath = 'original.jpg';
$keyFilePath = 'Otp_Key_' . $originalFilePath;
$encryptedFilePath = 'Otp_Data_' . $originalFilePath;
$decryptedFilePath = 'Otp_Decrypted_' . $originalFilePath;
echo Otp::encryptFile($originalFilePath, $encryptedFilePath, $keyFilePath);
echo Otp::decryptFile($encryptedFilePath, $keyFilePath, $decryptedFilePath);

The problem seems to be only happening when $poziciaaktualneho is equal to $poziciasifrovana and so by adding another if statement on line 78 to check for this and instead set $move equal to $poziciasifrovana I was able to fix the problem. The below script should work:
final class Otp
{
private static $charSet = array('+','/','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L',
'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r',
's','t','u','v','w','x','y','z');
public static function encryptFile($originalFilePath, $encryptedFilePath, $keyFilePath)
{
if(!self::existsFile($keyFilePath) || !self::existsFile($encryptedFilePath)) {
if($originalFileData = self::existsFile($originalFilePath)) {
$originalFileBase64Data = base64_encode($originalFileData);
$originalFileBase64DataLength = strlen($originalFileBase64Data) - 1;
$originalFileBase64DataArray = str_split($originalFileBase64Data);
$encryptedData = NULL;
$encryptedDataKey = NULL;
for ($i = 0; $i <= $originalFileBase64DataLength; $i++) {
$randKey = rand(0, sizeOf(self::$charSet) - 1);
$arrayKey = array_search($originalFileBase64DataArray[$i], self::$charSet);
if($randKey > $arrayKey) {
$str = '-' . ($randKey - $arrayKey);
} elseif($randKey < $arrayKey) {
$str = ($randKey + $arrayKey);
} else {
$str = $randKey;
}
$encryptedData .= self::$charSet[$randKey];
$encryptedDataKey .= $str. ';';
}
$encryptedDataString = $encryptedData;
$encryptedDataKeyString = $encryptedDataKey;
if(!self::existsFile($keyFilePath)) {
file_put_contents($keyFilePath, $encryptedDataKeyString);
}
if(!self::existsFile($encryptedFilePath)) {
file_put_contents($encryptedFilePath, $encryptedDataString);
}
return 'OK';
} else {
return 'Source file not exists';
}
} else {
return 'Encrypted data already exists';
}
}
public static function decryptFile($encryptedFilePath, $keyFilePath, $decryptedFilePath)
{
$keyFileData = self::existsFile($keyFilePath);
$encryptedFileData = self::existsFile($encryptedFilePath);
$encryptedFileDataLength = strlen($encryptedFileData) - 1;
if($encryptedFileData && $keyFileData) {
$encryptedFileDataArray = str_split($encryptedFileData);
$keyFileDataArray = explode(';', $keyFileData);
$decryptedData = NULL;
for ($i = 0; $i <= $encryptedFileDataLength; $i++) {
$poziciaaktualneho = array_search($encryptedFileDataArray[$i], self::$charSet);
$poziciasifrovana = $keyFileDataArray[$i];
if ($poziciasifrovana == $poziciaaktualneho) {
$move = $poziciasifrovana;
} elseif($poziciasifrovana < 0) {
$move = $poziciasifrovana + $poziciaaktualneho;
} elseif($poziciasifrovana > 0) {
$move = $poziciasifrovana - $poziciaaktualneho;
} else {
$move = '0';
}
$decryptedData .= self::$charSet[$move];
}
if(!self::existsFile($decryptedFilePath)) {
file_put_contents($decryptedFilePath, base64_decode($decryptedData));
return 'OK';
} else {
return 'Decrypted data already exists';
}
}
}
private static function existsFile($filePath)
{
$fileData = #file_get_contents($filePath);
if($fileData) {
return $fileData;
}
return FALSE;
}
}
$originalFilePath = 'original.jpg';
$keyFilePath = 'Otp_Key_' . $originalFilePath;
$encryptedFilePath = 'Otp_Data_' . $originalFilePath;
$decryptedFilePath = 'Otp_Decrypted_' . $originalFilePath;
echo Otp::encryptFile($originalFilePath, $encryptedFilePath, $keyFilePath);
echo Otp::decryptFile($encryptedFilePath, $keyFilePath, $decryptedFilePath);
Warning: I would not recommend using my solution in an enterprise setting if at all since I do not know why this fixes your script or what was
originally wrong with it and it is most likely not air tight.

Related

Printing prime numbers from an user's input php

I'm trying ta make a program that will ask the user for a number and then show all the prime numbers between 0 to this number in an array.
<?php
class SmallerPrimes{
public function __construct($file){
$this->input_filename = $file;
}
public function Main(){
$smaller_than = (int)$this->readString()[0];
$result = array();
/* YOUR CODE HERE */
function isPrime($n)
{
if ($n <= 1)
return false;
for ($i = 2; $i < $n; $i++)
if ($n % $i == 0)
return false;
return true;
}
function printPrime($n)
{
for ($i = 2; $i <= $n; $i++)
{
if (isPrime($i))
echo $i . " ";
}
}
$n = 7;
printPrime($n);
/*end of your code here */
return $result;
}
public function readString(){
$file = fopen($this->input_filename, "r");
$line = array();
while (!feof($file)){
array_push($line, str_replace(PHP_EOL, "", fgets($file)));
}
return $line;
}
}
$o = new SmallerPrimes($argv[1]);
echo implode(" ", $o->Main()) . PHP_EOL;
This code work, but with a $n fixed.
I don't know how to use an input who ask the user a number
It's a code who verify itself and show us when it's ok
The code that i have to complete is this one (we only have this at first):
class SmallerPrimes{
public function __construct($file){
$this->input_filename = $file;
}
public function Main(){
$smaller_than = (int)$this->readString()[0];
$result = array();
/* YOUR CODE HERE */
return $result;
}
public function readString(){
$file = fopen($this->input_filename, "r");
$line = array();
while (!feof($file)){
array_push($line, str_replace(PHP_EOL, "", fgets($file)));
}
return $line;
}
}
$o = new SmallerPrimes($argv[1]);
echo implode(" ", $o->Main()) . PHP_EOL;
This is your script:
<?php
class SmallerPrimes{
public function __construct($file){
$this->input_filename = $file;
}
public function Main(){
$smaller_than = (int)$this->readString()[0];
$result = array();
function isPrime($n) {
if ($n <= 1) return false;
for ($i = 2; $i < $n; $i++) if ($n % $i == 0) return false;
return true;
}
function printPrime($n) {
for ($i = 2; $i < $n; $i++) if (isPrime($i)) $result[] = $i;
return $result;
}
$result = printPrime($smaller_than);
return $result;
}
public function readString(){
$file = fopen($this->input_filename, "r");
$line = array();
while (!feof($file)){
array_push($line, str_replace(PHP_EOL, "", fgets($file)));
}
return $line;
}}
$o = new SmallerPrimes($argv[1]);
echo implode(" ", $o->Main()) . PHP_EOL;
function getSmallerPrimes($smaller_than) {
if ($smaller_than <= 2) {
return [];
}
$result = [2];
$sqrt = sqrt($smaller_than);
for ($n = 3; $n < $smaller_than; $n += 2) {
$isPrime = true;
foreach($result as $prime) {
if ($n % $prime == 0) {
$isPrime = false;
break;
}
if ($prime > $sqrt) {
break;
}
}
if ($isPrime) {
$result[] = $n;
}
}
return $result;
}
$input = 23;
echo implode(' ', getSmallerPrimes($input));
Result: 2 3 5 7 11 13 17 19

Create recursive function from array in php

I have a situation showed in the PHP code below, and I want to make a recursive function called check_recursive().
I have made the check_recursive() function below, but I want a recursive function, if it is possible.
Thank You!
$menu = '[{"id":"3|case_studies","children":[{"id":"2|case_studies","children":[{"id":"1|custom_links","children":[{"id":"2|faqe"}]}]}]},{"id":"11|klientet","children":[{"id":"8|klientet","children":[{"id":"7|klientet"}]}]},{"id":"9|klientet","children":[{"id":"10|klientet"}]},{"id":"4|klientet"}]';
$old_menu = json_decode($menu, true);
$new_menu = $this->check_recursive($old_menu);
function check_recursive($old_menu)
{
$i = 0;
$new_menu = [];
foreach ($old_menu as $menu_item)
{
if($name = $this->check_menu($menu_item['id']))
{
$new_menu[$i]['id'] = $menu_item['id'] . '|' . $name;
if(isset($menu_item['children']))
{
$e = 0;
foreach ($menu_item['children'] as $menu_item)
{
if($name = $this->check_menu($menu_item['id']))
{
$new_menu[$i]['children'][$e]['id'] = $menu_item['id'] . '|' . $name;
if(isset($menu_item['children']))
{
$y = 0;
foreach ($menu_item['children'] as $menu_item)
{
if($name = $this->check_menu($menu_item['id']))
{
$new_menu[$i]['children'][$e]['children'][$y]['id'] = $menu_item['id'] . '|' . $name;
if(isset($menu_item['children']))
{
$a = 0;
foreach ($menu_item['children'] as $menu_item)
{
if($name = $this->check_menu($menu_item['id']))
{
$new_menu[$i]['children'][$e]['children'][$y]['children'][$a]['id'] = $menu_item['id'] . '|' . $name;
}
$a++;
}
}
}
$y++;
}
}
}
$e++;
}
}
}
$i++;
}
return $new_menu;
}
function check_menu($string){
//Check if string exists in database
if($string){
return 'String exists';
}
return false;
}
I came up with this :
$menu = '[{"id":"3|case_studies","children":[{"id":"2|case_studies","children":[{"id":"1|custom_links","children":[{"id":"2|faqe"}]}]}]},{"id":"11|klientet","children":[{"id":"8|klientet","children":[{"id":"7|klientet"}]}]},{"id":"9|klientet","children":[{"id":"10|klientet"}]},{"id":"4|klientet"}]';
$old_menu = json_decode($menu, true);
$new_menu = check_recursive($old_menu);
function check_recursive($old_menu)
{
$new_menu = array();
foreach ($old_menu as $item) {
$name = check_menu($item['id']);
if($name){
$new_item = array(
'id' => $item['id'] . '|' . $name,
);
if(isset($item['children'])){
$new_item['children'] = check_recursive($item['children']);
}
$new_menu[] = $new_item;
}
}
return $new_menu;
}
function check_menu($string)
{
//Check if string exists in database
if ($string) {
return 'String exists';
}
return false;
}
Let me know if it suits your needs.

Amazon Scraper Script works on XAMPP Windows but not PHP5 Cli on Linux

I'm trying to scrape Amazon ASIN codes using the below code:
<?php
class Scraper {
const BASE_URL = "http://www.amazon.com";
private $categoryFile = "";
private $outputFile = "";
private $catArray;
private $currentPage = NULL;
private $asin = array();
private $categoriesMatched = 0;
private $categoryProducts = array();
private $pagesMatched = 0;
private $totalPagesMatched = 0;
private $productsMatched = 0;
public function __construct($categoryFile, $outputFile) {
$this->categoryFile = $categoryFile;
$this->outputFile = $outputFile;
}
public function run() {
$this->readCategories($this->categoryFile);
$this->setupASINArray($this->asin);
$x = 1;
foreach ($this->catArray as $cat) {
$this->categoryProducts["$x"] = 0;
if ($this->currentPage == NULL) {
$this->currentPage = $cat;
$this->scrapeASIN($this->currentPage, $x);
$this->pagesMatched++;
}
if ($this->getNextPageLink($this->currentPage)) {
do {
// next page found
$this->pagesMatched++;
$this->scrapeASIN($this->currentPage, $x);
} while ($this->getNextPageLink($this->currentPage));
}
echo "Category complete: $this->pagesMatched Pages" . "\n";
$this->totalPagesMatched += $this->pagesMatched;
$this->pagesMatched = 0;
$this->writeASIN($this->outputFile, $x);
$x++;
$this->currentPage = NULL;
$this->categoriesMatched++;
}
$this->returnStats();
}
private function readCategories($categoryFile) {
$catArray = file($categoryFile, FILE_IGNORE_NEW_LINES);
$this->catArray = $catArray;
}
private function setupASINArray($asinArray) {
$x = 0;
foreach ($this->catArray as $cat) {
$asinArray["$x"][0] = "$cat";
$x++;
}
$this->asin = $asinArray;
}
private function getNextPageLink($currentPage) {
$document = new DOMDocument();
$html = file_get_contents($currentPage);
#$document->loadHTML($html);
$xpath = new DOMXPath($document);
$element = $xpath->query("//a[#id='pagnNextLink']/#href");
if ($element->length != 0) {
$this->currentPage = self::BASE_URL . $element->item(0)->value;
return true;
} else {
return false;
}
}
private function scrapeASIN($currentPage, $catNo) {
$html = file_get_contents($currentPage);
$regex = '~(?:www\.)?ama?zo?n\.(?:com|ca|co\.uk|co\.jp|de|fr)/(?:exec/obidos/ASIN/|o/|gp/product/|(?:(?:[^"\'/]*)/)?dp/|)(B[A-Z0-9]{9})(?:(?:/|\?|\#)(?:[^"\'\s]*))?~isx';
preg_match_all($regex, $html, $asin);
foreach ($asin[1] as $match) {
$this->asin[$catNo-1][] = $match;
}
}
private function writeASIN($outputFile, $catNo) {
$fh = fopen($outputFile, "a+");
$this->fixDupes($catNo);
$this->productsMatched += (count($this->asin[$catNo-1]) - 1);
$this->categoryProducts["$catNo"] = (count($this->asin[$catNo-1]) - 1);
flock($fh, LOCK_EX);
$x = 0;
foreach ($this->asin[$catNo-1] as $asin) {
fwrite($fh, "$asin" . "\n");
$x++;
}
flock($fh, LOCK_UN);
fclose($fh);
$x -= 1;
echo "$x ASIN codes written to file" . "\n";
}
private function fixDupes($catNo) {
$this->asin[$catNo-1] = array_unique($this->asin[$catNo-1], SORT_STRING);
}
public function returnStats() {
echo "Categories matched: " . $this->categoriesMatched . "\n";
echo "Pages parsed: " . $this->totalPagesMatched . "\n";
echo "Products parsed: " . $this->productsMatched . "\n";
echo "Category breakdown:" . "\n";
$x = 1;
foreach ($this->categoryProducts as $catProds) {
echo "Category $x had $catProds products" . "\n";
$x++;
}
}
}
$scraper = new Scraper($argv[1], $argv[2]);
$scraper->run();
?>
But it works fine on XAMPP on Windows but not on Linux. Any ideas as to why this may be? Sometimes it scrapes 0 ASIN's to file, sometimes it only scrapes 1 page in a category of 400+ pages. But the output/functionality is totally fine in Windows/XAMPP.
Any thoughts would be greatly appreciated!
Cheers
- Bryce
So try to change this way, just to avoid the error messages:
private function readCategories($categoryFile) {
if (file_exists($categoryFile)) {
$catArray = file($categoryFile, FILE_IGNORE_NEW_LINES);
$this->catArray = $catArray;
} else {
echo "File ".$categoryFile.' not exists!';
$this->catArray = array();
}
}

php imap - get body and make plain text

I am using the PHP imap function to get emails from a POP3 mailbox and insert the data into a MySQL database.
Here is the PHP code:
$inbox = imap_open($hostname,$username,$password) or die('Cannot connect: ' . imap_last_error());
$emails = imap_search($inbox,'ALL');
if($emails)
{
$output = '';
rsort($emails);
foreach($emails as $email_number)
{
$header=imap_headerinfo($inbox,$email_number);
$from = $header->from[0]->mailbox . "#" . $header->from[0]->host;
$toaddress=$header->toaddress;
$replyto=$header->reply_to[0]->mailbox."#".$header->reply_to[0]->host;
$datetime=date("Y-m-d H:i:s",$header->udate);
$subject=$header->subject;
//remove the " from the $toaddress
$toaddress = str_replace('"','',$toaddress);
echo '<strong>To:</strong> '.$toaddress.'<br>';
echo '<strong>From:</strong> '.$from.'<br>';
echo '<strong>Subject:</strong> '.$subject.'<br>';
//get message body
$message = (imap_fetchbody($inbox,$email_number,1.1));
if($message == '')
{
$message = (imap_fetchbody($inbox,$email_number,1));
}
}
It works fine, however on some emails in the body I get = in between words, or =20 in between words. And other times the emails will just be blank even though they are not blank when sent.
This only happens when coming from certain emails.
How can I get round this and just make the email completely plain text?
This happens because the emails are normally Quoted-printable encoded. The = is a soft line break and =20 is a white space. I think, you could use quoted_printable_decode() on the message so it shows correctly. About the blank emails, I don't know, I would need more details.
Basically:
//get message body
$message = quoted_printable_decode(imap_fetchbody($inbox,$email_number,1.1));
$data = imap_fetchbody($this->imapStream, $Part->uid, $Part->path, FT_UID | FT_PEEK);
if ($Part->format === 'quoted-printable' && $data) {
$data = quoted_printable_decode($data);
}
This is required for mails with
Content-Transfer-Encoding: quoted-printable
But for mails with
Content-Transfer-Encoding: 8bit
simply imap_fetchbody is enough.
Above code was taken from a cake-php component created for fetching mails from mail boxes throgh IMAP.
I made an entire class some years ago, and I'm still using it when I need to get contents from emails. It will help you fetch all email bodies (sometimes you have html and plain text) in a readable format, and get all attached files, just ready to be saved somewhere or sent to a website user.
It is not really optimized so on a big mailbox you may have troubles; but the purpose of this class was to access emails in a readable format to put them on a widget of a website. I let you play with the sample below to get how it works.
ImapReader.class.php Here is the source code.
<?php
class ImapReader
{
private $host;
private $port;
private $user;
private $pass;
private $box;
private $box_list;
private $errors;
private $connected;
private $list;
private $deleted;
const FROM = 0;
const TO = 1;
const REPLY_TO = 2;
const SUBJECT = 3;
const CONTENT = 4;
const ATTACHMENT = 5;
public function __construct($host = null, $port = '143', $user = null, $pass = null)
{
$this->host = $host;
$this->port = $port;
$this->user = $user;
$this->pass = $pass;
$this->box = null;
$this->box_list = null;
$this->errors = array ();
$this->connected = false;
$this->list = null;
$this->deleted = false;
}
public function __destruct()
{
if ($this->isConnected())
{
$this->disconnect();
}
}
public function changeServer($host = null, $port = '143', $user = null, $pass = null)
{
if ($this->isConnected())
{
$this->disconnect();
}
$this->host = $host;
$this->port = $port;
$this->user = $user;
$this->pass = $pass;
$this->box_list = null;
$this->errors = array ();
$this->list = null;
return $this;
}
public function canConnect()
{
return (($this->connected == false) && (is_string($this->host)) && (!empty($this->host))
&& (is_numeric($this->port)) && ($this->port >= 1) && ($this->port <= 65535)
&& (is_string($this->user)) && (!empty($this->user)) && (is_string($this->pass)) && (!empty($this->pass)));
}
public function connect()
{
if ($this->canConnect())
{
$this->box = #imap_open("{{$this->host}:{$this->port}/imap/ssl/novalidate-cert}INBOX", $this->user,
$this->pass);
if ($this->box !== false)
{
$this->_connected();
}
else
{
$this->errors = array_merge($this->errors, imap_errors());
}
}
return $this;
}
public function boxList()
{
if (is_null($this->box_list))
{
$list = imap_getsubscribed($this->box, "{{$this->host}:{$this->port}}", "*");
$this->box_list = array ();
foreach ($list as $box)
{
$this->box_list[] = $box->name;
}
}
return $this->box_list;
}
public function fetchAllHeaders($mbox)
{
if ($this->isConnected())
{
$test = imap_reopen($this->box, "{$mbox}");
if (!$test)
{
return false;
}
$num_msgs = imap_num_msg($this->box);
$this->list = array ();
for ($id = 1; ($id <= $num_msgs); $id++)
{
$this->list[] = $this->_fetchHeader($mbox, $id);
}
return true;
}
return false;
}
public function fetchSearchHeaders($mbox, $criteria)
{
if ($this->isConnected())
{
$test = imap_reopen($this->box, "{$mbox}");
if (!$test)
{
return false;
}
$msgs = imap_search($this->box, $criteria);
if ($msgs)
{
foreach ($msgs as $id)
{
$this->list[] = $this->_fetchHeader($mbox, $id);
}
}
return true;
}
return false;
}
public function isConnected()
{
return $this->connected;
}
public function disconnect()
{
if ($this->connected)
{
if ($this->deleted)
{
imap_expunge($this->box);
$this->deleted = false;
}
imap_close($this->box);
$this->connected = false;
$this->box = null;
}
return $this;
}
/**
* Took from khigashi dot oang at gmail dot com at php.net
* with replacement of ereg family functions by preg's ones.
*
* #param string $str
* #return string
*/
private function _fix($str)
{
if (preg_match("/=\?.{0,}\?[Bb]\?/", $str))
{
$str = preg_split("/=\?.{0,}\?[Bb]\?/", $str);
while (list($key, $value) = each($str))
{
if (preg_match("/\?=/", $value))
{
$arrTemp = preg_split("/\?=/", $value);
$arrTemp[0] = base64_decode($arrTemp[0]);
$str[$key] = join("", $arrTemp);
}
}
$str = join("", $str);
}
if (preg_match("/=\?.{0,}\?Q\?/", $str))
{
$str = quoted_printable_decode($str);
$str = preg_replace("/=\?.{0,}\?[Qq]\?/", "", $str);
$str = preg_replace("/\?=/", "", $str);
}
return trim($str);
}
private function _connected()
{
$this->connected = true;
return $this;
}
public function getErrors()
{
$errors = $this->errors;
$this->errors = array ();
return $errors;
}
public function count()
{
if (is_null($this->list))
{
return 0;
}
return count($this->list);
}
public function get($nbr = null)
{
if (is_null($nbr))
{
return $this->list;
}
if ((is_array($this->list)) && (isset($this->list[$nbr])))
{
return $this->list[$nbr];
}
return null;
}
public function fetch($nbr = null)
{
return $this->_callById('_fetch', $nbr);
}
private function _fetchHeader($mbox, $id)
{
$header = imap_header($this->box, $id);
if (!is_object($header))
{
return;
}
$mail = new stdClass();
$mail->id = $id;
$mail->mbox = $mbox;
$mail->timestamp = (isset($header->udate)) ? ($header->udate) : ('');
$mail->date = date("d/m/Y H:i:s", (isset($header->udate)) ? ($header->udate) : (''));
$mail->from = $this->_fix(isset($header->fromaddress) ? ($header->fromaddress) : (''));
$mail->to = $this->_fix(isset($header->toaddress) ? ($header->toaddress) : (''));
$mail->reply_to = $this->_fix(isset($header->reply_toaddress) ? ($header->reply_toaddress) : (''));
$mail->subject = $this->_fix(isset($header->subject) ? ($header->subject) : (''));
$mail->content = array ();
$mail->attachments = array ();
$mail->deleted = false;
return $mail;
}
private function _fetch($mail)
{
$test = imap_reopen($this->box, "{$mail->mbox}");
if (!$test)
{
return $mail;
}
$structure = imap_fetchstructure($this->box, $mail->id);
if ((!isset($structure->parts)) || (!is_array($structure->parts)))
{
$body = imap_body($this->box, $mail->id);
$content = new stdClass();
$content->type = 'content';
$content->mime = $this->_fetchType($structure);
$content->charset = $this->_fetchParameter($structure->parameters, 'charset');
$content->data = $this->_decode($body, $structure->type);
$content->size = strlen($content->data);
$mail->content[] = $content;
return $mail;
}
else
{
$parts = $this->_fetchPartsStructureRoot($mail, $structure);
foreach ($parts as $part)
{
$content = new stdClass();
$content->type = null;
$content->data = null;
$content->mime = $this->_fetchType($part->data);
if ((isset($part->data->disposition))
&& ((strcmp('attachment', $part->data->disposition) == 0)
|| (strcmp('inline', $part->data->disposition) == 0)))
{
$content->type = $part->data->disposition;
$content->name = null;
if (isset($part->data->dparameters))
{
$content->name = $this->_fetchParameter($part->data->dparameters, 'filename');
}
if (is_null($content->name))
{
if (isset($part->data->parameters))
{
$content->name = $this->_fetchParameter($part->data->parameters, 'name');
}
}
$mail->attachments[] = $content;
}
else if ($part->data->type == 0)
{
$content->type = 'content';
$content->charset = null;
if (isset($part->data->parameters))
{
$content->charset = $this->_fetchParameter($part->data->parameters, 'charset');
}
$mail->content[] = $content;
}
$body = imap_fetchbody($this->box, $mail->id, $part->no);
if (isset($part->data->encoding))
{
$content->data = $this->_decode($body, $part->data->encoding);
}
else
{
$content->data = $body;
}
$content->size = strlen($content->data);
}
}
return $mail;
}
private function _fetchPartsStructureRoot($mail, $structure)
{
$parts = array ();
if ((isset($structure->parts)) && (is_array($structure->parts)) && (count($structure->parts) > 0))
{
foreach ($structure->parts as $key => $data)
{
$this->_fetchPartsStructure($mail, $data, ($key + 1), $parts);
}
}
return $parts;
}
private function _fetchPartsStructure($mail, $structure, $prefix, &$parts)
{
if ((isset($structure->parts)) && (is_array($structure->parts)) && (count($structure->parts) > 0))
{
foreach ($structure->parts as $key => $data)
{
$this->_fetchPartsStructure($mail, $data, $prefix . "." . ($key + 1), $parts);
}
}
$part = new stdClass;
$part->no = $prefix;
$part->data = $structure;
$parts[] = $part;
}
private function _fetchParameter($parameters, $key)
{
foreach ($parameters as $parameter)
{
if (strcmp($key, $parameter->attribute) == 0)
{
return $parameter->value;
}
}
return null;
}
private function _fetchType($structure)
{
$primary_mime_type = array ("TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "OTHER");
if ((isset($structure->subtype)) && ($structure->subtype) && (isset($structure->type)))
{
return $primary_mime_type[(int) $structure->type] . '/' . $structure->subtype;
}
return "TEXT/PLAIN";
}
private function _decode($message, $coding)
{
switch ($coding)
{
case 2:
$message = imap_binary($message);
break;
case 3:
$message = imap_base64($message);
break;
case 4:
$message = imap_qprint($message);
break;
case 5:
break;
default:
break;
}
return $message;
}
private function _callById($method, $data)
{
$callback = array ($this, $method);
// data is null
if (is_null($data))
{
$result = array ();
foreach ($this->list as $mail)
{
$result[] = $this->_callById($method, $mail);
}
return $result;
}
// data is an array
if (is_array($data))
{
$result = array ();
foreach ($data as $elem)
{
$result[] = $this->_callById($method, $elem);
}
return $result;
}
// data is an object
if ((is_object($data)) && ($data instanceof stdClass) && (isset($data->id)))
{
return call_user_func($callback, $data);
}
// data is numeric
if (($this->isConnected()) && (is_array($this->list)) && (is_numeric($data)))
{
foreach ($this->list as $mail)
{
if ($mail->id == $data)
{
return call_user_func($callback, $mail);
}
}
}
return null;
}
public function delete($nbr)
{
$this->_callById('_delete', $nbr);
return;
}
private function _delete($mail)
{
if ($mail->deleted == false)
{
$test = imap_reopen($this->box, "{$mail->mbox}");
if ($test)
{
$this->deleted = true;
imap_delete($this->box, $mail->id);
$mail->deleted = true;
}
}
}
public function searchBy($pattern, $type)
{
$result = array ();
if (is_array($this->list))
{
foreach ($this->list as $mail)
{
$match = false;
switch ($type)
{
case self::FROM:
$match = $this->_match($mail->from, $pattern);
break;
case self::TO:
$match = $this->_match($mail->to, $pattern);
break;
case self::REPLY_TO:
$match = $this->_match($mail->reply_to, $pattern);
break;
case self::SUBJECT:
$match = $this->_match($mail->subject, $pattern);
break;
case self::CONTENT:
foreach ($mail->content as $content)
{
$match = $this->_match($content->data, $pattern);
if ($match)
{
break;
}
}
break;
case self::ATTACHMENT:
foreach ($mail->attachments as $attachment)
{
$match = $this->_match($attachment->name, $pattern);
if ($match)
{
break;
}
}
break;
}
if ($match)
{
$result[] = $mail;
}
}
}
return $result;
}
private function _nmatch($string, $pattern, $a, $b)
{
if ((!isset($string[$a])) && (!isset($pattern[$b])))
{
return 1;
}
if ((isset($pattern[$b])) && ($pattern[$b] == '*'))
{
if (isset($string[$a]))
{
return ($this->_nmatch($string, $pattern, ($a + 1), $b) + $this->_nmatch($string, $pattern, $a, ($b + 1)));
}
else
{
return ($this->_nmatch($string, $pattern, $a, ($b + 1)));
}
}
if ((isset($string[$a])) && (isset($pattern[$b])) && ($pattern[$b] == '?'))
{
return ($this->_nmatch($string, $pattern, ($a + 1), ($b + 1)));
}
if ((isset($string[$a])) && (isset($pattern[$b])) && ($pattern[$b] == '\\'))
{
if ((isset($pattern[($b + 1)])) && ($string[$a] == $pattern[($b + 1)]))
{
return ($this->_nmatch($string, $pattern, ($a + 1), ($b + 2)));
}
}
if ((isset($string[$a])) && (isset($pattern[$b])) && ($string[$a] == $pattern[$b]))
{
return ($this->_nmatch($string, $pattern, ($a + 1), ($b + 1)));
}
return 0;
}
private function _match($string, $pattern)
{
return $this->_nmatch($string, $pattern, 0, 0);
}
}
ImapReader.demo.php Here is the usage sample
<?php
require_once("ImapReader.class.php");
$box = new ImapReader('example.com', '143', 'somebody#example.com', 'xxxxxxxxxxxx');
$box
->connect()
->fetchAllHeaders()
;
echo $box->count() . " emails in mailbox\n";
for ($i = 0; ($i < $box->count()); $i++)
{
$msg = $box->get($i);
echo "Reception date : {$msg->date}\n";
echo "From : {$msg->from}\n";
echo "To : {$msg->to}\n";
echo "Reply to : {$msg->from}\n";
echo "Subject : {$msg->subject}\n";
$msg = $box->fetch($msg);
echo "Number of readable contents : " . count($msg->content) . "\n";
foreach ($msg->content as $key => $content)
{
echo "\tContent " . ($key + 1) . " :\n";
echo "\t\tContent type : {$content->mime}\n";
echo "\t\tContent charset : {$content->charset}\n";
echo "\t\tContent size : {$content->size}\n";
}
echo "Number of attachments : " . count($msg->attachments) . "\n";
foreach ($msg->attachments as $key => $attachment)
{
echo "\tAttachment " . ($key + 1) . " :\n";
echo "\t\tAttachment type : {$attachment->type}\n";
echo "\t\tContent type : {$attachment->mime}\n";
echo "\t\tFile name : {$attachment->name}\n";
echo "\t\tFile size : {$attachment->size}\n";
}
echo "\n";
}
echo "Searching '*Bob*' ...\n";
$results = $box->searchBy('*Bob*', ImapReader::FROM);
foreach ($results as $result)
{
echo "\tMatched: {$result->from} - {$result->date} - {$result->subject}\n";
}
Enjoy
Regarding the blank emails, check the encoding of the mail.
If it is a binary encoded mail then you will get blank mails when you try to insert them into a mysql text field.
Try shifting every mail to UTF-8 and then insert it
iconv(mb_detect_encoding($mail_content, mb_detect_order(), true), "UTF-8", $mail_content);
function getmsg($mbox,$mid) {
// input $mbox = IMAP stream, $mid = message id
// output all the following:
global $charset,$htmlmsg,$plainmsg,$attachments;
$htmlmsg = $plainmsg = $charset = '';
$attachments = array();
// HEADER
$h = imap_header($mbox,$mid);
// add code here to get date, from, to, cc, subject...
// BODY
$s = imap_fetchstructure($mbox,$mid);
if (!$s->parts) // simple
getpart($mbox,$mid,$s,0); // pass 0 as part-number
else { // multipart: cycle through each part
foreach ($s->parts as $partno0=>$p)
getpart($mbox,$mid,$p,$partno0+1);
}
}
function getpart($mbox,$mid,$p,$partno) {
// $partno = '1', '2', '2.1', '2.1.3', etc for multipart, 0 if simple
global $htmlmsg,$plainmsg,$charset,$attachments;
// DECODE DATA
$data = ($partno)?
imap_fetchbody($mbox,$mid,$partno): // multipart
imap_body($mbox,$mid); // simple
// Any part may be encoded, even plain text messages, so check everything.
if ($p->encoding==4)
$data = quoted_printable_decode($data);
elseif ($p->encoding==3)
$data = base64_decode($data);
// PARAMETERS
// get all parameters, like charset, filenames of attachments, etc.
$params = array();
if ($p->parameters)
foreach ($p->parameters as $x)
$params[strtolower($x->attribute)] = $x->value;
if ($p->dparameters)
foreach ($p->dparameters as $x)
$params[strtolower($x->attribute)] = $x->value;
// ATTACHMENT
// Any part with a filename is an attachment,
// so an attached text file (type 0) is not mistaken as the message.
if ($params['filename'] || $params['name']) {
// filename may be given as 'Filename' or 'Name' or both
$filename = ($params['filename'])? $params['filename'] : $params['name'];
// filename may be encoded, so see imap_mime_header_decode()
$attachments[$filename] = $data; // this is a problem if two files have same name
}
// TEXT
if ($p->type==0 && $data) {
// Messages may be split in different parts because of inline attachments,
// so append parts together with blank row.
if (strtolower($p->subtype)=='plain')
$plainmsg. = trim($data) ."\n\n";
else
$htmlmsg. = $data ."<br><br>";
$charset = $params['charset']; // assume all parts are same charset
}
// EMBEDDED MESSAGE
// Many bounce notifications embed the original message as type 2,
// but AOL uses type 1 (multipart), which is not handled here.
// There are no PHP functions to parse embedded messages,
// so this just appends the raw source to the main message.
elseif ($p->type==2 && $data) {
$plainmsg. = $data."\n\n";
}
// SUBPART RECURSION
if ($p->parts) {
foreach ($p->parts as $partno0=>$p2)
getpart($mbox,$mid,$p2,$partno.'.'.($partno0+1)); // 1.2, 1.2.1, etc.
}
}
Reference (First user contributed note): http://php.net/manual/en/function.imap-fetchstructure.php
I tried all this answers, but neither one worked for me. Then I hit first user contributed note on this PHP page:
http://php.net/manual/en/function.imap-fetchstructure.php
and this works for all my cases. Quite old answer btw.

Corrupt .docx files when using a PHP IMAP class

I'm Using this PHP IMAP Class: http://code.google.com/p/php-imap/source/browse/trunk/ImapMailbox.php on a current project. After a few modifications the class is working. However whenever the class downloads .docx files they are always corrupt and have to be recovered by office.
protected function initMailPart(IncomingMail $mail, $partStruct, $partNum) {
$data = $partNum ? $this->imap_fetchbody($this->mbox, $mail->mId, $partNum, FT_UID) : $this->imap_body($this->mbox, $mail->mId, FT_UID);
if($partStruct->encoding == 1) {
$data = $this->imap_utf8($data);
}
elseif($partStruct->encoding == 2) {
$data = $this->imap_binary($data);
}
elseif($partStruct->encoding == 3) {
$data = $this->imap_base64($data);
}
elseif($partStruct->encoding == 4) {
$data = $this->imap_qprint($data);
}
$data = trim($data);
$params = array();
if(!empty($partStruct->parameters)) {
foreach($partStruct->parameters as $param) {
$params[strtolower($param->attribute)] = $param->value;
}
}
if(!empty($partStruct->dparameters)) {
foreach($partStruct->dparameters as $param) {
$params[strtolower($param->attribute)] = $param->value;
}
}
if(!empty($params['charset'])) {
$data = iconv($params['charset'], $this->serverEncoding, $data);
}
// attachments
if($this->attachmentsDir) {
$filename = false;
$attachmentId = $partStruct->ifid ? trim($partStruct->id, " <>") : null;
if(empty($params['filename']) && empty($params['name']) && $attachmentId) {
$filename = $attachmentId . '.' . strtolower($partStruct->subtype);
}
elseif(!empty($params['filename']) || !empty($params['name'])) {
$filename = !empty($params['filename']) ? $params['filename'] : $params['name'];
$filename = $this->decodeMimeStr($filename);
$filename = $this->quoteAttachmentFilename($filename);
}
if($filename) {
if($this->attachmentsDir) {
$filepath = rtrim($this->attachmentsDir, '/\\') . DIRECTORY_SEPARATOR . $filename;
file_put_contents($filepath, $data);
$mail->attachments[$filename] = $filepath;
}
else {
$mail->attachments[$filename] = $filename;
}
if($attachmentId) {
$mail->attachmentsIds[$filename] = $attachmentId;
}
}
}
if($partStruct->type == 0 && $data) {
if(strtolower($partStruct->subtype) == 'plain') {
$mail->textPlain .= $data;
}
else {
$mail->textHtml .= $data;
}
}
elseif($partStruct->type == 2 && $data) {
$mail->textPlain .= trim($data);
}
if(!empty($partStruct->parts)) {
foreach($partStruct->parts as $subpartNum => $subpartStruct) {
$this->initMailPart($mail, $subpartStruct, $partNum . '.' . ($subpartNum + 1));
}
}
}
protected function decodeMimeStr($string, $charset = 'UTF-8') {
$newString = '';
$elements = $this->imap_mime_header_decode($string);
for($i = 0; $i < count($elements); $i++) {
if($elements[$i]->charset == 'default') {
$elements[$i]->charset = 'iso-8859-1';
}
$newString .= iconv($elements[$i]->charset, $charset, $elements[$i]->text);
}
return $newString;
}
Try wading into the binary files with a good editor, both before and after the round-trip from IMAP, to see if there's something obvious. I've had similar problems where whitespace made its way into the PHP script (e.g. at the end of a file after the close ?> tag); most formats won't blink but .docx may get kicked into a recovery if there's whitespace left over at the end.

Categories