I have written/copied a script that reads emails from an inbox and updates a ticket and then moves the email to a proccessed folder. This all works perfectly on new emails to the inbox but when someone replys to an email and it ends up in the inbox my scrtipt reads nothing on the email.
Is there something different to the way an email is structured when its a reply? I need a way of reading whats in the email so I can update a ticket with the contents. Knowing which email it is to update is all taken care of its just purely reading the content Im struggling with.
Here is the code
class Email
{
// imap server connection
public $conn;
// inbox storage and inbox message count
public $inbox;
private $msg_cnt;
// email login credentials
private $server = '????????????';
private $user = '????????';
private $pass = '?????????????';
private $port = ??;
// connect to the server and get the inbox emails
function __construct()
{
$this->connect();
$this->inbox();
}
function getdecodevalue($message,$coding)
{
switch($coding) {
case 0:
case 1:
$message = imap_8bit($message);
break;
case 2:
$message = imap_binary($message);
break;
case 3:
case 5:
$message=imap_base64($message);
break;
case 4:
$message = imap_qprint($message);
break;
}
return $message;
}
// close the server connection
function close()
{
$this->inbox = array();
$this->msg_cnt = 0;
imap_close($this->conn);
}
// open the server connection
// the imap_open function parameters will need to be changed for the particular server
// these are laid out to connect to a Dreamhost IMAP server
function connect()
{
$this->conn = imap_open("{".$this->server.":".$this->port."/imap/novalidate-cert}INBOX", $this->user, $this->pass);
}
// move the message to a new folder
function move($msg_index, $folder='Read')
{
// move on server
imap_mail_move($this->conn, $msg_index, $folder);
// re-read the inbox
//$this->inbox();
}
// get a specific message (1 = first email, 2 = second email, etc.)
function get($msg_index=NULL)
{
if(count($this->inbox) <= 0)
{
return array();
}
elseif( ! is_null($msg_index) && isset($this->inbox[$msg_index]))
{
return $this->inbox[$msg_index];
}
return $this->inbox[0];
}
// read the inbox
function inbox()
{
$this->msg_cnt = imap_num_msg($this->conn);
$in = array();
for($i = 1; $i <= $this->msg_cnt; $i++)
{
$in[] = array(
'index' => $i,
'header' => imap_headerinfo($this->conn, $i),
'body' => $this->cleanBody(imap_fetchbody($this->conn, $i,1)),
'structure' => imap_fetchstructure($this->conn, $i)
);
}
$this->inbox = $in;
}
function cleanBody($body)
{
$delimiter = '#';
$startTag = '----------START REPLY----------';
$endTag = '----------END REPLY----------';
$regex = $delimiter . preg_quote($startTag, $delimiter)
. '(.*?)'
. preg_quote($endTag, $delimiter)
. $delimiter
. 's';
preg_match($regex,$body,$matches);
$ret = trim($matches[1]);
return $ret;
}
}
$emails = new Email();
$email = array();
$emailCount = 1;
foreach($emails->inbox as $ems => $em)
{
$email[$emailCount]['subject'] = $sub = $em['header']->subject;
//echo $sub;
$subParts = explode('-',$sub);
$ticketUniqueCode = trim($subParts[1]);
$sql = "SELECT * FROM ticket_main WHERE uniquecode = '".mysql_escape_string($ticketUniqueCode)."' LIMIT 1";
$query = mysql_query($sql);
if(mysql_num_rows($query))
{
$res = mysql_fetch_object($query);
$ticketBody = $em['body'];
$customerID = $res->customerID;
$sql2 = "INSERT INTO ticket_updates SET ticketID = '".$res->ticketID."' , submitted = NOW() , submittedBy = '".$res->customerID."' , message = '".mysql_escape_string($ticketBody)."' , inhouse = 0";
$query = mysql_query($sql2);
// attachment section
$message_number = $em['index'];
$attachments = array();
if(isset($em['structure']->parts) && count($em['structure']->parts))
{
//echo 'hi';
for($i = 0; $i < count($em['structure']->parts); $i++)
{
$attachments[$i] = array(
'is_attachment' => false,
'filename' => '',
'name' => '',
'attachment' => ''
);
if($em['structure']->parts[$i]->ifdparameters) {
foreach($em['structure']->parts[$i]->dparameters as $object)
{
if(strtolower($object->attribute) == 'filename')
{
$attachments[$i]['is_attachment'] = true;
$attachments[$i]['filename'] = $object->value;
}
}
}
if($em['structure']->parts[$i]->ifparameters) {
foreach($em['structure']->parts[$i]->parameters as $object)
{
if(strtolower($object->attribute) == 'name')
{
$attachments[$i]['is_attachment'] = true;
$attachments[$i]['name'] = $object->value;
}
}
}
if($attachments[$i]['is_attachment'])
{
$attachments[$i]['attachment'] = imap_fetchbody($emails->conn, $message_number, $i+1);
if($em['structure']->parts[$i]->encoding == 3)
{ // 3 = BASE64
$attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']);
}
elseif($em['structure']->parts[$i]->encoding == 4)
{ // 4 = QUOTED-PRINTABLE
$attachments[$i]['attachment'] = quoted_printable_decode($attachments[$i]['attachment']);
}
}
if(isset($em['structure']->parts[$i]->disposition) && $em['structure']->parts[$i]->disposition == "attachment")
{
$filename = $attachments[$i]['name'];
$mege="";
$data="";
$mege = imap_fetchbody($emails->conn, $message_number, $i+1);
$filename= $filename;
$fp=fopen('???????????????'.$filename,"w");
$data=$emails->getdecodevalue($mege,$em['structure']->parts[$i]->type);
fputs($fp,$data);
fclose($fp);
$email[$emailCount]['attachment'] = $attachments;
}
}
}
}
$emailCount++;
}
$emailNumbers = imap_search($emails->conn,'ALL');
if(!empty($emailNumbers))
{
foreach($emailNumbers as $a)
{
$emails->move($a);
}
imap_expunge($emails->conn);
}
$emails->close();
Hope that makes some sense and someone can actually help.
Many many thanks in advance
Jon
Well the most obvious thing is your code assumes a message is always in part 1, your line:
'body' => $this->cleanBody(imap_fetchbody($this->conn, $i,1)),
Means it's only looking at body 1. If it's a pure text email body 1 will be right, but if it's multipart/alternative (text & html) body 1 would be the base MIME message which tells you that there are sub bodies (body 1.1, 1.2) that actually contain the content.
Further if the reply includes embedded images, or includes the message it's replying to as an attachment then you could have even more bodies/locations.
So how do I find the body? (you ask)... well you can use imap_fetchstructure to learn about all the body parts, then search through it to find a piece with type=0 (text) and then download that body part. (The first found text should be the right one, but note there could be more than one text body type in an email).
Related
I'm retrieving Gmail e-mails through IMAP. I'm setting the FT_PEEK option wherever I can, and finally, I've even opened the mailbox as read-only (OP_READONLY). Yet, my code is marking the messages as read.
Here is the code:
class imap{
CONST HOSTNAME='{imap.gmail.com:993/imap/ssl}INBOX';
CONST USERNAME = '[Address hidden]';
CONST PASSWORD = '[Password hidden]'; //App password from Google
function getMessagesSince($date){
//This will return a collection of email objects.
$messages=array();
if(!$imap=imap_open($this::HOSTNAME, $this::USERNAME, $this::PASSWORD, OP_READONLY)) throw new exception("Unable to connect to IMAP mailbox. ".imap_last_error());
$since=date_format($date, 'j F Y');
$emails=imap_search($imap, 'SINCE "'.$since.'"', SE_UID|FT_PEEK);
foreach($emails as $email){
$messages[]=$this->getMessage($email, $imap);
}
imap_close($imap);
return $messages;
}
private function getMessage($uid, $imap){
//First get the headers
$headers=$this->getHeaders($uid, $imap);
$datereceived=date('Y-m-d H:i:s', strtotime($headers->date));
$sender=$headers->from[0]->mailbox."#".$headers->from[0]->host;
$cc=$headers->cc;
$subject=$headers->subject;
//Now get the message body
$message=$this->getBody($uid, $imap);
$email=new email();
$email->uid=$uid;
$email->datereceived=$datereceived;
$email->sender=$sender;
$email->cc=$cc;
$email->subject=$subject;
$email->message=$message;
return $email;
}
private function getHeaders($uid, $imap){
//Return an array of headers for the referenced message
//$overview = imap_fetch_overview($imap, $uid, FT_UID); //As we used the SE_UID flag when searching, we have to use it when fetching.
//We use this, rather than fetch_overview, because the overview doesn't have the cc information.
$hText = imap_fetchbody($imap, $uid, '0', FT_UID|FT_PEEK);
$headers = imap_rfc822_parse_headers($hText);
return $headers;
}
private function getBody($uid, $imap){
$body = $this->get_part($imap, $uid, "TEXT/HTML");
// if HTML body is empty, try getting text body
if ($body == "") {
$body = $this->get_part($imap, $uid, "TEXT/PLAIN");
}
return $body;
}
private function get_part($imap, $uid, $mimetype, $structure = false, $partNumber = false){
if (!$structure) {
$structure = imap_fetchstructure($imap, $uid, FT_UID);
}
if ($structure) {
if ($mimetype == $this->get_mime_type($structure)) {
if (!$partNumber) {
$partNumber = 1;
}
$text = imap_fetchbody($imap, $uid, $partNumber, FT_UID|FT_PEEK);
switch ($structure->encoding) {
case 3:
return imap_base64($text);
case 4:
return imap_qprint($text);
default:
return $text;
}
}
// multipart
if ($structure->type == 1) {
foreach ($structure->parts as $index => $subStruct) {
$prefix = "";
if ($partNumber) {
$prefix = $partNumber . ".";
}
$data = $this->get_part($imap, $uid, $mimetype, $subStruct, $prefix . ($index + 1));
if ($data) {
return $data;
}
}
}
}
return false;
}
private function get_mime_type($structure){
$primaryMimetype = ["TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "OTHER"];
if ($structure->subtype) {
return $primaryMimetype[(int)$structure->type] . "/" . $structure->subtype;
}
return "TEXT/PLAIN";
}
}
Can anyone spot something I'm missing?
(This is just some extra text, because it's saying that the post is mostly code. I'm just typing here until it lets me post.)
You should delete OP_READONLY flag from imap_open() .
I need to change the code below that belongs to Impresspages CMS to use SMTP instead of sendmail. How can I do this?
<?php
/**
* Queue controls the amount of emails per hour.
* All emails ar placed in queue and send as quiq as possible.
* Parameter "hourlyLimit" defines how much emails can be send in one hour.
* If required amount of emails is bigger than this parameter, part of messages will wait until next hour.
*
* #package ImpressPages
*
*
*/
namespace Ip\Internal\Email;
/**
* Class to send emails. Typically all emails should be send trouht this class.
* #package ImpressPages
*/
class Module
{
/**
* Adds email to the queue
*
* Even if there is a big amount of emails, there is always reserved 20% of traffic for immediate emails.
* Such emails are: registration cofirmation, contact form data and other.
* Newsletters, greetings always can wait a litle. So they are not immediate and will not be send if is less than 20% of traffic left.
*
* #param string $from email address from whish an email should be send
* #param $fromName
* #param string $to email address where an email should be send
* #param $toName
* #param $subject
* #param string $email email html text
* #param bool $immediate indicate hurry of an email.
* #param bool $html true if email message should be send as html
* #param array $files files that should be attached to the email. Files should be accessible for php at this moment. They will be cached until send time.
* #internal param $string #fromName
* #internal param $string #toName
*/
function addEmail($from, $fromName, $to, $toName, $subject, $email, $immediate, $html, $files = null)
{
$cached_files = [];
$cached_fileNames = [];
$cached_fileMimeTypes = [];
if ($files) {
if (is_string($files)) {
$files = array($files);
}
foreach ($files as $fileSetting) {
$file = [];
if (is_array($fileSetting)) {
$file['real_name'] = $fileSetting[0];
$file['required_name'] = basename($fileSetting[1]);
} else {
$file['real_name'] = $fileSetting;
$file['required_name'] = basename($fileSetting);
}
$new_name = 'contact_form_' . rand();
$new_name = \Ip\Internal\File\Functions::genUnoccupiedName($new_name, ipFile('file/tmp/'));
if (copy($file['real_name'], ipFile('file/tmp/' . $new_name))) {
$cached_files[] = ipFile('file/tmp/' . $new_name);
$cached_fileNames[] = $file['required_name'];
$tmpMimeType = \Ip\Internal\File\Functions::getMimeType($file['real_name']);
if ($tmpMimeType == null) {
$tmpMimeType = 'Application/octet-stream';
}
$cached_fileMimeTypes[] = $tmpMimeType;
} else {
trigger_error('File caching failed');
}
}
}
$cachedFilesStr = implode("\n", $cached_files);
$cachedFileNamesStr = implode("\n", $cached_fileNames);
$cachedFileMimeTypesStr = implode("\n", $cached_fileMimeTypes);
$email = str_replace('src="' . ipConfig()->baseUrl(), 'src="', $email);
Db::addEmail(
$from,
$fromName,
$to,
$toName,
$subject,
$email,
$immediate,
$html,
$cachedFilesStr,
$cachedFileNamesStr,
$cachedFileMimeTypesStr
);
}
/**
* Checks if there are some emails waiting in queue and sends them if possible.
*/
function send()
{
$alreadySent = Db::sentOrLockedCount(60);
if ($alreadySent !== false) {
$available = floor(ipGetOption('Email.hourlyLimit') * 0.8 - $alreadySent); //20% for immediate emails
$lockKey = md5(uniqid(rand(), true));
if ($available > 0) {
if ($available > 5 && !defined('CRON')) { //only cron job can send many emails at once.
$available = 5;
}
$locked = Db::lock($available, $lockKey);
} else {
$available = 0;
$locked = 0;
}
if ($locked == $available) { //if in queue left some messages
if (ipGetOption('Email.hourlyLimit') - ($alreadySent + $available) > 0) {
$locked = $locked + Db::lockOnlyImmediate(
ipGetOption('Email.hourlyLimit') - ($alreadySent + $available),
$lockKey
);
}
}
if ($locked) {
$emails = Db::getLocked($lockKey);
foreach ($emails as $key => $email) {
if (function_exists('set_time_limit')) {
set_time_limit((sizeof($emails) - $key) * 10 + 100);
}
$mail = new \PHPMailer();
/* $mail->Sender = $email['from'];
$mail->addCustomHeader("Return-Path: " . $email['from']);*/
$mail->From = $email['from'];
$mail->FromName = $email['fromName'];
$mail->AddReplyTo($email['from'], $email['fromName']);
$mail->WordWrap = 50; // set word wrap
$mail->CharSet = ipConfig()->get('charset');
$mail->Subject = $email['subject'];
/* foreach($this->posted_files as $file){
if(isset($_FILES[$file]['tmp_name']) && $_FILES[$file]['error'] == 0){
$mail->AddAttachment($_FILES[$file]['tmp_name'], $_FILES[$file]['name']);
}
}*/
$files = explode("\n", $email['files']);
$fileNames = explode("\n", $email['fileNames']);
$fileMimeTypes = explode("\n", $email['fileMimeTypes']);
$fileCount = min(count($files), count($fileNames), count($fileMimeTypes));
for ($i = 0; $i < $fileCount; $i++) {
if ($files[$i] != '') {
if ($fileMimeTypes[$i] == '') {
$answer = $mail->AddAttachment($files[$i], $fileNames[$i]);
} else {
$answer = $mail->AddAttachment(
$files[$i],
$fileNames[$i],
"base64",
$fileMimeTypes[$i]
);
}
if (!$answer) {
ipLog()->error(
'Email.addAttachmentFailed: {subject} to {to}',
array(
'to' => $email['to'],
'subject' => $email['subject'],
'filename' => $fileNames[$i],
)
);
return false;
}
}
}
if ($email['html']) {
$mail->IsHTML(true); // send as HTML
$mail->MsgHTML($email['email']);
try {
$altBody = \Ip\Internal\Text\Html2Text::convert($email['email']);
} catch (\Ip\Internal\Text\Html2TextException $e) {
$altBody = $email['email'];
}
$mail->AltBody = $altBody;
} else {
/*$h2t = new \Ip\Internal\Text\Html2Text($content, false);
$mail->Body = $h2t->get_text();*/
$mail->Body = $email['email'];
}
$mail->AddAddress($email['to'], $email['toName']);
$mail = ipFilter('ipSendEmailPHPMailerObject', $mail, $email);
if (!$mail->Send()) {
ipLog()->error(
'Email.sendFailed: {subject} to {to}',
array('to' => $email['to'], 'subject' => $email['subject'], 'body' => $email['email'])
);
return false;
}
if (sizeof($emails) > 5) {
sleep(1);
}
Db::unlockOne($email['id']);
}
}
}
return null;
}
}
I have an issue where I need to data from a RETS server and populate it on the database, I was able to get all but two tables to properly automate this data in, but I'm having issues with the last (and biggest) two. After looking at it, the issue is timeout with the server, it cannot finish in time for the database to populate.
I am working off of a godaddy server and already have the maximum time I can set for the program to run, the only thing I can do now is figure out a way to speed up my code so it finishes in time, and would like some help figuring that out.
The code (posted below) was modified to fit my needs from here: https://github.com/coreyrowell/retshelper/blob/master/rets_helper.php
While it works well and I was able to get it working, I haven't been able to figure out the best way to speed it up. Any help would be sincerely appreciated.
<?php
/*
.---------------------------------------------------------------------------.
| Software: RETSHELPER - PHP Class to interface RETS with Database |
| Version: 1.0 |
| Contact: corey#coreyrowell.com |
| Info: None |
| Support: corey#coreyrowell.com |
| ------------------------------------------------------------------------- |
| Author: Corey Rowell - corey#coreyrowell.com |
| Copyright (c) 2013, Corey Rowell. All Rights Reserved. |
| ------------------------------------------------------------------------- |
| License: This content is released under the |
| (http://opensource.org/licenses/MIT) MIT License. | |
'---------------------------------------------------------------------------'
*/
/*
.---------------------------------------------------------------------------.
| This software requires the use of the PHPRETS library |
| http://troda.com/projects/phrets/ |
'---------------------------------------------------------------------------'
*/
define("BASE_PATH",dirname(__FILE__)."/");
ini_set('mysql.connect_timeout',0);
ini_set('default_socket_timeout',0);
class RETSHELPER
{
// Defaults
private $rets, $auth, $config, $database, $mysqli, $data, $log, $scriptstart, $scriptend,
$previous_start_time, $current_start_time, $updates_log, $active_ListingRids = array();
public function __construct()
{
// Require PHRETS library
require_once("phrets.php");
// Start rets connection
$this->rets = new phRETS;
$this->scriptstart = date("m-d-y_h-i-s", time());
// RETS Server Info
$this->auth['url'] = 'redacted';//MLS_URL;//MLS_URL;
$this->auth['username'] = 'redacted'; //MLS_USERNAME;
$this->auth['password'] = 'redacted'; //MLS_PASS;
$this->auth['retsversion'] = ''; //USER Agent Version
$this->auth['useragent'] = ''; //USER Agent
// RETS Options
$this->config['property_classes'] = array("A");//,"B","C","D","E","F");
$this->config['KeyField'] = "LIST_1";
$this->config['offset_support'] = TRUE; // Enable if RETS server supports 'offset'
$this->config['useragent_support'] = FALSE;
$this->config['images_path'] = BASE_PATH."listing_photos/";
$this->config['logs_path'] = BASE_PATH."logs/";
$this->config['start_times_path'] = BASE_PATH."logs/";
$this->config['previous_start_time'] = $this->get_previous_start_time();
$this->config['create_tables'] = FALSE; // Create tables for classes (terminates program)
// Log to screen?
$this->config['to_screen'] = TRUE;
// Database Config
$this->database['host'] = 'redacted'; //DB_SERVER;
$this->database['username'] = 'redacted'; //DB_USER;
$this->database['password'] = 'redacted'; //DB_PASS;
$this->database['database'] = 'redacted'; //DB_NAME;
$this->config_init();
// Load the run function
$this->run();
}
private function config_init()
{
// Set offset support based on config
if($this->config['offset_support'])
{
$this->rets->SetParam("offset_support", true);
} else {
$this->rets->SetParam("offset_support", false);
}
if($this->config['useragent_support'])
{
$this->rets->AddHeader("RETS-Version", $this->auth['retsversion']);
$this->rets->AddHeader("User-Agent", $this->auth['useragent']);
}
}
public function run()
{
// Start Logging
$this->logging_start();
// RETS Connection
$this->connect();
// Connect to Database
$this->database_connect();
if($this->config['create_tables'])
{
$this->log_data("Creating database tables, program will exit after finishing.");
foreach ($this->config['property_classes'] as $class)
{
$this->log_data("Creating table for: " . $class);
$this->create_table_for_property_class($class);
}
$this->log_data("Exiting program.");
return;
}
// Get Properties (and images)
$this->get_properties_by_class();
// Close RETS Connection
$this->disconnect();
// Delete inactive listings
$this->database_delete_records();
// Insert new listings
$this->database_insert_records();
// Disconnect from Database
$this->database_disconnect();
// End Logging
$this->logging_end();
// Time for next scheduled update
$this->set_previous_start_time();
}
private function connect()
{
$this->log_data("Connecting to RETS...");
// Connect to RETS
$connect = $this->rets->Connect($this->auth['url'], $this->auth['username'], $this->auth['password']);
if($connect)
{
$this->log_data("Successfully connected to RETS.");
return TRUE;
} else {
$error = $this->rets->Error();
if($error['text'])
{
$error = $error['text'];
} else {
$error = "No error message returned from RETS. Check RETS debug file.";
}
$this->log_error("Failed to connect to RETS.\n".$error);
die();
}
}
private function get_properties_by_class()
{
$this->log_data("Getting Classes...");
foreach ($this->config['property_classes'] as $class)
{
$this->log_data("Getting Class: ".$class);
// Set
$fields_order = array();
$mod_timestamp_field = $this->get_timestamp_field($class);
$previous_start_time = $this->config['previous_start_time'];
$search_config = array('Format' => 'COMPACT-DECODED', 'QueryType' => 'DMQL2', 'Limit'=> 1000, 'Offset' => 1, 'Count' => 1);
/*--------------------------------------------------------------------------------.
| |
| If you're having problems, they probably lie here in the $query and/or $search. |
| |
'--------------------------------------------------------------------------------*/
// Query
$query = "({$mod_timestamp_field}=2016-09-16T00:00:00-2016-09-16T01:00:00)";//{$previous_start_time}+)";
// Run Search
$search = $this->rets->SearchQuery("Property", $class, $query, $search_config);
// Get all active listings
$query_all = "({$mod_timestamp_field}=1980-01-01T00:00:00+)";
$search_all = $this->rets->SearchQuery("Property", $class, $query_all, array('Format'=>'COMPACT', 'Select'=>$this->config['KeyField']));
$tmpArray = array();
while($active_rid = $this->rets->FetchRow($search_all)) {
array_push($tmpArray, $active_rid[$this->config['KeyField']]);
}
$this->active_ListingRids['property_'.strtolower($class)] = $tmpArray;
$data = array();
if ($this->rets->NumRows($search) > 0)
{
// Get columns
$fields_order = $this->rets->SearchGetFields($search);
$this->data['headers'] = $fields_order;
// Process results
while ($record = $this->rets->FetchRow($search))
{
$this_record = array();
// Loop it
foreach ($fields_order as $fo)
{
$this_record[$fo] = $record[$fo];
}
$ListingRid = $record[$this->config['KeyField']];
$data[] = $this_record;
}
}
// Set data
$this->data['classes'][$class] = $data;
$this->log_data("Finished Getting Class: ".$class . "\nTotal found: " .$this->rets->TotalRecordsFound());
// Free RETS Result
$this->rets->FreeResult($search);
}
}
private function get_timestamp_field($class)
{
$class = strtolower($class);
switch($class)
{
case 'a':
$field = "LIST_87";
break;
}
return $field;
}
private function disconnect()
{
$this->log_data("Disconnected from RETS.");
$this->rets->Disconnect();
}
private function database_connect()
{
$this->log_data("Connecting to database...");
$host = $this->database['host'];
$username = $this->database['username'];
$password = $this->database['password'];
$database = $this->database['database'];
// Create connection
$this->mysqli = new mysqli($host, $username, $password, $database);
// Throw error if connection fails
if ($this->mysqli->connect_error) {
$this->log_error("Database Connection Error". $this->mysqli->connect_error);
die('Connect Error (' . $this->mysqli->connect_errno . ') '
. $this->mysqli->connect_error);
}
}
private function database_delete_records()
{
$this->log_data("Updating database...");
// Loop through each table and update
foreach($this->config['property_classes'] as $class)
{
// Get Tables
$table = "rets_property_".strtolower($class);
$activeListings = $this->active_ListingRids['property_'.strtolower($class)];
$sql = "DELETE FROM {$table} WHERE {$this->config['KeyField']} NOT IN (".implode(',', $activeListings).");";
$this->mysqli->query($sql);
if($this->mysqli->affected_rows > 0)
{
$this->log_data("Deleted {$this->mysqli->affected_rows} Listings.");
// return TRUE;
} else if($this->mysqli->affected_rows == 0) {
$this->log_data("Deleted {$this->mysqli->affected_rows} Listings.");
} else {
$this->log_data("Deleting database records failed \n\n" . mysqli_error($this->mysqli));
// return FALSE;
}
}
}
private function database_insert_records()
{
$this->log_data("Inserting records...");
foreach($this->config['property_classes'] as $class)
{
// Get Tables
$table = "rets_property_".strtolower($class);
// Get data
$data_row = $this->data['classes'][$class];
// Defaults
$total_rows = 0;
$total_affected_rows = 0;
// Loop through data
foreach($data_row as $drow)
{
// Clean data
// replace empty with NULL
// and wrap data in quotes
$columns = array();
$values = array();
foreach($drow as $key => $val)
{
if($val === '')
{
$val = '""';
} else {
$val = mysqli_real_escape_string($this->mysqli ,$val);
$val = "'$val'";
}
$columns[] = $key;
$values[] = $val;
}
// Implode data rows with commas
$values = implode(', ', $values);
$columns = implode(', ', $columns);
// Build SQL
$sql = "REPLACE INTO {$table} ({$columns}) VALUES ({$values})";
// Do query
$this->mysqli->query($sql);
if($this->mysqli->affected_rows > 0)
{
$total_affected_rows++;
} else {
$this->log_error("Failed to insert the following record: ".$sql . "\n\n" . mysqli_error($this->mysqli));
}
$total_rows++;
}
$this->log_data("Done inserting data. ".$class."\nTotal Records: ".$total_rows." .\nTotal Inserted: ".$total_affected_rows);
}
}
private function database_disconnect()
{
$this->log_data("Database disconnected...");
// Close connection
$this->mysqli->close();
}
private function create_table_for_property_class($class)
{
// gets resource information. need this for the KeyField
$rets_resource_info = $this->rets->GetMetadataInfo();
$resource = "Property";
// pull field format information for this class
$rets_metadata = $this->rets->GetMetadata($resource, $class);
$table_name = "rets_".strtolower($resource)."_".strtolower($class);
// i.e. rets_property_resi
$sql = $this->create_table_sql_from_metadata($table_name, $rets_metadata, $rets_resource_info[$resource]['KeyField']);
$this->mysqli->query($sql);
}
private function create_table_sql_from_metadata($table_name, $rets_metadata, $key_field, $field_prefix = "")
{
$sql_query = "CREATE TABLE {$table_name} (\n";
foreach ($rets_metadata as $field) {
$field['SystemName'] = "`{$field_prefix}{$field['SystemName']}`";
$cleaned_comment = addslashes($field['LongName']);
$sql_make = "{$field['SystemName']} ";
if ($field['Interpretation'] == "LookupMulti") {
$sql_make .= "TEXT";
}
elseif ($field['Interpretation'] == "Lookup") {
$sql_make .= "VARCHAR(50)";
}
elseif ($field['DataType'] == "Int" || $field['DataType'] == "Small" || $field['DataType'] == "Tiny") {
$sql_make .= "INT({$field['MaximumLength']})";
}
elseif ($field['DataType'] == "Long") {
$sql_make .= "BIGINT({$field['MaximumLength']})";
}
elseif ($field['DataType'] == "DateTime") {
$sql_make .= "DATETIME default '0000-00-00 00:00:00' not null";
}
elseif ($field['DataType'] == "Character" && $field['MaximumLength'] <= 255) {
$sql_make .= "VARCHAR({$field['MaximumLength']})";
}
elseif ($field['DataType'] == "Character" && $field['MaximumLength'] > 255) {
$sql_make .= "TEXT";
}
elseif ($field['DataType'] == "Decimal") {
$pre_point = ($field['MaximumLength'] - $field['Precision']);
$post_point = !empty($field['Precision']) ? $field['Precision'] : 0;
$sql_make .= "DECIMAL({$field['MaximumLength']},{$post_point})";
}
elseif ($field['DataType'] == "Boolean") {
$sql_make .= "CHAR(1)";
}
elseif ($field['DataType'] == "Date") {
$sql_make .= "DATE default '0000-00-00' not null";
}
elseif ($field['DataType'] == "Time") {
$sql_make .= "TIME default '00:00:00' not null";
}
else {
$sql_make .= "VARCHAR(255)";
}
$sql_make .= " COMMENT '{$cleaned_comment}'";
$sql_make .= ",\n";
$sql_query .= $sql_make;
}
$sql_query .= "`Photos` TEXT COMMENT 'Photos Array', ";
$sql_query .= "PRIMARY KEY(`{$field_prefix}{$key_field}`) )";
return $sql_query;
}
private function get_previous_start_time()
{
$filename = "previous_start_time_A.txt";
// See if file exists
if(file_exists($this->config['start_times_path'].$filename))
{
$time=time();
$this->updates_log = fopen($this->config['start_times_path'].$filename, "r+");
$this->previous_start_time = fgets($this->updates_log);
$this->current_start_time = date("Y-m-d", $time) . 'T' . date("H:i:s", $time);
} else {
// Create file
$this->updates_log = fopen($this->config['start_times_path'].$filename, "w+");
fwrite($this->updates_log, "1980-01-01T00:00:00\n");
$this->get_previous_start_time();
}
// fgets reads up to & includes the first newline, strip it
return str_replace("\n", '', $this->previous_start_time);
}
private function set_previous_start_time()
{
$file = $this->config['start_times_path'] . "previous_start_time_A.txt";
$file_data = $this->current_start_time."\n";
$file_data .= file_get_contents($file);
file_put_contents($file, $file_data);
}
private function logging_start()
{
$filename = "Log".date("m-d-y_h-i-s", time()).".txt";
// See if file exists
if(file_exists($this->config['logs_path'].$filename))
{
$this->log = fopen($this->config['logs_path'].$filename, "a");
} else {
// Create file
$this->log = fopen($this->config['logs_path'].$filename, "w+");
}
}
private function log_data($data)
{
$write_data = "\nInfo Message: [".date("m/d/y - h:i:s", time())."]\n------------------------------------------------\n";
$write_data .= $data."\n";
$write_data .= "\n------------------------------------------------\n";
fwrite($this->log, $write_data);
if($this->config['to_screen'])
{
echo str_replace(array("\n"), array('<br />'), $write_data);
}
}
private function log_error($error)
{
$write_data = "\nError Message: [".date("m/d/y - h:i:s", time())."]\n------------------------------------------------\n";
$write_data .= $error."\n";
$write_data .= "\n------------------------------------------------\n";
fwrite($this->log, $write_data);
if($this->config['to_screen'])
{
echo str_replace(array("\n"), array('<br />'), $write_data);
}
}
private function logging_end()
{
$this->scriptend = date("m-d-y_h-i-s", time());
$this->log_data("Closing log file.\n
Start Time: {$this->scriptstart}\n
End Time: {$this->scriptend}");
fclose($this->log);
}
}
// Load the class
$retshelper = new RETSHELPER;
Sorry for the wall of code; I would shorten it down but I'm at a loss with what is still needed and what isn't. Once again, any help would or a point into the right direction would be appreciated.
Re-visiting this problem specified in my previous question, I tried and tried, also with different accounts (I tried gmail, as well as outlook), but the problem still persists. The error I get is the following if I try to access my google account
Error: Unable to get imap_thread after 4 retries. 'Can't open mailbox {imap.gmail.com:993/ssl/imap/tls/novalidate-cert}INBOX: invalid remote specification'
if I try accessing email on my outlook account, the error is the same :
Error: Unable to get imap_thread after 4 retries. 'Can't open mailbox {outlook.office365.com:993/ssl/imap/tls/novalidate-cert}INBOX: invalid remote specification'
My setup is as follows :
public $emailTicket = array(
'datasource' => 'ImapSource',
'server' => 'outlook.office365.com',
'connect' => 'imap/tls/novalidate-cert',
'username' => 'my email here',
'password' => 'my password here',
'port' => '993', //incoming port
'ssl' => true,
'encoding' => 'UTF-8',
'error_handler' => 'php',
'auto_mark_as' => array(
'Seen',
// 'Answered',
// 'Flagged',
// 'Deleted',
// 'Draft',
),
);
I am working on a local machine, does anyone know if this might be the problem or not? Has anyone ever tried this and worked for him/her? I am open to all input!
I can't seem to find what's wrong here, I've been at this for about 2days now, so if anyone can help, I appreciate it!
Also here's the link for the plugin i'm using, by Nicolas Ramy..
You can use the following implemented code to fulfill your requirements:
public function generate_email_response_pdf()
{
$this->layout = false;
$this->autoRender = false;
$username = EMP_SMTP_MAIL_FROM;
$password = EMP_SMTP_MAIL_PASSWORD;
$imap = imap_open('{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX', $username, $password);
$emails = imap_search($imap, 'ALL');
if(!empty($emails))
{
//put the newest emails on top
rsort($emails);
foreach($emails as $email_number)
{
$flag = 0;
$mail_data = array();
$file_name = array();
$output = array();
$savefilename = null;
$filename = null;
$overview = imap_fetch_overview($imap, $email_number, 0);
//initialize the subject index with -000, considering not receving this will not be received in
//subject line of email
$output['subject'] = '-000x';
if(isset($overview[0] -> subject))
{
$output['subject'] = $overview[0] -> subject;
}
$structure = imap_fetchstructure($imap, $email_number);
if(property_exists($structure, 'parts'))
{
$flag = 1;
$flattened_parts = $this->flatten_parts($structure->parts);
foreach($flattened_parts as $part_number => $part)
{
switch($part->type)
{
case 0:
//the HTML or plain text part of the email
if((isset($part->subtype)=='HTML')&&(isset($part->disposition)=='ATTACHMENT'))
{
$part_number = 1.2;
}
else if(isset($part->subtype)=='HTML')
{
$part_number = $part_number;
}
else
{
$part_number = $part_number;
}
$message = $this->get_part($imap, $email_number, $part_number, $part->encoding);
//now do something with the message, e.g. render it
break;
case 1:
// multi-part headers, can ignore
break;
case 2:
// attached message headers, can ignore
break;
case 3: // application
case 4: // audio
case 5: // image
case 6: // video
case 7: // other
break;
}
if(isset($part->disposition))
{
$filename = $this->get_filename_from_part($part);
if($filename)
{
// it's an attachment
$attachment = $this->get_part($imap, $email_number, $part_number, $part->encoding);
$file_info = pathinfo($filename);
$savefilename = RESPONSE_ATTACHMENT_PREFIX.$file_info['filename'].'_'.$this->_getRandId(4).'.'.$file_info['extension'];
$file_name[] = $savefilename;
$attachment_file_name = $this->save_attachment($attachment, $savefilename, $directory_path);
//imap_fetchbody($imap, $email_number, 2); //This marks message as read
}
else
{
// don't know what it is
}
}
}
}
else
{
$encoding = $structure->encoding;
$message = imap_fetchbody($imap, $email_number, 1.2);
//echo $message; die;
if($message == "")
{
$message = imap_body($imap, $email_number);
if($encoding == 3)
{
$message = base64_decode($message);
}
else if($encoding == 4)
{
$message = quoted_printable_decode($message);
}
}
}
$header = imap_headerinfo($imap, $email_number);
$from_email = $header->from[0]->mailbox."#".$header->from[0]->host;
$to_email = $header->to[0]->mailbox."#".$header->to[0]->host;
$reply_to_email = $header->reply_to[0]->mailbox."#".$header->reply_to[0]->host;
$cc_email = array();
if(isset($header->cc))
{
foreach($header->cc as $ccmail)
{
$cc_email[] = $ccmail->mailbox.'#'.$ccmail->host;
}
$cc_email = implode(", ", $cc_email);
}
$output['to'] = $to_email;
$output['from'] = $from_email;
$output['reply_to'] = $reply_to_email;
$output['cc'] = $cc_email;
$formatted_date = date('D, d M Y h:i A', strtotime($overview[0] -> date));
$output['date'] = $formatted_date;
$output['message'] = $message;
$output['flag'] = $flag;
$mail_data['Attachment'] = $file_name;
$mail_data['Data'] = $output;
$this->set('response_data', $mail_data);
$mail_content = null;
if(!empty($mail_data))
{
$this->viewPath = 'Elements/default';
$mail_content = $this->render('pdf_content');
}
$header = null;
$footer = null;
$html = preg_replace(array('/[^\r\n\t\x20-\x7E\xA0-\xFF]*/'), '', $mail_content);
$pdfFile = $this->_generateWkPdf($html, $directory_path, $new_file_name, $header, $footer);
$image_type = EXT_JPG;
$response_files_array = $this->_generateImagesFromPdf($directory_path.$pdfFile, $directory_path, $new_file_name, $image_type);
}
}
imap_close($imap);
}
Using the following class:
class Email_reader {
// imap server connection
public $conn;
// inbox storage and inbox message count
private $inbox;
private $msg_cnt;
// email login credentials
private $server = 'xxxxxxxxxxxx.com';
private $user = 'admin#xxxxxxxxx.com';
private $pass = 'xxxxxxxxx';
private $port = 143; // adjust according to server settings
// connect to the server and get the inbox emails
function __construct() {
$this->connect();
$this->inbox();
}
// close the server connection
function close() {
$this->inbox = array();
$this->msg_cnt = 0;
imap_close($this->conn);
}
// open the server connection
// the imap_open function parameters will need to be changed for the particular server
// these are laid out to connect to a Dreamhost IMAP server
function connect() {
$this->conn = imap_open('{'.$this->server.'/notls}', $this->user, $this->pass);
}
// move the message to a new folder
function move($msg_index, $folder='INBOX.Processed') {
// move on server
imap_mail_move($this->conn, $msg_index, $folder);
imap_expunge($this->conn);
// re-read the inbox
$this->inbox();
}
// get a specific message (1 = first email, 2 = second email, etc.)
function get($msg_index=NULL) {
if (count($this->inbox) <= 0) {
return array();
}
elseif ( ! is_null($msg_index) && isset($this->inbox[$msg_index])) {
return $this->inbox[$msg_index];
}
return $this->inbox[0];
}
// read the inbox
function inbox() {
$this->msg_cnt = imap_num_msg($this->conn);
$in = array();
for($i = 1; $i <= $this->msg_cnt; $i++) {
$in[] = array(
'index' => $i,
'header' => imap_headerinfo($this->conn, $i),
'body' => imap_body($this->conn, $i),
'structure' => imap_fetchstructure($this->conn, $i)
);
}
$this->inbox = $in;
}
}
And the following usage code:
$email = new Email_reader();
$msg = $email->get(1);
echo "message body is [".$msg['body']."]<br />"; //prints body //good
echo "message index is [".$msg['index']."]<br />"; //prints "2" //good
echo "message subject is [".$msg['header']->Subject."]<br />"; //prints strangness
echo "message toaddress is [".$msg['header']->toaddress."]<br />"; //prints strangeness
the attempt to print subject line prints "=?utf-8?B?TWljcm9zb2Z0IE9mZmljZSBPdXRsb29rIFRlc3QgTWVzc2FnZQ==?="
and the toaddress also something similar.
I looked at some other examples online but i dont see anything different that they do than what im doing.
You have to decode the RFC 2047 encoding of the Unicode data. Check the imap_utf8 function.