grthtrhthjhtyjytjytkergtrhtrjytjerhrfh4:24 29/09/2026', '|', '$',);
public $currentCache;
public $defaultSort = 'date';
public $defaultDirection = "DESC";
public $hrSort = array(
0 => 'flagged',
1 => 'status',
2 => 'from',
3 => 'subj',
4 => 'date',
);
public $hrSortLocal = array(
'flagged' => 'flagged',
'status' => 'answered',
'from' => 'fromaddr',
'subject' => 'subject',
'date' => 'senddate',
);
// default attributes
public $transferEncoding = array(
0 => '7BIT',
1 => '8BIT',
2 => 'BINARY',
3 => 'BASE64',
4 => 'QUOTED-PRINTABLE',
5 => 'OTHER'
);
// object attributes
public $compoundMessageId; // concatenation of messageID and deliveredToEmail
public $serverConnectString;
public $disable_row_level_security = true;
public $InboundEmailCachePath;
public $InboundEmailCacheFile = 'InboundEmail.cache.php';
public $object_name = 'InboundEmail';
public $module_dir = 'InboundEmail';
public $table_name = 'inbound_email';
public $new_schema = true;
public $process_save_dates = true;
public $order_by;
public $dbManager;
public $field_defs;
public $column_fields;
public $required_fields = array(
'name' => 'name',
'server_url' => 'server_url',
'mailbox' => 'mailbox',
'user' => 'user',
'port' => 'port',
);
public $imageTypes = array("JPG", "JPEG", "GIF", "PNG");
public $inlineImages = array(); // temporary space to store ID of inlined images
public $defaultEmailNumAutoreplies24Hours = 10;
public $maxEmailNumAutoreplies24Hours = 10;
// custom ListView attributes
public $mailbox_type_name;
public $global_personal_string;
// service attributes
public $tls;
public $ca;
public $ssl;
public $protocol;
public $keyForUsersDefaultIEAccount = 'defaultIEAccount';
// prefix to use when importing inlinge images in emails
public $imagePrefix;
public $job_name = 'function::pollMonitoredInboxes';
/**
*
* @var ImapHandlerInterface
*/
protected $imap;
/**
* Email constructor
*/
public function __construct(ImapHandlerInterface $imapHandler = null)
{
// using ImapHandlerInterface as dependency
if (null == $imapHandler) {
LoggerManager::getLogger()->debug('Using system default ImapHandler. Hint: Use any ImapHandlerInterface as dependency of InboundEmail');
$imapHandlerFactory = new ImapHandlerFactory();
$imapHandler = $imapHandlerFactory->getImapHandler();
}
$this->imap = $imapHandler;
$this->InboundEmailCachePath = sugar_cached('modules/InboundEmail');
$this->EmailCachePath = sugar_cached('modules/Emails');
parent::__construct();
if ($this->getImap()->isAvailable()) {
/*
* 1: Open
* 2: Read
* 3: Write
* 4: Close
*/
$this->getImap()->setTimeout(1, 60);
$this->getImap()->setTimeout(2, 60);
$this->getImap()->setTimeout(3, 60);
}
$this->smarty = new Sugar_Smarty();
$this->overview = new Overview();
$this->imagePrefix = "{$GLOBALS['sugar_config']['site_url']}/cache/images/";
}
/**
*
* @return ImapHandlerInterface
*/
public function getImap(ImapHandlerInterface $imap = null)
{
if (null === $this->imap) {
if (null === $imap) {
$imapFactory = new ImapHandlerFactory();
$imap = $imapFactory->getImapHandler();
}
$this->imap = $imap;
}
return $this->imap;
}
/**
* retrieves I-E bean
* @param string id
* @return object Bean
*/
public function retrieve($id = -1, $encode = true, $deleted = true)
{
$ret = parent::retrieve($id, $encode, $deleted);
// if I-E bean exist
if (!is_null($ret)) {
$this->email_password = blowfishDecode(blowfishGetKey('InboundEmail'), $this->email_password);
$this->retrieveMailBoxFolders();
}
return $ret;
}
/**
* wraps SugarBean->save()
* @param string ID of saved bean
*/
public function save($check_notify = false)
{
// generate cache table for email 2.0
$multiDImArray = $this->generateMultiDimArrayFromFlatArray(
explode(",", $this->mailbox),
$this->retrieveDelimiter()
);
$raw = $this->generateFlatArrayFromMultiDimArray($multiDImArray, $this->retrieveDelimiter());
sort($raw);
//_pp(explode(",", $this->mailbox));
//_ppd($raw);
$raw = $this->filterMailBoxFromRaw(explode(",", $this->mailbox), $raw);
$this->mailbox = implode(',', $raw);
if (!empty($this->email_password)) {
$this->email_password = blowfishEncode(blowfishGetKey('InboundEmail'), $this->email_password);
}
$ret = parent::save($check_notify);
return $ret;
}
public function filterMailBoxFromRaw($mailboxArray, $rawArray)
{
$newArray = array_intersect($mailboxArray, $rawArray);
sort($newArray);
return $newArray;
} // fn
/**
* Overrides SugarBean's mark_deleted() to drop the related cache table
* @param string $id GUID of I-E instance
*/
public function mark_deleted($id)
{
parent::mark_deleted($id);
//bug52021 we need to keep the reference to the folders table in order for emails module to function properly
$this->deleteCache();
}
/**
* Mark cached email answered (replied)
* @param string $mailid (uid for imap, message_id for pop3)
*/
public function mark_answered($mailid, $type = 'smtp')
{
switch ($type) {
case 'smtp':
$q = "update email_cache set answered = 1 WHERE imap_uid = $mailid and ie_id = '{$this->id}'";
$this->db->query($q);
break;
case 'pop3':
$q = "update email_cache set answered = 1 WHERE message_id = '$mailid' and ie_id = '{$this->id}'";
$this->db->query($q);
break;
}
}
/**
* Renames an IMAP mailbox
* @param string $newName
*/
public function renameFolder($oldName, $newName)
{
//$this->mailbox = "INBOX"
$this->connectMailserver();
$oldConnect = $this->getConnectString('', $oldName);
$newConnect = $this->getConnectString('', $newName);
$state = new StateSaver();
$state->pushErrorLevel();
error_reporting(0);
$imapRenameMailbox = $this->getImap()->renameMailbox($oldConnect, $newConnect);
$state->popErrorLevel();
if (!$imapRenameMailbox) {
$GLOBALS['log']->debug("***INBOUNDEMAIL: failed to rename mailbox [ {$oldConnect} ] to [ {$newConnect} ]");
} else {
$this->mailbox = str_replace($oldName, $newName, $this->mailbox);
$this->save();
$sessionFoldersString = $this->getSessionInboundFoldersString(
$this->server_url,
$this->email_user,
$this->port,
$this->protocol
);
$sessionFoldersString = str_replace($oldName, $newName, $sessionFoldersString);
$this->setSessionInboundFoldersString(
$this->server_url,
$this->email_user,
$this->port,
$this->protocol,
$sessionFoldersString
);
}
return $imapRenameMailbox;
}
/**
* @return bool
*/
public function isPersonalEmailAccount()
{
return (bool)$this->is_personal;
}
/**
* @return bool
*/
public function isGroupEmailAccount()
{
return !$this->isPersonalEmailAccount();
}
/**
* @param int $offset
* @param int $pageSize
* @param array $order
* @param array $filter
* @return array
*/
public function checkWithPagination($offset = 0, $pageSize = 20, $order = array(), $filter = array(), $columns = array())
{
$mailboxInfo = array('Nmsgs' => 0);
if ($this->connectMailserver() !== 'true') {
LoggerManager::getLogger()->error('Unable to connect to IMAP server.');
return false;
}
// handle sorting
// Default: to sort the date in descending order
$sortCriteria = SORTDATE;
$sortCRM = 'udate';
$sortOrder = 1;
if ($order['sortOrder'] == 'ASC') {
$sortOrder = 0;
}
if (stristr($order['orderBy'], 'date') !== false) {
$sortCriteria = SORTDATE;
$sortCRM = 'udate';
} elseif (stristr($order['orderBy'], 'to') !== false) {
$sortCriteria = SORTTO;
$sortCRM = 'to';
} elseif (stristr($order['orderBy'], 'from') !== false) {
$sortCriteria = SORTFROM;
$sortCRM = 'from';
} elseif (stristr($order['orderBy'], 'cc') !== false) {
$sortCriteria = SORTCC;
} elseif (stristr($order['orderBy'], 'name') !== false) {
$sortCriteria = SORTSUBJECT;
$sortCRM = 'subject';
} elseif (stristr($order['orderBy'], 'subject') !== false) {
$sortCriteria = SORTSUBJECT;
$sortCRM = 'subject';
}
// handle filtering
$filterCriteria = null;
if (!empty($filter)) {
foreach ($filter as $filterField => $filterFieldValue) {
if (empty($filterFieldValue)) {
continue;
}
// Convert to a blank string as NULL will break the IMAP request
if ($filterCriteria == null) {
$filterCriteria = '';
}
$filterCriteria .= ' ' . $filterField . ' "' . $filterFieldValue . '" ';
}
}
if (empty($filterCriteria) && $sortCriteria === SORTDATE) {
// Performance fix when no filters are enabled
$totalMsgs = $this->getImap()->getNumberOfMessages();
$mailboxInfo['Nmsgs'] = $totalMsgs;
if ($sortOrder === 0) {
// Ascending order
if ($offset === "end") {
$firstMsg = $totalMsgs - (int)$pageSize;
$lastMsg = $totalMsgs;
} elseif ($offset <= 0) {
$firstMsg = 1;
$lastMsg = $firstMsg + (int)$pageSize;
} else {
$firstMsg = (int)$offset;
$lastMsg = $firstMsg + (int)$pageSize;
}
} else {
// Descending order
if ($offset === "end") {
$firstMsg = 1;
$lastMsg = $firstMsg + (int)$pageSize;
} elseif ($offset <= 0) {
$firstMsg = $totalMsgs - (int)$pageSize;
$lastMsg = $totalMsgs;
} else {
$offset = ($totalMsgs - (int)$offset) - (int)$pageSize;
$firstMsg = $offset;
$lastMsg = $firstMsg + (int)$pageSize;
}
}
$firstMsg = $firstMsg < 1 ? 1 : $firstMsg;
$firstMsg = $firstMsg > $totalMsgs ? $totalMsgs : $firstMsg;
$lastMsg = $lastMsg < $firstMsg ? $firstMsg : $lastMsg;
$lastMsg = $lastMsg > $totalMsgs ? $totalMsgs : $lastMsg;
$sequence = $firstMsg . ':' . $lastMsg;
$emailSortedHeaders = $this->getImap()->fetchOverview($sequence);
$uids = array_map(
function ($x) {
return $x->uid;
},
$emailSortedHeaders // TODO: this should be an array!
);
} else {
// Filtered case and other sorting cases
// Returns an array of msgno's which are sorted and filtered
$emailSortedHeaders = $this->getImap()->sort(
$sortCriteria,
$sortOrder,
SE_UID,
$filterCriteria
);
$uids = array_slice($emailSortedHeaders, $offset, $pageSize);
$lastSequenceNumber = $mailboxInfo['Nmsgs'] = count($emailSortedHeaders);
// paginate
if ($offset === "end") {
$offset = $lastSequenceNumber - $pageSize;
} elseif ($offset <= 0) {
$offset = 0;
}
}
// TODO: uids could be invalid for implode!
$uids = implode(',', $uids);
// Get result
$emailHeaders = $this->getImap()->fetchOverview(
$uids,
FT_UID
);
$emailHeaders = json_decode(json_encode($emailHeaders), true);
if (isset($columns['has_attachment'])) {
// get attachment status
foreach ($emailHeaders as $i=> $emailHeader) {
$structure = $this->getImap()->fetchStructure($emailHeader['uid'], FT_UID);
$emailHeaders[$i]['has_attachment'] = $this->mesageStructureHasAttachment($structure);
}
}
// TODO: parameter 1 could be a bool but it should be an array!
usort(
$emailHeaders,
function ($a, $b) use ($sortCRM) { // defaults to DESC order
if ($a[$sortCRM] === $b[$sortCRM]) {
return 0;
} elseif ($a[$sortCRM] < $b[$sortCRM]) {
return 1;
}
return -1;
}
);
// Make it ASC order
if (!$sortOrder) {
array_reverse($emailHeaders);
};
return array(
"data" => $emailHeaders,
"mailbox_info" => json_decode(json_encode($mailboxInfo), true),
);
}
/**
* @param $imapStructure
* @return bool
*/
private function mesageStructureHasAttachment($imapStructure)
{
if (!isset($imapStructure->parts)
&& isset($imapStructure->disposition)
&& $imapStructure->disposition == 'attachment') {
return true;
}
if (isset($imapStructure->parts)) {
foreach ($imapStructure->parts as $part) {
if ($this->mesageStructureHasAttachment($part)) {
return true;
}
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////
//// CUSTOM LOGIC HOOKS
/**
* Called from $this->getMessageText()
* Allows upgrade-safe custom processing of message text.
*
* To use:
* 1. Create a directory path: ./custom/modules/InboundEmail if it does not exist
* 2. Create a file in the ./custom/InboundEmail/ folder called "getMessageText.php"
* 3. Define a function named "custom_getMessageText()" that takes a string as an argument and returns a string
*
* @param string $msgPart
* @return string
*/
public function customGetMessageText($msgPart)
{
$custom = "custom/modules/InboundEmail/getMessageText.php";
if (file_exists($custom)) {
include_once($custom);
if (function_exists("custom_getMessageText")) {
$GLOBALS['log']->debug("*** INBOUND EMAIL-CUSTOM_LOGIC: calling custom_getMessageText()");
$msgPart = custom_getMessageText($msgPart);
}
}
return $msgPart;
}
//// END CUSTOM LOGIC HOOKS
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//// EMAIL 2.0 SPECIFIC
/**
* constructs a nicely formatted version of raw source
* @param int $uid UID of email
* @return string
*/
public function getFormattedRawSource($uid)
{
global $app_strings;
if (empty($this->id)) {
$q = "SELECT raw_source FROM emails_text WHERE email_id = '{$uid}'";
$r = $this->db->query($q);
$a = $this->db->fetchByAssoc($r);
$ret = array();
$raw = $this->convertToUtf8($a['raw_source']);
if (empty($raw)) {
$raw = $app_strings['LBL_EMAIL_ERROR_VIEW_RAW_SOURCE'];
}
} else {
if ($this->isPop3Protocol()) {
$uid = $this->getCorrectMessageNoForPop3($uid);
}
if (!is_resource($this->conn)) {
LoggerManager::getLogger()->fatal('Inbound Email connection is not a resource for getting Formatted Raw Source');
return null;
}
$raw = $this->getImap()->fetchHeader($uid, FT_UID + FT_PREFETCHTEXT);
$raw .= $this->convertToUtf8($this->getImap()->getBody($uid, FT_UID));
} // else
$raw = to_html($raw);
$raw = nl2br($raw);
//}
return $raw;
}
/**
* helper method to convert text to utf-8 if necessary
*
* @param string $input text
* @return string output text
*/
public function convertToUtf8($input)
{
$charset = $GLOBALS['locale']->detectCharset($input, true);
// we haven't a clue due to missing package?, just return as itself
if ($charset === false) {
return $input;
}
// convert if we can or must
return $this->handleCharsetTranslation($input, $charset);
}
/**
* constructs a nicely formatted version of email headers.
* @param int $uid
* @return string
*/
public function getFormattedHeaders($uid)
{
global $app_strings;
//if($this->protocol == 'pop3') {
// $header = $app_strings['LBL_EMAIL_VIEW_UNSUPPORTED'];
//} else {
if ($this->isPop3Protocol()) {
$uid = $this->getCorrectMessageNoForPop3($uid);
}
if (!is_resource($this->conn)) {
LoggerManager::getLogger()->fatal('Inbound Email connection is not a resource');
return null;
}
$headers = $this->getImap()->fetchHeader($uid, FT_UID);
$lines = explode("\n", $headers);
$header = "