', '|', '$',);
var $currentCache;
var $defaultSort = 'date';
var $defaultDirection = "DESC";
var $hrSort = array(
0 => 'flagged',
1 => 'status',
2 => 'from',
3 => 'subj',
4 => 'date',
);
var $hrSortLocal = array(
'flagged' => 'flagged',
'status' => 'answered',
'from' => 'fromaddr',
'subject' => 'subject',
'date' => 'senddate',
);
// default attributes
var $transferEncoding = array(0 => '7BIT',
1 => '8BIT',
2 => 'BINARY',
3 => 'BASE64',
4 => 'QUOTED-PRINTABLE',
5 => 'OTHER'
);
// object attributes
var $compoundMessageId; // concatenation of messageID and deliveredToEmail
var $serverConnectString;
var $disable_row_level_security = true;
var $InboundEmailCachePath;
var $InboundEmailCacheFile = 'InboundEmail.cache.php';
var $object_name = 'InboundEmail';
var $module_dir = 'InboundEmail';
var $table_name = 'inbound_email';
var $new_schema = true;
var $process_save_dates = true;
var $order_by;
var $db;
var $dbManager;
var $field_defs;
var $column_fields;
var $required_fields = array('name' => 'name',
'server_url' => 'server_url',
'mailbox' => 'mailbox',
'user' => 'user',
'port' => 'port',
);
var $imageTypes = array("JPG", "JPEG", "GIF", "PNG");
var $inlineImages = array(); // temporary space to store ID of inlined images
var $defaultEmailNumAutoreplies24Hours = 10;
var $maxEmailNumAutoreplies24Hours = 10;
// custom ListView attributes
var $mailbox_type_name;
var $global_personal_string;
// service attributes
var $tls;
var $ca;
var $ssl;
var $protocol;
var $keyForUsersDefaultIEAccount = 'defaultIEAccount';
// prefix to use when importing inlinge images in emails
public $imagePrefix;
/**
* Sole constructor
*/
function InboundEmail() {
$this->InboundEmailCachePath = sugar_cached('modules/InboundEmail');
$this->EmailCachePath = sugar_cached('modules/Emails');
parent::SugarBean();
if(function_exists("imap_timeout")) {
/*
* 1: Open
* 2: Read
* 3: Write
* 4: Close
*/
imap_timeout(1, 60);
imap_timeout(2, 60);
imap_timeout(3, 60);
}
$this->smarty = new Sugar_Smarty();
$this->overview = new Overview();
$this->imagePrefix = "{$GLOBALS['sugar_config']['site_url']}/cache/images/";
}
/**
* retrieves I-E bean
* @param string id
* @return object Bean
*/
function retrieve($id, $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
*/
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;
}
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
*/
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
//$q = "update inbound_email set groupfolder_id = null WHERE id = '{$id}'";
//$r = $this->db->query($q);
$this->deleteCache();
}
/**
* Mark cached email answered (replied)
* @param string $mailid (uid for imap, message_id for pop3)
*/
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
*/
function renameFolder($oldName, $newName) {
//$this->mailbox = "INBOX"
$this->connectMailserver();
$oldConnect = $this->getConnectString('', $oldName);
$newConnect = $this->getConnectString('', $newName);
if(!imap_renamemailbox($this->conn, $oldConnect , $newConnect)) {
$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);
}
}
///////////////////////////////////////////////////////////////////////////
//// 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
*/
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
*/
function getFormattedRawSource($uid) {
global $app_strings;
//if($this->protocol == 'pop3') {
//$raw = $app_strings['LBL_EMAIL_VIEW_UNSUPPORTED'];
//} else {
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);
}
$raw = imap_fetchheader($this->conn, $uid, FT_UID+FT_PREFETCHTEXT);
$raw .= $this->convertToUtf8(imap_body($this->conn, $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
*/
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
*/
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);
}
$headers = imap_fetchheader($this->conn, $uid, FT_UID);
$lines = explode("\n", $headers);
$header = "
";
foreach($lines as $line) {
$line = trim($line);
if(!empty($line)) {
$key = trim(substr($line, 0, strpos($line, ":")));
$key = strip_tags($key);
$value = trim(substr($line, strpos($line, ":") + 1));
$value = to_html($value);
$header .= "";
$header .= "{$key} | ";
$header .= "{$value} | ";
$header .= "
";
}
}
$header .= "
";
//}
return $header;
}
/**
* Empties Trash folders
*/
function emptyTrash() {
global $sugar_config;
$this->mailbox = $this->get_stored_options("trashFolder");
if (empty($this->mailbox)) {
$this->mailbox = 'INBOX.Trash';
}
$this->connectMailserver();
$uids = imap_search($this->conn, "ALL", SE_UID);
foreach($uids as $uid) {
if(!imap_delete($this->conn, $uid, FT_UID)) {
$lastError = imap_last_error();
$GLOBALS['log']->warn("INBOUNDEMAIL: emptyTrash() Could not delete message [ {$uid} ] from [ {$this->mailbox} ]. IMAP_ERROR [ {$lastError} ]");
}
}
// remove local cache file
$q = "DELETE FROM email_cache WHERE mbox = '{$this->mailbox}' AND ie_id = '{$this->id}'";
$r = $this->db->query($q);
}
/**
* Fetches a timestamp
*/
function getCacheTimestamp($mbox) {
$key = $this->db->quote("{$this->id}_{$mbox}");
$q = "SELECT ie_timestamp FROM inbound_email_cache_ts WHERE id = '{$key}'";
$r = $this->db->query($q);
$a = $this->db->fetchByAssoc($r);
if(empty($a)) {
return -1;
}
return $a['ie_timestamp'];
}
/**
* sets the cache timestamp
* @param string mbox
*/
function setCacheTimestamp($mbox) {
$key = $this->db->quote("{$this->id}_{$mbox}");
$ts = mktime();
$tsOld = $this->getCacheTimestamp($mbox);
if($tsOld < 0) {
$q = "INSERT INTO inbound_email_cache_ts (id, ie_timestamp) VALUES ('{$key}', {$ts})";
} else {
$q = "UPDATE inbound_email_cache_ts SET ie_timestamp = {$ts} WHERE id = '{$key}'";
}
$r = $this->db->query($q, true);
$GLOBALS['log']->info("INBOUNDEMAIL-CACHE: setting timestamp query [ {$q} ]");
}
/**
* Gets a count of all rows that are flagged seen = 0
* @param string $mbox
* @return int
*/
function getCacheUnreadCount($mbox) {
$q = "SELECT count(*) c FROM email_cache WHERE mbox = '{$mbox}' AND seen = 0 AND ie_id = '{$this->id}'";
$r = $this->db->query($q);
$a = $this->db->fetchByAssoc($r);
return $a['c'];
}
/**
* Returns total number of emails for a mailbox
* @param string mbox
* @return int
*/
function getCacheCount($mbox) {
$q = "SELECT count(*) c FROM email_cache WHERE mbox = '{$mbox}' AND ie_id = '{$this->id}'";
$r = $this->db->query($q);
$a = $this->db->fetchByAssoc($r);
return $a['c'];
}
function getCacheUnread($mbox) {
$q = "SELECT count(*) c FROM email_cache WHERE mbox = '{$mbox}' AND ie_id = '{$this->id}' AND seen = '0'";
$r = $this->db->query($q);
$a = $this->db->fetchByAssoc($r);
return $a['c'];
}
/**
* Deletes all rows for a given instance
*/
function deleteCache() {
$q = "DELETE FROM email_cache WHERE ie_id = '{$this->id}'";
$GLOBALS['log']->info("INBOUNDEMAIL: deleting cache using query [ {$q} ]");
$r = $this->db->query($q);
}
/**
* Deletes all the pop3 data which has been deleted from server
*/
function deletePop3Cache() {
global $sugar_config;
$UIDLs = $this->pop3_getUIDL();
$cacheUIDLs = $this->pop3_getCacheUidls();
foreach($cacheUIDLs as $msgNo => $msgId) {
if (!in_array($msgId, $UIDLs)) {
$md5msgIds = md5($msgId);
$file = "{$this->EmailCachePath}/{$this->id}/messages/INBOX{$md5msgIds}.PHP";
$GLOBALS['log']->debug("INBOUNDEMAIL: deleting file [ {$file} ] ");
if(file_exists($file)) {
if(!unlink($file)) {
$GLOBALS['log']->debug("INBOUNDEMAIL: Could not delete [ {$file} ] ");
} // if
} // if
$q = "DELETE from email_cache where imap_uid = {$msgNo} AND msgno = {$msgNo} AND ie_id = '{$this->id}' AND message_id = '{$msgId}'";
$r = $this->db->query($q);
} // if
} // for
} // fn
/**
* Retrieves cached headers
* @return array
*/
function getCacheValueForUIDs($mbox, $UIDs) {
if (!is_array($UIDs) || empty($UIDs)) {
return array();
}
$q = "SELECT * FROM email_cache WHERE ie_id = '{$this->id}' AND mbox = '{$mbox}' AND ";
$startIndex = 0;
$endIndex = 5;
$slicedArray = array_slice($UIDs, $startIndex ,$endIndex);
$columnName = ($this->isPop3Protocol() ? "message_id" : "imap_uid");
$ret = array(
'timestamp' => $this->getCacheTimestamp($mbox),
'uids' => array(),
'retArr' => array(),
);
while (!empty($slicedArray)) {
$messageIdString = implode(',', $slicedArray);
$GLOBALS['log']->debug("sliced array = {$messageIdString}");
$extraWhere = "{$columnName} IN (";
$i = 0;
foreach($slicedArray as $UID) {
if($i != 0) {
$extraWhere = $extraWhere . ",";
} // if
$i++;
$extraWhere = "{$extraWhere} '{$UID}'";
} // foreach
$newQuery = $q . $extraWhere . ")";
$r = $this->db->query($newQuery);
while($a = $this->db->fetchByAssoc($r)) {
if (isset($a['uid'])) {
if ($this->isPop3Protocol()) {
$ret['uids'][] = $a['message_id'];
} else {
$ret['uids'][] = $a['uid'];
}
}
$overview = new Overview();
foreach($a as $k => $v) {
$k=strtolower($k);
switch($k) {
case "imap_uid":
$overview->imap_uid = $v;
if ($this->isPop3Protocol()) {
$overview->uid = $a['message_id'];
} else {
$overview->uid = $v;
}
break;
case "toaddr":
$overview->to = from_html($v);
break;
case "fromaddr":
$overview->from = from_html($v);
break;
case "mailsize":
$overview->size = $v;
break;
case "senddate":
$overview->date = $v;
break;
default:
$overview->$k = from_html($v);
break;
} // switch
} // foreach
$ret['retArr'][] = $overview;
} // while
$startIndex = $startIndex + $endIndex;
$slicedArray = array_slice($UIDs, $startIndex ,$endIndex);
$messageIdString = implode(',', $slicedArray);
$GLOBALS['log']->debug("sliced array = {$messageIdString}");
} // while
return $ret;
}
/**
* Retrieves cached headers
* @return array
*/
function getCacheValue($mbox, $limit = 20, $page = 1, $sort='', $direction='') {
// try optimizing this call as we don't want repeat queries
if(!empty($this->currentCache)) {
return $this->currentCache;
}
$sort = (empty($sort)) ? $this->defaultSort : $sort;
$direction = (empty($direction)) ? $this->defaultDirection : $direction;
$order = " ORDER BY {$this->hrSortLocal[$sort]} {$direction}";
$q = "SELECT * FROM email_cache WHERE ie_id = '{$this->id}' AND mbox = '{$mbox}' {$order}";
if(!empty($limit)) {
$start = ( $page - 1 ) * $limit;
$r = $this->db->limitQuery($q, $start, $limit);
} else {
$r = $this->db->query($q);
}
$ret = array(
'timestamp' => $this->getCacheTimestamp($mbox),
'uids' => array(),
'retArr' => array(),
);
while($a = $this->db->fetchByAssoc($r)) {
if (isset($a['uid'])) {
if ($this->isPop3Protocol()) {
$ret['uids'][] = $a['message_id'];
} else {
$ret['uids'][] = $a['uid'];
}
}
$overview = new Overview();
foreach($a as $k => $v) {
$k=strtolower($k);
switch($k) {
case "imap_uid":
$overview->imap_uid = $v;
if ($this->isPop3Protocol()) {
$overview->uid = $a['message_id'];
} else {
$overview->uid = $v;
}
break;
case "toaddr":
$overview->to = from_html($v);
break;
case "fromaddr":
$overview->from = from_html($v);
break;
case "mailsize":
$overview->size = $v;
break;
case "senddate":
$overview->date = $v;
break;
default:
$overview->$k = from_html($v);
break;
}
}
$ret['retArr'][] = $overview;
}
$this->currentCache = $ret;
return $ret;
}
/**
* Sets cache values
*/
function setCacheValue($mbox, $insert, $update=array(), $remove=array()) {
if(empty($mbox)) {
return;
}
global $timedate;
// reset in-memory cache
$this->currentCache = null;
$table = 'email_cache';
$where = "WHERE ie_id = '{$this->id}' AND mbox = '{$mbox}'";
// handle removed rows
if(!empty($remove)) {
$removeIds = '';
foreach($remove as $overview) {
if(!empty($removeIds)) {
$removeIds .= ",";
}
$removeIds .= "'{$overview->imap_uid}'";
}
$q = "DELETE FROM {$table} {$where} AND imap_uid IN ({$removeIds})";
$GLOBALS['log']->info("INBOUNDEMAIL-CACHE: delete query [ {$q} ]");
$r = $this->db->query($q, true, $q);
}
// handle insert rows
if(!empty($insert)) {
$q = "SELECT imap_uid FROM {$table} {$where}";
$GLOBALS['log']->info("INBOUNDEMAIL-CACHE: filter UIDs query [ {$q} ]");
$r = $this->db->query($q);
$uids = array();
while($a = $this->db->fetchByAssoc($r)) {
$uids[] = $a['imap_uid'];
}
$count = count($uids);
$GLOBALS['log']->info("INBOUNDEMAIL-CACHE: found [ {$count} ] UIDs to filter against");
$tmp = '';
foreach($uids as $uid) {
if(!empty($tmp))
$tmp .= ", ";
$tmp .= "{$uid}";
}
$GLOBALS['log']->info("INBOUNDEMAIL-CACHE: filter UIDs: [ {$tmp} ]");
$cols = "";
foreach($this->overview->fieldDefs as $colDef) {
if(!empty($cols))
$cols .= ",";
$cols .= "{$colDef['name']}";
}
foreach($insert as $overview) {
if(in_array($overview->imap_uid, $uids))
{
// fixing bug #49543: setting 'mbox' property for the following updating of other items in this box
if (!isset($overview->mbox))
{
$overview->mbox = $mbox;
}
$update[] = $overview;
continue;
}
$values = '';
foreach($this->overview->fieldDefs as $colDef) {
if(!empty($values)) {
$values .= ", ";
}
// trim values for Oracle/MSSql
if( isset($colDef['len']) && !empty($colDef['len']) &&
isset($colDef['type']) && !empty($colDef['type']) &&
$colDef['type'] == 'varchar'
)
{
if (isset($overview->$colDef['name']))
{
$overview->$colDef['name'] = substr($overview->$colDef['name'], 0, $colDef['len']);
}
}
switch($colDef['name']) {
case "imap_uid":
if(isset($overview->uid) && !empty($overview->uid)) {
$this->imap_uid = $overview->uid;
}
$values .= "'{$this->imap_uid}'";
break;
case "ie_id":
$values .= "'{$this->id}'";
break;
case "toaddr":
$values .= $this->db->quoted($overview->to);
break;
case "fromaddr":
$values .= $this->db->quoted($overview->from);
break;
case "message_id" :
$values .= $this->db->quoted($overview->message_id);
break;
case "mailsize":
$values .= $overview->size;
break;
case "senddate":
$conv=$timedate->fromString($overview->date);
if (!empty($conv)) {
$values .= $this->db->quoted($conv->asDb());
} else {
$values .= "NULL";
}
break;
case "mbox":
$values .= "'{$mbox}'";
break;
default:
$overview->$colDef['name'] = SugarCleaner::cleanHtml(from_html($overview->$colDef['name']));
$values .= $this->db->quoted($overview->$colDef['name']);
break;
}
}
$q = "INSERT INTO {$table} ({$cols}) VALUES ({$values})";
$GLOBALS['log']->info("INBOUNDEMAIL-CACHE: insert query [ {$q} ]");
$r = $this->db->query($q, true, $q);
}
}
// handle update rows
if(!empty($update)) {
$cols = "";
foreach($this->overview->fieldDefs as $colDef) {
if(!empty($cols))
$cols .= ",";
$cols .= "{$colDef['name']}";
}
foreach($update as $overview) {
$q = "UPDATE {$table} SET ";
$set = '';
foreach($this->overview->fieldDefs as $colDef) {
switch($colDef['name']) {
case "toaddr":
case "fromaddr":
case "mailsize":
case "senddate":
case "mbox":
case "ie_id":
break;
default:
if(!empty($set))
{
$set .= ",";
}
$value = '';
if (isset($overview->$colDef['name']))
{
$value = $this->db->quoted($overview->$colDef['name']);
}
else
{
$value = $this->db->quoted($value);
}
$set .= "{$colDef['name']} = " . $value;
break;
}
}
$q .= $set . " WHERE ie_id = '{$this->id}' AND mbox = '{$overview->mbox}' AND imap_uid = '{$overview->imap_uid}'";
$GLOBALS['log']->info("INBOUNDEMAIL-CACHE: update query [ {$q} ]");
$r = $this->db->query($q, true, $q);
}
}
}
/**
* Opens a socket connection to the pop3 server
* @return bool
*/
function pop3_open() {
if(!is_resource($this->pop3socket)) {
$GLOBALS['log']->info("*** INBOUNDEMAIL: opening socket connection");
$exServ = explode('::', $this->service);
$socket = ($exServ[2] == 'ssl') ? "ssl://" : "tcp://";
$socket .= $this->server_url;
$this->pop3socket = fsockopen($socket, $this->port);
} else {
$GLOBALS['log']->info("*** INBOUNDEMAIL: REUSING socket connection");
return true;
}
if(!is_resource($this->pop3socket)) {
$GLOBALS['log']->debug("*** INBOUNDEMAIL: unable to open socket connection");
return false;
}
// clear buffer
$ret = trim(fgets($this->pop3socket, 1024));
$GLOBALS['log']->info("*** INBOUNDEMAIL: got socket connection [ {$ret} ]");
return true;
}
/**
* Closes connections and runs clean-up routines
*/
function pop3_cleanUp() {
$GLOBALS['log']->info("*** INBOUNDEMAIL: cleaning up socket connection");
fputs($this->pop3socket, "QUIT\r\n");
$buf = fgets($this->pop3socket, 1024);
fclose($this->pop3socket);
}
/**
* sends a command down to the POP3 server
* @param string command
* @param string args
* @param bool return
* @return string
*/
function pop3_sendCommand($command, $args='', $return=true) {
$command .= " {$args}";
$command = trim($command);
$GLOBALS['log']->info("*** INBOUNDEMAIL: pop3_sendCommand() SEND [ {$command} ]");
$command .= "\r\n";
fputs($this->pop3socket, $command);
if($return) {
$ret = trim(fgets($this->pop3socket, 1024));
$GLOBALS['log']->info("*** INBOUNDEMAIL: pop3_sendCommand() RECEIVE [ {$ret} ]");
return $ret;
}
}
function getPop3NewMessagesToDownload() {
$pop3UIDL = $this->pop3_getUIDL();
$cacheUIDLs = $this->pop3_getCacheUidls();
// new email cache values we should deal with
$diff = array_diff_assoc($pop3UIDL, $cacheUIDLs);
// this is msgNo to UIDL array
$diff = $this->pop3_shiftCache($diff, $cacheUIDLs);
// get all the keys which are msgnos;
return array_keys($diff);
}
function getPop3NewMessagesToDownloadForCron() {
$pop3UIDL = $this->pop3_getUIDL();
$cacheUIDLs = $this->pop3_getCacheUidls();
// new email cache values we should deal with
$diff = array_diff_assoc($pop3UIDL, $cacheUIDLs);
// this is msgNo to UIDL array
$diff = $this->pop3_shiftCache($diff, $cacheUIDLs);
// insert data into email_cache
if ($this->groupfolder_id != null && $this->groupfolder_id != "" && $this->isPop3Protocol()) {
$searchResults = array_keys($diff);
$concatResults = implode(",", $searchResults);
if ($this->connectMailserver() == 'true') {
$fetchedOverviews = imap_fetch_overview($this->conn, $concatResults);
// clean up cache entry
foreach($fetchedOverviews as $k => $overview) {
$overview->message_id = trim($diff[$overview->msgno]);
$fetchedOverviews[$k] = $overview;
}
$this->updateOverviewCacheFile($fetchedOverviews);
}
} // if
return $diff;
}
/**
* This method returns all the UIDL for this account. This should be called if the protocol is pop3
* @return array od messageno to UIDL array
*/
function pop3_getUIDL() {
$UIDLs = array();
if($this->pop3_open()) {
// authenticate
$this->pop3_sendCommand("USER", $this->email_user);
$this->pop3_sendCommand("PASS", $this->email_password);
// get UIDLs
$this->pop3_sendCommand("UIDL", '', false); // leave socket buffer alone until the while()
fgets($this->pop3socket, 1024); // handle "OK+";
$UIDLs = array();
$buf = '!';
if(is_resource($this->pop3socket)) {
while(!feof($this->pop3socket)) {
$buf = fgets($this->pop3socket, 1024); // 8kb max buffer - shouldn't be more than 80 chars via pop3...
//_pp(trim($buf));
if(trim($buf) == '.') {
$GLOBALS['log']->debug("*** GOT '.'");
break;
}
// format is [msgNo] [UIDL]
$exUidl = explode(" ", $buf);
$UIDLs[$exUidl[0]] = trim($exUidl[1]);
} // while
} // if
$this->pop3_cleanUp();
} // if
return $UIDLs;
} // fn
/**
* Special handler for POP3 boxes. Standard IMAP commands are useless.
* This will fetch only partial emails for POP3 and hence needs to be call again and again based on status it returns
*/
function pop3_checkPartialEmail($synch = false) {
require_once('include/utils/array_utils.php');
global $current_user;
global $sugar_config;
$cacheDataExists = false;
$diff = array();
$results = array();
$cacheFilePath = clean_path("{$this->EmailCachePath}/{$this->id}/folders/MsgNOToUIDLData.php");
if(file_exists($cacheFilePath)) {
$cacheDataExists = true;
if($fh = @fopen($cacheFilePath, "rb")) {
$data = "";
$chunksize = 1*(1024*1024); // how many bytes per chunk
while(!feof($fh)) {
$buf = fgets($fh, $chunksize); // 8kb max buffer - shouldn't be more than 80 chars via pop3...
$data = $data . $buf;
flush();
} // while
fclose($fh);
$diff = unserialize($data);
if (!empty($diff)) {
if (count($diff)> 50) {
$newDiff = array_slice($diff, 50, count($diff), true);
} else {
$newDiff=array();
}
$results = array_slice(array_keys($diff), 0 ,50);
$data = serialize($newDiff);
if($fh = @fopen($cacheFilePath, "w")) {
fputs($fh, $data);
fclose($fh);
} // if
}
} // if
} // if
if (!$cacheDataExists) {
if ($synch) {
$this->deletePop3Cache();
}
$UIDLs = $this->pop3_getUIDL();
if(count($UIDLs) > 0) {
// get cached UIDLs
$cacheUIDLs = $this->pop3_getCacheUidls();
// new email cache values we should deal with
$diff = array_diff_assoc($UIDLs, $cacheUIDLs);
$diff = $this->pop3_shiftCache($diff, $cacheUIDLs);
require_once('modules/Emails/EmailUI.php');
EmailUI::preflightEmailCache("{$this->EmailCachePath}/{$this->id}");
if (count($diff)> 50) {
$newDiff = array_slice($diff, 50, count($diff), true);
} else {
$newDiff=array();
}
$results = array_slice(array_keys($diff), 0 ,50);
$data = serialize($newDiff);
if($fh = @fopen($cacheFilePath, "w")) {
fputs($fh, $data);
fclose($fh);
} // if
} else {
$GLOBALS['log']->debug("*** INBOUNDEMAIL: could not open socket connection to POP3 server");
return "could not open socket connection to POP3 server";
} // else
} // if
// build up msgNo request
if(count($diff) > 0) {
// remove dirty cache entries
$startingNo = 0;
if (isset($_REQUEST['currentCount']) && $_REQUEST['currentCount'] > -1) {
$startingNo = $_REQUEST['currentCount'];
}
$this->mailbox = 'INBOX';
$this->connectMailserver();
//$searchResults = array_keys($diff);
//$fetchedOverviews = array();
//$chunkArraySerachResults = array_chunk($searchResults, 50);
$concatResults = implode(",", $results);
$GLOBALS['log']->info('$$$$ '.$concatResults);
$GLOBALS['log']->info("[EMAIL] Start POP3 fetch overview on mailbox [{$this->mailbox}] for user [{$current_user->user_name}] on 50 data");
$fetchedOverviews = imap_fetch_overview($this->conn, $concatResults);
$GLOBALS['log']->info("[EMAIL] End POP3 fetch overview on mailbox [{$this->mailbox}] for user [{$current_user->user_name}] on "
. sizeof($fetchedOverviews) . " data");
// clean up cache entry
foreach($fetchedOverviews as $k => $overview) {
$overview->message_id = trim($diff[$overview->msgno]);
$fetchedOverviews[$k] = $overview;
}
$GLOBALS['log']->info("[EMAIL] Start updating overview cache for pop3 mailbox [{$this->mailbox}] for user [{$current_user->user_name}]");
$this->updateOverviewCacheFile($fetchedOverviews);
$GLOBALS['log']->info("[EMAIL] Start updating overview cache for pop3 mailbox [{$this->mailbox}] for user [{$current_user->user_name}]");
return array('status' => "In Progress", 'mbox' => $this->mailbox, 'count'=> (count($results) + $startingNo), 'totalcount' => count($diff), 'ieid' => $this->id);
} // if
unlink($cacheFilePath);
return array('status' => "done");
}
/**
* Special handler for POP3 boxes. Standard IMAP commands are useless.
*/
function pop3_checkEmail() {
if($this->pop3_open()) {
// authenticate
$this->pop3_sendCommand("USER", $this->email_user);
$this->pop3_sendCommand("PASS", $this->email_password);
// get UIDLs
$this->pop3_sendCommand("UIDL", '', false); // leave socket buffer alone until the while()
fgets($this->pop3socket, 1024); // handle "OK+";
$UIDLs = array();
$buf = '!';
if(is_resource($this->pop3socket)) {
while(!feof($this->pop3socket)) {
$buf = fgets($this->pop3socket, 1024); // 8kb max buffer - shouldn't be more than 80 chars via pop3...
//_pp(trim($buf));
if(trim($buf) == '.') {
$GLOBALS['log']->debug("*** GOT '.'");
break;
}
// format is [msgNo] [UIDL]
$exUidl = explode(" ", $buf);
$UIDLs[$exUidl[0]] = trim($exUidl[1]);
}
}
$this->pop3_cleanUp();
// get cached UIDLs
$cacheUIDLs = $this->pop3_getCacheUidls();
// _pp($UIDLs);_pp($cacheUIDLs);
// new email cache values we should deal with
$diff = array_diff_assoc($UIDLs, $cacheUIDLs);
// remove dirty cache entries
$diff = $this->pop3_shiftCache($diff, $cacheUIDLs);
// build up msgNo request
if(!empty($diff)) {
$this->mailbox = 'INBOX';
$this->connectMailserver();
$searchResults = array_keys($diff);
$concatResults = implode(",", $searchResults);
$fetchedOverviews = imap_fetch_overview($this->conn, $concatResults);
// clean up cache entry
foreach($fetchedOverviews as $k => $overview) {
$overview->message_id = trim($diff[$overview->msgno]);
$fetchedOverviews[$k] = $overview;
}
$this->updateOverviewCacheFile($fetchedOverviews);
}
} else {
$GLOBALS['log']->debug("*** INBOUNDEMAIL: could not open socket connection to POP3 server");
return false;
}
}
/**
* Iterates through msgno and message_id to remove dirty cache entries
* @param array diff
*/
function pop3_shiftCache($diff, $cacheUIDLs) {
$msgNos = "";
$msgIds = "";
$newArray = array();
foreach($diff as $msgNo => $msgId) {
if (in_array($msgId, $cacheUIDLs)) {
$q1 = "UPDATE email_cache SET imap_uid = {$msgNo}, msgno = {$msgNo} WHERE ie_id = '{$this->id}' AND message_id = '{$msgId}'";
$this->db->query($q1);
} else {
$newArray[$msgNo] = $msgId;
}
}
return $newArray;
/*
foreach($diff as $msgNo => $msgId) {
if(!empty($msgNos)) {
$msgNos .= ", ";
}
if(!empty($msgIds)) {
$msgIds .= ", ";
}
$msgNos .= $msgNo;
$msgIds .= "'{$msgId}'";
}
if(!empty($msgNos)) {
$q1 = "DELETE FROM email_cache WHERE ie_id = '{$this->id}' AND msgno IN ({$msgNos})";
$this->db->query($q1);
}
if(!empty($msgIds)) {
$q2 = "DELETE FROM email_cache WHERE ie_id = '{$this->id}' AND message_id IN ({$msgIds})";
$this->db->query($q2);
}
*/
}
/**
* retrieves cached uidl values.
* When dealing with POP3 accounts, the message_id column in email_cache will contain the UIDL.
* @return array
*/
function pop3_getCacheUidls() {
$q = "SELECT msgno, message_id FROM email_cache WHERE ie_id = '{$this->id}'";
$r = $this->db->query($q);
$ret = array();
while($a = $this->db->fetchByAssoc($r)) {
$ret[$a['msgno']] = $a['message_id'];
}
return $ret;
}
/**
* This function is used by cron job for group mailbox without group folder
* @param string $msgno for pop
* @param string $uid for imap
*/
function getMessagesInEmailCache($msgno, $uid) {
$fetchedOverviews = array();
if ($this->isPop3Protocol()) {
$fetchedOverviews = imap_fetch_overview($this->conn, $msgno);
foreach($fetchedOverviews as $k => $overview) {
$overview->message_id = $uid;
$fetchedOverviews[$k] = $overview;
}
} else {
$fetchedOverviews = imap_fetch_overview($this->conn, $uid, FT_UID);
} // else
$this->updateOverviewCacheFile($fetchedOverviews);
} // fn
/**
* Checks email (local caching too) for one mailbox
* @param string $mailbox IMAP Mailbox path
* @param bool $prefetch Flag to prefetch email body on check
*/
function checkEmailOneMailbox($mailbox, $prefetch=true, $synchronize=false) {
global $sugar_config;
global $current_user;
global $app_strings;
$result = 1;
$GLOBALS['log']->info("INBOUNDEMAIL: checking mailbox [ {$mailbox} ]");
$this->mailbox = $mailbox;
$this->connectMailserver();
$checkTime = '';
$shouldProcessRules = true;
$timestamp = $this->getCacheTimestamp($mailbox);
if($timestamp > 0) {
$checkTime = date('r', $timestamp);
}
/* first time through, process ALL emails */
if(empty($checkTime) || $synchronize) {
// do not process rules for the first time or sunchronize
$shouldProcessRules = false;
$criteria = "ALL UNDELETED";
$prefetch = false; // do NOT prefetch emails on a brand new account - timeouts happen.
$GLOBALS['log']->info("INBOUNDEMAIL: new account detected - not prefetching email bodies.");
} else {
$criteria = "SINCE \"{$checkTime}\" UNDELETED"; // not using UNSEEN
}
$this->setCacheTimestamp($mailbox);
$GLOBALS['log']->info("[EMAIL] Performing IMAP search using criteria [{$criteria}] on mailbox [{$mailbox}] for user [{$current_user->user_name}]");
$searchResults = imap_search($this->conn, $criteria, SE_UID);
$GLOBALS['log']->info("[EMAIL] Done IMAP search on mailbox [{$mailbox}] for user [{$current_user->user_name}]. Result count = ".count($searchResults));
if(!empty($searchResults)) {
$concatResults = implode(",", $searchResults);
$GLOBALS['log']->info("[EMAIL] Start IMAP fetch overview on mailbox [{$mailbox}] for user [{$current_user->user_name}]");
$fetchedOverview = imap_fetch_overview($this->conn, $concatResults, FT_UID);
$GLOBALS['log']->info("[EMAIL] Done IMAP fetch overview on mailbox [{$mailbox}] for user [{$current_user->user_name}]");
$GLOBALS['log']->info("[EMAIL] Start updating overview cache for mailbox [{$mailbox}] for user [{$current_user->user_name}]");
$this->updateOverviewCacheFile($fetchedOverview);
$GLOBALS['log']->info("[EMAIL] Done updating overview cache for mailbox [{$mailbox}] for user [{$current_user->user_name}]");
// prefetch emails
if($prefetch == true) {
$GLOBALS['log']->info("[EMAIL] Start fetching emails for mailbox [{$mailbox}] for user [{$current_user->user_name}]");
if(!$this->fetchCheckedEmails($fetchedOverview))
{
$result = 0;
}
$GLOBALS['log']->info("[EMAIL] Done fetching emails for mailbox [{$mailbox}] for user [{$current_user->user_name}]");
}
} else {
$GLOBALS['log']->info("INBOUNDEMAIL: no results for mailbox [ {$mailbox} ]");
$result = 1;
}
/**
* To handle the use case where an external client is also connected, deleting emails, we need to clear our
* local cache of all emails with the "DELETED" flag
*/
$criteria = 'DELETED';
$criteria .= (!empty($checkTime)) ? " SINCE \"{$checkTime}\"" : "";
$GLOBALS['log']->info("INBOUNDEMAIL: checking for deleted emails using [ {$criteria} ]");
$trashFolder = $this->get_stored_options("trashFolder");
if (empty($trashFolder)) {
$trashFolder = "INBOX.Trash";
}
if($this->mailbox != $trashFolder) {
$searchResults = imap_search($this->conn, $criteria, SE_UID);
if(!empty($searchResults)) {
$uids = implode($app_strings['LBL_EMAIL_DELIMITER'], $searchResults);
$GLOBALS['log']->info("INBOUNDEMAIL: removing UIDs found deleted [ {$uids} ]");
$this->getOverviewsFromCacheFile($uids, $mailbox, true);
}
}
return $result;
}
/**
* Checks email (local caching too) for one mailbox
* @param string $mailbox IMAP Mailbox path
* @param bool $prefetch Flag to prefetch email body on check
*/
function checkEmailOneMailboxPartial($mailbox, $prefetch=true, $synchronize=false, $start = 0, $max = -1) {
global $sugar_config;
global $current_user;
global $app_strings;
$GLOBALS['log']->info("INBOUNDEMAIL: checking mailbox [ {$mailbox} ]");
$this->mailbox = $mailbox;
$this->connectMailserver();
$checkTime = '';
$shouldProcessRules = true;
$timestamp = $this->getCacheTimestamp($mailbox);
if($timestamp > 0) {
$checkTime = date('r', $timestamp);
}
/* first time through, process ALL emails */
if(empty($checkTime) || $synchronize) {
// do not process rules for the first time or sunchronize
$shouldProcessRules = false;
$criteria = "ALL UNDELETED";
$prefetch = false; // do NOT prefetch emails on a brand new account - timeouts happen.
$GLOBALS['log']->info("INBOUNDEMAIL: new account detected - not prefetching email bodies.");
} else {
$criteria = "SINCE \"{$checkTime}\" UNDELETED"; // not using UNSEEN
}
$this->setCacheTimestamp($mailbox);
$GLOBALS['log']->info("[EMAIL] Performing IMAP search using criteria [{$criteria}] on mailbox [{$mailbox}] for user [{$current_user->user_name}]");
$searchResults = $this->getCachedIMAPSearch($criteria);
if(!empty($searchResults)) {
$total = sizeof($searchResults);
$searchResults = array_slice($searchResults, $start, $max);
$GLOBALS['log']->info("INBOUNDEMAIL: there are $total messages in [{$mailbox}], we are on $start");
$GLOBALS['log']->info("INBOUNDEMAIL: getting the next " . sizeof($searchResults) . " messages");
$concatResults = implode(",", $searchResults);
$GLOBALS['log']->info("INBOUNDEMAIL: Start IMAP fetch overview on mailbox [{$mailbox}] for user [{$current_user->user_name}]");
$fetchedOverview = imap_fetch_overview($this->conn, $concatResults, FT_UID);
$GLOBALS['log']->info("INBOUNDEMAIL: Done IMAP fetch overview on mailbox [{$mailbox}] for user [{$current_user->user_name}]");
$GLOBALS['log']->info("INBOUNDEMAIL: Start updating overview cache for mailbox [{$mailbox}] for user [{$current_user->user_name}]");
$this->updateOverviewCacheFile($fetchedOverview);
$GLOBALS['log']->info("INBOUNDEMAIL: Done updating overview cache for mailbox [{$mailbox}] for user [{$current_user->user_name}]");
// prefetch emails
if($prefetch == true) {
$GLOBALS['log']->info("INBOUNDEMAIL: Start fetching emails for mailbox [{$mailbox}] for user [{$current_user->user_name}]");
$this->fetchCheckedEmails($fetchedOverview);
$GLOBALS['log']->info("INBOUNDEMAIL: Done fetching emails for mailbox [{$mailbox}] for user [{$current_user->user_name}]");
}
$status = ($total > $start + sizeof($searchResults)) ? 'continue' : 'done';
$ret = array('status' => $status, 'count' => $start + sizeof($searchResults), 'mbox' => $mailbox, 'totalcount' => $total);
$GLOBALS['log']->info("INBOUNDEMAIL: $status : Downloaded " . $start + sizeof($searchResults) . "messages of $total");
} else {
$GLOBALS['log']->info("INBOUNDEMAIL: no results for mailbox [ {$mailbox} ]");
$ret = array('status' =>'done');
}
if ($ret['status'] == 'done') {
//Remove the cached search if we are done with this mailbox
$cacheFilePath = clean_path("{$this->EmailCachePath}/{$this->id}/folders/SearchData.php");
unlink($cacheFilePath);
/**
* To handle the use case where an external client is also connected, deleting emails, we need to clear our
* local cache of all emails with the "DELETED" flag
*/
$criteria = 'DELETED';
$criteria .= (!empty($checkTime)) ? " SINCE \"{$checkTime}\"" : "";
$GLOBALS['log']->info("INBOUNDEMAIL: checking for deleted emails using [ {$criteria} ]");
$trashFolder = $this->get_stored_options("trashFolder");
if (empty($trashFolder)) {
$trashFolder = "INBOX.Trash";
}
if($this->mailbox != $trashFolder) {
$searchResults = imap_search($this->conn, $criteria, SE_UID);
if(!empty($searchResults)) {
$uids = implode($app_strings['LBL_EMAIL_DELIMITER'], $searchResults);
$GLOBALS['log']->info("INBOUNDEMAIL: removing UIDs found deleted [ {$uids} ]");
$this->getOverviewsFromCacheFile($uids, $mailbox, true);
}
}
}
return $ret;
}
function getCachedIMAPSearch($criteria) {
global $current_user;
global $sugar_config;
$cacheDataExists = false;
$diff = array();
$results = array();
$cacheFolderPath = clean_path("{$this->EmailCachePath}/{$this->id}/folders");
if (!file_exists($cacheFolderPath)) {
mkdir_recursive($cacheFolderPath);
}
$cacheFilePath = $cacheFolderPath . '/SearchData.php';
$GLOBALS['log']->info("INBOUNDEMAIL: Cache path is $cacheFilePath");
if(file_exists($cacheFilePath)) {
$cacheDataExists = true;
if($fh = @fopen($cacheFilePath, "rb")) {
$data = "";
$chunksize = 1*(1024*1024); // how many bytes per chunk
while(!feof($fh)) {
$buf = fgets($fh, $chunksize); // 8kb max buffer - shouldn't be more than 80 chars via pop3...
$data = $data . $buf;
flush();
} // while
fclose($fh);
$results = unserialize($data);
} // if
} // if
if (!$cacheDataExists) {
$searchResults = imap_search($this->conn, $criteria, SE_UID);
if(count($searchResults) > 0) {
$results = $searchResults;
$data = serialize($searchResults);
if($fh = @fopen($cacheFilePath, "w")) {
fputs($fh, $data);
fclose($fh);
} // if
}
} // if
return $results;
}
function checkEmailIMAPPartial($prefetch=true, $synch = false) {
$GLOBALS['log']->info("*****************INBOUNDEMAIL: at IMAP check partial");
global $sugar_config;
$result = $this->connectMailserver();
if ($result == 'false')
{
return array(
'status' => 'error',
'message' => 'Email server is down'
);
}
$mailboxes = $this->getMailboxes(true);
if (!in_array('INBOX', $mailboxes)) {
$mailboxes[] = 'INBOX';
}
sort($mailboxes);
if (isset($_REQUEST['mbox']) && !empty($_REQUEST['mbox']) && isset($_REQUEST['currentCount'])) {
$GLOBALS['log']->info("INBOUNDEMAIL: Picking up from where we left off");
$mbox = $_REQUEST['mbox'];
$count = $_REQUEST['currentCount'];
} else {
if ($synch) {
$GLOBALS['log']->info("INBOUNDEMAIL: Cleaning out the cache");
$this->cleanOutCache();
}
$mbox = $mailboxes[0];
$count = 0;
}
$GLOBALS['log']->info("INBOUNDEMAIL:found " . sizeof($mailboxes) . " Mailboxes");
$index = array_search($mbox, $mailboxes) + 1;
$ret = $this->checkEmailOneMailboxPartial($mbox, $prefetch, $synch, $count, 100);
while($ret['status'] == 'done' && $index < sizeof($mailboxes)) {
if ($ret['count'] > 100) {
$ret['mbox'] = $mailboxes[$index];
$ret['status'] = 'continue';
return $ret;
}
$GLOBALS['log']->info("INBOUNDEMAIL: checking account [ $index => $mbox : $count]");
$mbox = $mailboxes[$index];
$ret = $this->checkEmailOneMailboxPartial($mbox, $prefetch, $synch, 0, 100);
$index++;
}
return $ret;
}
function checkEmail2_meta() {
global $sugar_config;
$this->connectMailserver();
$mailboxes = $this->getMailboxes(true);
$mailboxes[] = 'INBOX';
sort($mailboxes);
$GLOBALS['log']->info("INBOUNDEMAIL: checking account [ {$this->name} ]");
$mailboxes_meta = array();
foreach($mailboxes as $mailbox) {
$mailboxes_meta[$mailbox] = $this->getMailboxProcessCount($mailbox);
}
$ret = array();
$ret['mailboxes'] = $mailboxes_meta;
foreach($mailboxes_meta as $count) {
$ret['processCount'] += $count;
}
return $ret;
}
function getMailboxProcessCount($mailbox) {
global $sugar_config;
$GLOBALS['log']->info("INBOUNDEMAIL: checking mailbox [ {$mailbox} ]");
$this->mailbox = $mailbox;
$this->connectMailserver();
$timestamp = $this->getCacheTimestamp($mailbox);
$checkTime = '';
if($timestamp > 0) {
$checkTime = date('r', $timestamp);
}
/* first time through, process ALL emails */
if(empty($checkTime)) {
$criteria = "ALL UNDELETED";
$prefetch = false; // do NOT prefetch emails on a brand new account - timeouts happen.
$GLOBALS['log']->info("INBOUNDEMAIL: new account detected - not prefetching email bodies.");
} else {
$criteria = "SINCE \"{$checkTime}\" UNDELETED"; // not using UNSEEN
}
$GLOBALS['log']->info("INBOUNDEMAIL: using [ {$criteria} ]");
$searchResults = imap_search($this->conn, $criteria, SE_UID);
if(!empty($searchResults)) {
$concatResults = implode(",", $searchResults);
} else {
$GLOBALS['log']->info("INBOUNDEMAIL: no results for mailbox [ {$mailbox} ]");
}
if(empty($searchResults)) {
return 0;
}
return count($searchResults);
}
/**
* update INBOX
*/
function checkEmail($prefetch=true, $synch = false) {
global $sugar_config;
if($this->protocol == 'pop3') {
$this->pop3_checkEmail();
} else {
$this->connectMailserver();
$mailboxes = $this->getMailboxes(true);
sort($mailboxes);
$GLOBALS['log']->info("INBOUNDEMAIL: checking account [ {$this->name} ]");
foreach($mailboxes as $mailbox) {
$this->checkEmailOneMailbox($mailbox, $prefetch, $synch);
}
}
}
/**
* full synchronization
*/
function syncEmail() {
global $sugar_config;
global $current_user;
$showFolders = unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
if(empty($showFolders)) {
$showFolders = array();
}
$email = new Email();
$email->email2init();
// personal accounts
if($current_user->hasPersonalEmail()) {
$personals = $this->retrieveByGroupId($current_user->id);
foreach($personals as $personalAccount) {
if(in_array($personalAccount->id, $showFolders)) {
$personalAccount->email = $email;
if ($personalAccount->isPop3Protocol()) {
$personalAccount->deletePop3Cache();
continue;
}
$personalAccount->cleanOutCache();
$personalAccount->connectMailserver();
$mailboxes = $personalAccount->getMailboxes(true);
$mailboxes[] = 'INBOX';
sort($mailboxes);
$GLOBALS['log']->info("[EMAIL] Start checking account [{$personalAccount->name}] for user [{$current_user->user_name}]");
foreach($mailboxes as $mailbox) {
$GLOBALS['log']->info("[EMAIL] Start checking mailbox [{$mailbox}] of account [{$personalAccount->name}] for user [{$current_user->user_name}]");
$personalAccount->checkEmailOneMailbox($mailbox, false, true);
$GLOBALS['log']->info("[EMAIL] Done checking mailbox [{$mailbox}] of account [{$personalAccount->name}] for user [{$current_user->user_name}]");
}
$GLOBALS['log']->info("[EMAIL] Done checking account [{$personalAccount->name}] for user [{$current_user->user_name}]");
}
}
}
// group accounts
$beans = $this->retrieveAllByGroupId($current_user->id, false);
foreach($beans as $k => $groupAccount) {
if(in_array($groupAccount->id, $showFolders)) {
$groupAccount->email = $email;
$groupAccount->cleanOutCache();
$groupAccount->connectMailserver();
$mailboxes = $groupAccount->getMailboxes(true);
$mailboxes[] = 'INBOX';
sort($mailboxes);
$GLOBALS['log']->info("INBOUNDEMAIL: checking account [ {$groupAccount->name} ]");
foreach($mailboxes as $mailbox) {
$groupAccount->checkEmailOneMailbox($mailbox, false, true);
}
}
}
}
/**
* Deletes cached messages when moving from folder to folder
* @param string $uids
* @param string $fromFolder
* @param string $toFolder
*/
function deleteCachedMessages($uids, $fromFolder) {
global $sugar_config;
if(!isset($this->email) && !isset($this->email->et)) {
$this->email = new Email();
$this->email->email2init();
}
$uids = $this->email->et->_cleanUIDList($uids);
foreach($uids as $uid) {
$file = "{$this->EmailCachePath}/{$this->id}/messages/{$fromFolder}{$uid}.php";
if(file_exists($file)) {
if(!unlink($file)) {
$GLOBALS['log']->debug("INBOUNDEMAIL: Could not delete [ {$file} ]");
}
}
}
}
/**
* similar to imap_fetch_overview, but it gets overviews from a local cache
* file.
* @param string $uids UIDs in comma-delimited format
* @param string $mailbox The mailbox in focus, will default to $this->mailbox
* @param bool $remove Default false
* @return array
*/
function getOverviewsFromCacheFile($uids, $mailbox='', $remove=false) {
global $app_strings;
if(!isset($this->email) && !isset($this->email->et)) {
$this->email = new Email();
$this->email->email2init();
}
$uids = $this->email->et->_cleanUIDList($uids, true);
// load current cache file
$mailbox = empty($mailbox) ? $this->mailbox : $mailbox;
$cacheValue = $this->getCacheValue($mailbox);
$ret = array();
// prep UID array
$exUids = explode($app_strings['LBL_EMAIL_DELIMITER'], $uids);
foreach($exUids as $k => $uid) {
$exUids[$k] = trim($uid);
}
// fill $ret will requested $uids
foreach($cacheValue['retArr'] as $k => $overview) {
if(in_array($overview->imap_uid, $exUids)) {
$ret[] = $overview;
}
}
// remove requested $uids from current cache file (move_mail() type action)
if($remove) {
$this->setCacheValue($mailbox, array(), array(), $ret);
}
return $ret;
}
/**
* merges new info with the saved cached file
* @param array $array Array of email Overviews
* @param string $type 'append' or 'remove'
* @param string $mailbox Target mailbox if not current assigned
*/
function updateOverviewCacheFile($array, $type='append', $mailbox='') {
$mailbox = empty($mailbox) ? $this->mailbox : $mailbox;
$cacheValue = $this->getCacheValue($mailbox);
$uids = $cacheValue['uids'];
$updateRows = array();
$insertRows = array();
$removeRows = array();
// update values
if($type == 'append') { // append
/* we are adding overviews to the cache file */
foreach($array as $overview) {
if(isset($overview->uid)) {
$overview->imap_uid = $overview->uid; // coming from imap_fetch_overview() call
}
if(!in_array($overview->imap_uid, $uids)) {
$insertRows[] = $overview;
}
}
} else {
$updatedCacheOverviews = array();
// compare against generated list
/* we are removing overviews from the cache file */
foreach($cacheValue['retArr'] as $cacheOverview) {
if(!in_array($cacheOverview->imap_uid, $uids)) {
$insertRows[] = $cacheOverview;
} else {
$removeRows[] = $cacheOverview;
}
}
$cacheValue['retArr'] = $updatedCacheOverviews;
}
$this->setCacheValue($mailbox, $insertRows, $updateRows, $removeRows);
}
/**
* Check email prefetches email bodies for quicker display
* @param array array of fetched overviews
*/
function fetchCheckedEmails($fetchedOverviews) {
global $sugar_config;
if(is_array($fetchedOverviews) && !empty($fetchedOverviews)) {
foreach($fetchedOverviews as $overview) {
if($overview->size < 10000) {
$uid = $overview->imap_uid;
if(!empty($uid)) {
$file = "{$this->mailbox}{$uid}.php";
$cacheFile = clean_path("{$this->EmailCachePath}/{$this->id}/messages/{$file}");
if(!file_exists($cacheFile)) {
$GLOBALS['log']->info("INBOUNDEMAIL: Prefetching email [ {$file} ]");
$this->setEmailForDisplay($uid);
$out = $this->displayOneEmail($uid, $this->mailbox);
$this->email->et->writeCacheFile('out', $out, $this->id, 'messages', "{$this->mailbox}{$uid}.php");
} else {
$GLOBALS['log']->debug("INBOUNDEMAIL: Trying to prefetch an email we already fetched! [ {$cacheFile} ]");
}
} else {
$GLOBALS['log']->debug("*** INBOUNDEMAIL: prefetch has a message with no UID");
}
return true;
} else {
$GLOBALS['log']->debug("INBOUNDEMAIL: skipping email prefetch - size too large [ {$overview->size} ]");
}
}
}
return false;
}
/**
* Sets flags on emails. Assumes that connection is live, correct folder is
* set.
* @param string $uids Sequence of UIDs, comma separated
* @param string $type Flag to mark
*/
function markEmails($uids, $type) {
switch($type) {
case 'unread':
$result = imap_clearflag_full($this->conn, $uids, '\\SEEN', ST_UID);
break;
case 'read':
$result = imap_setflag_full($this->conn, $uids, '\\SEEN', ST_UID);
break;
case 'flagged':
$result = imap_setflag_full($this->conn, $uids, '\\FLAGGED', ST_UID);
break;
case 'unflagged':
$result = imap_clearflag_full($this->conn, $uids, '\\FLAGGED', ST_UID);
break;
case 'answered':
$result = imap_setflag_full($this->conn, $uids, '\\Answered', ST_UID);
break;
}
}
//// END EMAIL 2.0 SPECIFIC
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//// SERVER MANIPULATION METHODS
/**
* Deletes the specified folder
* @param string $mbox "::" delimited IMAP mailbox path, ie, INBOX.saved.stuff
* @return bool
*/
function deleteFolder($mbox) {
$returnArray = array();
if ($this->getCacheCount($mbox) > 0) {
$returnArray['status'] = false;
$returnArray['errorMessage'] = "Can not delete {$mbox} as it has emails.";
return $returnArray;
}
$connectString = $this->getConnectString('', $mbox);
//Remove Folder cache
global $sugar_config;
unlink("{$this->EmailCachePath}/{$this->id}/folders/folders.php");
if(imap_unsubscribe($this->conn, imap_utf7_encode($connectString))) {
if(imap_deletemailbox($this->conn, $connectString)) {
$this->mailbox = str_replace(("," . $mbox), "", $this->mailbox);
$this->save();
$sessionFoldersString = $this->getSessionInboundFoldersString($this->server_url, $this->email_user, $this->port, $this->protocol);
$sessionFoldersString = str_replace(("," . $mbox), "", $sessionFoldersString);
$this->setSessionInboundFoldersString($this->server_url, $this->email_user, $this->port, $this->protocol, $sessionFoldersString);
$returnArray['status'] = true;
return $returnArray;
} else {
$GLOBALS['log']->error("*** ERROR: EMAIL2.0 - could not delete IMAP mailbox with path: [ {$connectString} ]");
$returnArray['status'] = false;
$returnArray['errorMessage'] = "NOOP: could not delete folder: {$connectString}";
return $returnArray;
return false;
}
} else {
$GLOBALS['log']->error("*** ERROR: EMAIL2.0 - could not unsubscribe from folder, {$connectString} before deletion.");
$returnArray['status'] = false;
$returnArray['errorMessage'] = "NOOP: could not unsubscribe from folder, {$connectString} before deletion.";
return $returnArray;
}
}
/**
* Saves new folders
* @param string $name Name of new IMAP mailbox
* @param string $mbox "::" delimited IMAP mailbox path, ie, INBOX.saved.stuff
* @return bool True on success
*/
function saveNewFolder($name, $mbox) {
global $sugar_config;
//Remove Folder cache
global $sugar_config;
//unlink("{$this->EmailCachePath}/{$this->id}/folders/folders.php");
//$mboxImap = $this->getImapMboxFromSugarProprietary($mbox);
$delimiter = $this->get_stored_options('folderDelimiter');
if (!$delimiter) {
$delimiter = '.';
}
$newFolder = $mbox . $delimiter . $name;
$mbox .= $delimiter.str_replace($delimiter, "_", $name);
$connectString = $this->getConnectString('', $mbox);
if(imap_createmailbox($this->conn, imap_utf7_encode($connectString))) {
imap_subscribe($this->conn, imap_utf7_encode($connectString));
$status = imap_status($this->conn, str_replace("{$delimiter}{$name}","",$connectString), SA_ALL);
$this->mailbox = $this->mailbox . "," . $newFolder;
$this->save();
$sessionFoldersString = $this->getSessionInboundFoldersString($this->server_url, $this->email_user, $this->port, $this->protocol);
$sessionFoldersString = $sessionFoldersString . "," . $newFolder;
$this->setSessionInboundFoldersString($this->server_url, $this->email_user, $this->port, $this->protocol, $sessionFoldersString);
echo json_encode($status);
return true;
} else {
echo "NOOP: could not create folder";
$GLOBALS['log']->error("*** ERROR: EMAIL2.0 - could not create IMAP mailbox with path: [ {$connectString} ]");
return false;
}
}
/**
* Constructs an IMAP c-client compatible folder path from Sugar proprietary
* @param string $mbox "::" delimited IMAP mailbox path, ie, INBOX.saved.stuff
* @return string
*/
function getImapMboxFromSugarProprietary($mbox) {
$exMbox = explode("::", $mbox);
$mboxImap = '';
for($i=2; $iretrieve($ieId);
$beans[] = $bean;
//$beans = $this->retrieveAllByGroupId($current_user->id, true);
$subject = urldecode($subject);
$criteria = "";
$criteria .= (!empty($subject)) ? 'SUBJECT '.from_html($subject).'' : "";
$criteria .= (!empty($from)) ? ' FROM "'.$from.'"' : "";
$criteria .= (!empty($to)) ? ' FROM "'.$to.'"' : "";
$criteria .= (!empty($body)) ? ' TEXT "'.$body.'"' : "";
$criteria .= (!empty($dateFrom)) ? ' SINCE "'.$timedate->fromString($dateFrom)->format('d-M-Y').'"' : "";
$criteria .= (!empty($dateTo)) ? ' BEFORE "'.$timedate->fromString($dateTo)->format('d-M-Y').'"' : "";
//$criteria .= (!empty($from)) ? ' FROM "'.$from.'"' : "";
$showFolders = unserialize(base64_decode($current_user->getPreference('showFolders', 'Emails')));
$out = array();
foreach($beans as $bean) {
if(!in_array($bean->id, $showFolders)) {
continue;
}
$GLOBALS['log']->info("*** INBOUNDEMAIL: searching [ {$bean->name} ] for [ {$subject}{$from}{$to}{$body}{$dateFrom}{$dateTo} ]");
$group = (!$bean->is_personal) ? 'group.' : '';
$bean->connectMailServer();
$mailboxes = $bean->getMailboxes(true);
if (!in_array('INBOX', $mailboxes)) {
$mailboxes[] = 'INBOX';
}
$totalHits = 0;
foreach($mailboxes as $mbox) {
$bean->mailbox = $mbox;
$searchOverviews = array();
if ($bean->protocol == 'pop3') {
$pop3Criteria = "SELECT * FROM email_cache WHERE ie_id = '{$bean->id}' AND mbox = '{$mbox}'";
$pop3Criteria .= (!empty($subject)) ? ' AND subject like "%'.$bean->db->quote($subject).'%"' : "";
$pop3Criteria .= (!empty($from)) ? ' AND fromaddr like "%'.$from.'%"' : "";
$pop3Criteria .= (!empty($to)) ? ' AND toaddr like "%'.$to.'%"' : "";
$pop3Criteria .= (!empty($dateFrom)) ? ' AND senddate > "'.$dateFrom.'"' : "";
$pop3Criteria .= (!empty($dateTo)) ? ' AND senddate < "'.$dateTo.'"' : "";
$GLOBALS['log']->info("*** INBOUNDEMAIL: searching [ {$mbox} ] using criteria [ {$pop3Criteria} ]");
$r = $bean->db->query($pop3Criteria);
while($a = $bean->db->fetchByAssoc($r)) {
$overview = new Overview();
foreach($a as $k => $v) {
$k=strtolower($k);
switch($k) {
case "imap_uid":
$overview->imap_uid = $v;
$overview->uid = $a['message_id'];
break;
case "toaddr":
$overview->to = from_html($v);
break;
case "fromaddr":
$overview->from = from_html($v);
break;
case "mailsize":
$overview->size = $v;
break;
case "senddate":
$overview->date = $timedate->fromString($v)->format('r');
break;
default:
$overview->$k = from_html($v);
break;
} // sqitch
} // foreach
$searchOverviews[] = $overview;
} // while
} else {
$bean->connectMailServer();
$searchResult = imap_search($bean->conn, $criteria, SE_UID);
if (!empty($searchResult)) {
$searchOverviews = imap_fetch_overview($bean->conn, implode(',', $searchResult), FT_UID);
} // if
} // else
$numHits = count($searchOverviews);
if($numHits > 0) {
$totalHits = $totalHits + $numHits;
$ret = $bean->sortFetchedOverview($searchOverviews, 'date', 'desc', true);
$mbox = "{$bean->id}.SEARCH";
$out = array_merge($out, $bean->displayFetchedSortedListXML($ret, $mbox, false));
}
}
}
$metadata = array();
$metadata['mbox'] = $app_strings['LBL_EMAIL_SEARCH_RESULTS_TITLE'];
$metadata['ieId'] = $this->id;
$metadata['name'] = $this->name;
$metadata['unreadChecked'] = ($current_user->getPreference('showUnreadOnly', 'Emails') == 1) ? 'CHECKED' : '';
$metadata['out'] = $out;
return $metadata;
}
/**
* repairs the encrypted password for a given I-E account
* @return bool True on success
*/
function repairAccount() {
for($i=0; $i<3; $i++) {
if($i != 0) { // decode is performed on retrieve already
$this->email_password = blowfishDecode(blowfishGetKey('InboundEmail'), $this->email_password);
}
if($this->connectMailserver() == 'true') {
$this->save(); // save decoded password (is encoded on save())
return true;
}
}
return false;
}
/**
* soft deletes a User's personal inbox
* @param string id I-E id
* @param string user_name User name of User in focus, NOT current_user
* @return bool True on success
*/
function deletePersonalEmailAccount($id, $user_name) {
$q = "SELECT ie.id FROM inbound_email ie LEFT JOIN users u ON ie.group_id = u.id WHERE u.user_name = '{$user_name}'";
$r = $this->db->query($q, true);
while($a = $this->db->fetchByAssoc($r)) {
if(!empty($a) && $a['id'] == $id) {
$this->retrieve($id);
$this->deleted = 1;
$this->save();
return true;
}
}
return false;
}
function getTeamSetIdForTeams($teamIds) {
if(!is_array($teamIds)){
$teamIds = array($teamIds);
} // if
$teamSet = new TeamSet();
$team_set_id = $teamSet->addTeams($teamIds);
return $team_set_id;
} // fn
/**
* Saves Personal Inbox settings for Users
* @param string userId ID of user to assign all emails for this account
* @param strings userName Name of account, for Sugar purposes
* @param bool forceSave Default true. Flag to save errored settings.
* @return boolean true on success, false on fail
*/
function savePersonalEmailAccount($userId = '', $userName = '', $forceSave=true) {
$groupId = $userId;
$accountExists = false;
if(isset($_REQUEST['ie_id']) && !empty($_REQUEST['ie_id'])) {
$this->retrieve($_REQUEST['ie_id']);
$accountExists = true;
}
$ie_name = $_REQUEST['ie_name'];
$this->is_personal = 1;
$this->name = $ie_name;
$this->group_id = $groupId;
$this->status = $_REQUEST['ie_status'];
$this->server_url = trim($_REQUEST['server_url']);
$this->email_user = trim($_REQUEST['email_user']);
if(!empty($_REQUEST['email_password'])) {
$this->email_password = html_entity_decode($_REQUEST['email_password'], ENT_QUOTES);
}
$this->port = trim($_REQUEST['port']);
$this->protocol = $_REQUEST['protocol'];
if ($this->protocol == "pop3") {
$_REQUEST['mailbox'] = "INBOX";
}
$this->mailbox = $_REQUEST['mailbox'];
$this->mailbox_type = 'pick'; // forcing this
if(isset($_REQUEST['ssl']) && $_REQUEST['ssl'] == 1) { $useSsl = true; }
else $useSsl = false;
$this->service = '::::::::::';
if($forceSave) {
$id = $this->save(); // saving here to prevent user from having to re-enter all the info in case of error
$this->retrieve($id);
}
$this->protocol = $_REQUEST['protocol']; // need to set this again since we safe the "service" string to empty explode values
$opts = $this->getSessionConnectionString($this->server_url, $this->email_user, $this->port, $this->protocol);
$detectedOpts = $this->findOptimumSettings($useSsl);
//If $detectedOpts is empty, there was an error connecting, so clear $opts. If $opts was empty, use $detectedOpts
if (empty($opts) || empty($detectedOpts) || (empty($detectedOpts['good']) && empty($detectedOpts['serial'])))
{
$opts = $detectedOpts;
}
$delimiter = $this->getSessionInboundDelimiterString($this->server_url, $this->email_user, $this->port, $this->protocol);
if(isset($opts['serial']) && !empty($opts['serial'])) {
$this->service = $opts['serial'];
if(isset($_REQUEST['mark_read']) && $_REQUEST['mark_read'] == 1) {
$this->delete_seen = 0;
} else {
$this->delete_seen = 1;
}
// handle stored_options serialization
if(isset($_REQUEST['only_since']) && $_REQUEST['only_since'] == 1) {
$onlySince = true;
} else {
$onlySince = false;
}
$focusUser = new User();
$focusUser->retrieve($groupId);
$mailerId = (isset($_REQUEST['outbound_email'])) ? $_REQUEST['outbound_email'] : "";
$oe = new OutboundEmail();
$oe->getSystemMailerSettings($focusUser, $mailerId);
$stored_options = array();
$stored_options['from_name'] = trim($_REQUEST['from_name']);
$stored_options['from_addr'] = trim($_REQUEST['from_addr']);
$stored_options['reply_to_addr'] = trim($_REQUEST['reply_to_addr']);
if (!$this->isPop3Protocol()) {
$stored_options['trashFolder'] = (isset($_REQUEST['trashFolder']) ? trim($_REQUEST['trashFolder']) : "");
$stored_options['sentFolder'] = (isset($_REQUEST['sentFolder']) ? trim($_REQUEST['sentFolder']) : "");
} // if
$stored_options['only_since'] = $onlySince;
$stored_options['filter_domain'] = '';
$storedOptions['folderDelimiter'] = $delimiter;
$stored_options['outbound_email'] = (isset($_REQUEST['outbound_email'])) ? $_REQUEST['outbound_email'] : $oe->id;
$this->stored_options = base64_encode(serialize($stored_options));
$ieId = $this->save();
//If this is the first personal account the user has setup mark it as default for them.
$currentIECount = $this->getUserPersonalAccountCount($focusUser);
if($currentIECount == 1)
$this->setUsersDefaultOutboundServerId($focusUser, $ieId);
return true;
} else {
// could not find opts, no save
$GLOBALS['log']->debug('-----> InboundEmail could not find optimums for User: '.$ie_name);
return false;
}
}
/**
* Determines if this instance of I-E is for a Group Inbox or Personal Inbox
*/
function handleIsPersonal() {
$qp = 'SELECT users.id, users.user_name FROM users WHERE users.is_group = 0 AND users.deleted = 0 AND users.status = \'active\' AND users.id = \''.$this->group_id.'\'';
$rp = $this->db->query($qp, true);
$personalBox = array();
while($ap = $this->db->fetchByAssoc($rp)) {
$personalBox[] = array($ap['id'], $ap['user_name']);
}
if(count($personalBox) > 0) {
return true;
} else {
return false;
}
}
function getUserNameFromGroupId() {
$r = $this->db->query('SELECT users.user_name FROM users WHERE deleted=0 AND id=\''.$this->group_id.'\'', true);
while($a = $this->db->fetchByAssoc($r)) {
return $a['user_name'];
}
return '';
}
function getFoldersListForMailBox() {
$return = array();
$foldersList = $this->getSessionInboundFoldersString($this->server_url, $this->email_user, $this->port, $this->protocol);
if (empty($foldersList)) {
global $mod_strings;
$msg = $this->connectMailserver(true);
if (strpos($msg, "successfully")) {
$foldersList = $this->getSessionInboundFoldersString($this->server_url, $this->email_user, $this->port, $this->protocol);
$return['status'] = true;
$return['foldersList'] = $foldersList;
$return['statusMessage'] = "";
} else {
$return['status'] = false;
$return['statusMessage'] = $msg;
} // else
} else {
$return['status'] = true;
$return['foldersList'] = $foldersList;
$return['statusMessage'] = "";
}
return $return;
} // fn
/**
* Programatically determines best-case settings for imap_open()
*/
function findOptimumSettings($useSsl=false, $user='', $pass='', $server='', $port='', $prot='', $mailbox='') {
global $mod_strings;
$serviceArr = array();
$returnService = array();
$badService = array();
$goodService = array();
$errorArr = array();
$raw = array();
$retArray = array( 'good' => $goodService,
'bad' => $badService,
'err' => $errorArr);
if(!function_exists('imap_open')) {
$retArray['err'][0] = $mod_strings['ERR_NO_IMAP'];
return $retArray;
}
imap_errors(); // clearing error stack
error_reporting(0); // turn off notices from IMAP
if(isset($_REQUEST['ssl']) && $_REQUEST['ssl'] == 1) {
$useSsl = true;
}
$exServ = explode('::', $this->service);
$service = '/'.$exServ[1];
$nonSsl = array('both-secure' => '/notls/novalidate-cert/secure',
'both' => '/notls/novalidate-cert',
'nocert-secure' => '/novalidate-cert/secure',
'nocert' => '/novalidate-cert',
'notls-secure' => '/notls/secure',
'secure' => '/secure', // for POP3 servers that force CRAM-MD5
'notls' => '/notls',
'none' => '', // try default nothing
);
$ssl = array(
'ssl-both-on-secure' => '/ssl/tls/validate-cert/secure',
'ssl-both-on' => '/ssl/tls/validate-cert',
'ssl-cert-secure' => '/ssl/validate-cert/secure',
'ssl-cert' => '/ssl/validate-cert',
'ssl-tls-secure' => '/ssl/tls/secure',
'ssl-tls' => '/ssl/tls',
'ssl-both-off-secure' => '/ssl/notls/novalidate-cert/secure',
'ssl-both-off' => '/ssl/notls/novalidate-cert',
'ssl-nocert-secure' => '/ssl/novalidate-cert/secure',
'ssl-nocert' => '/ssl/novalidate-cert',
'ssl-notls-secure' => '/ssl/notls/secure',
'ssl-notls' => '/ssl/notls',
'ssl-secure' => '/ssl/secure',
'ssl-none' => '/ssl',
);
if(isset($user) && !empty($user) && isset($pass) && !empty($pass)) {
$this->email_password = $pass;
$this->email_user = $user;
$this->server_url = $server;
$this->port = $port;
$this->protocol = $prot;
$this->mailbox = $mailbox;
}
// in case we flip from IMAP to POP3
if($this->protocol == 'pop3')
$this->mailbox = 'INBOX';
//If user has selected multiple mailboxes, we only need to test the first mailbox for the connection string.
$a_mailbox = explode(",", $this->mailbox);
$tmpMailbox = isset($a_mailbox[0]) ? $a_mailbox[0] : "";
if($useSsl == true)
{
foreach($ssl as $k => $service)
{
$returnService[$k] = 'foo'.$service;
$serviceArr[$k] = '{'.$this->server_url.':'.$this->port.'/service='.$this->protocol.$service.'}'.$tmpMailbox;
}
}
else
{
foreach($nonSsl as $k => $service)
{
$returnService[$k] = 'foo'.$service;
$serviceArr[$k] = '{'.$this->server_url.':'.$this->port.'/service='.$this->protocol.$service.'}'.$tmpMailbox;
}
}
$GLOBALS['log']->debug('---------------STARTING FINDOPTIMUMS LOOP----------------');
$l = 1;
//php imap library will capture c-client library warnings as errors causing good connections to be ignored.
//Check against known warnings to ensure good connections are used.
$acceptableWarnings = array("SECURITY PROBLEM: insecure server advertised AUTH=PLAIN", //c-client auth_pla.c
"Mailbox is empty");
$login = $this->email_user;
$passw = $this->email_password;
$foundGoodConnection = false;
foreach($serviceArr as $k => $serviceTest) {
$errors = '';
$alerts = '';
$GLOBALS['log']->debug($l.': I-E testing string: '.$serviceTest);
// open the connection and try the test string
$this->conn = imap_open($serviceTest, $login, $passw);
if(($errors = imap_last_error()) || ($alerts = imap_alerts())) {
if($errors == 'Too many login failures' || $errors == '[CLOSED] IMAP connection broken (server response)') { // login failure means don't bother trying the rest
$GLOBALS['log']->debug($l.': I-E failed using ['.$serviceTest.']');
$retArray['err'][$k] = $mod_strings['ERR_BAD_LOGIN_PASSWORD'];
$retArray['bad'][$k] = $serviceTest;
$GLOBALS['log']->debug($l.': I-E ERROR: $ie->findOptimums() failed due to bad user credentials for user login: '.$this->email_user);
return $retArray;
} elseif( in_array($errors, $acceptableWarnings, TRUE)) { // false positive
$GLOBALS['log']->debug($l.': I-E found good connection but with warnings ['.$serviceTest.'] Errors:' . $errors);
$retArray['good'][$k] = $returnService[$k];
$foundGoodConnection = true;
}
else {
$GLOBALS['log']->debug($l.': I-E failed using ['.$serviceTest.'] - error: '.$errors);
$retArray['err'][$k] = $errors;
$retArray['bad'][$k] = $serviceTest;
}
} else {
$GLOBALS['log']->debug($l.': I-E found good connect using ['.$serviceTest.']');
$retArray['good'][$k] = $returnService[$k];
$foundGoodConnection = true;
}
if(is_resource($this->conn)) {
if (!$this->isPop3Protocol()) {
$serviceTest = str_replace("INBOX", "", $serviceTest);
$boxes = imap_getmailboxes($this->conn, $serviceTest, "*");
$delimiter = '.';
// clean MBOX path names
foreach($boxes as $k => $mbox) {
$raw[] = $mbox->name;
if ($mbox->delimiter) {
$delimiter = $mbox->delimiter;
} // if
} // foreach
$this->setSessionInboundDelimiterString($this->server_url, $this->email_user, $this->port, $this->protocol, $delimiter);
} // if
if(!imap_close($this->conn)) $GLOBALS['log']->debug('imap_close() failed!');
}
$GLOBALS['log']->debug($l.': I-E clearing error and alert stacks.');
imap_errors(); // clear stacks
imap_alerts();
// If you find a good connection, then don't do any further testing to find URL
if ($foundGoodConnection) {
break;
} // if
$l++;
}
$GLOBALS['log']->debug('---------------end FINDOPTIMUMS LOOP----------------');
if(!empty($retArray['good'])) {
$newTls = '';
$newCert = '';
$newSsl = '';
$newNotls = '';
$newNovalidate_cert = '';
$good = array_pop($retArray['good']); // get most complete string
$exGood = explode('/', $good);
foreach($exGood as $v) {
switch($v) {
case 'ssl':
$newSsl = 'ssl';
break;
case 'tls':
$newTls = 'tls';
break;
case 'notls':
$newNotls = 'notls';
break;
case 'cert':
$newCert = 'validate-cert';
break;
case 'novalidate-cert':
$newNovalidate_cert = 'novalidate-cert';
break;
case 'secure':
$secure = 'secure';
break;
}
}
$goodStr['serial'] = $newTls.'::'.$newCert.'::'.$newSsl.'::'.$this->protocol.'::'.$newNovalidate_cert.'::'.$newNotls.'::'.$secure;
$goodStr['service'] = $good;
$testConnectString = str_replace('foo','', $good);
$testConnectString = '{'.$this->server_url.':'.$this->port.'/service='.$this->protocol.$testConnectString.'}';
$this->setSessionConnectionString($this->server_url, $this->email_user, $this->port, $this->protocol, $goodStr);
$i = 0;
foreach($raw as $mbox)
{
$raw[$i] = str_replace($testConnectString, "", $GLOBALS['locale']->translateCharset($mbox, "UTF7-IMAP", "UTF8" ));
$i++;
} // foreach
sort($raw);
$this->setSessionInboundFoldersString($this->server_url, $this->email_user, $this->port, $this->protocol, implode(",", $raw));
return $goodStr;
} else {
return false;
}
}
function getSessionConnectionString($server_url, $email_user, $port, $protocol) {
$sessionConnectionString = $server_url . $email_user . $port . $protocol;
return (isset($_SESSION[$sessionConnectionString]) ? $_SESSION[$sessionConnectionString] : "");
}
function setSessionConnectionString($server_url, $email_user, $port, $protocol, $goodStr) {
$sessionConnectionString = $server_url . $email_user . $port . $protocol;
$_SESSION[$sessionConnectionString] = $goodStr;
}
function getSessionInboundDelimiterString($server_url, $email_user, $port, $protocol) {
$sessionInboundDelimiterString = $server_url . $email_user . $port . $protocol . "delimiter";
return (isset($_SESSION[$sessionInboundDelimiterString]) ? $_SESSION[$sessionInboundDelimiterString] : "");
}
function setSessionInboundDelimiterString($server_url, $email_user, $port, $protocol, $delimiter) {
$sessionInboundDelimiterString = $server_url . $email_user . $port . $protocol . "delimiter";
$_SESSION[$sessionInboundDelimiterString] = $delimiter;
}
function getSessionInboundFoldersString($server_url, $email_user, $port, $protocol) {
$sessionInboundFoldersListString = $server_url . $email_user . $port . $protocol . "foldersList";
return (isset($_SESSION[$sessionInboundFoldersListString]) ? $_SESSION[$sessionInboundFoldersListString] : "");
}
function setSessionInboundFoldersString($server_url, $email_user, $port, $protocol, $foldersList) {
$sessionInboundFoldersListString = $server_url . $email_user . $port . $protocol . "foldersList";
$_SESSION[$sessionInboundFoldersListString] = $foldersList;
}
/**
* Checks for duplicate Group User names when creating a new one at save()
* @return GUID returns GUID of Group User if user_name match is
* found
* @return boolean false if NO DUPE IS FOUND
*/
function groupUserDupeCheck() {
$q = "SELECT u.id FROM users u WHERE u.deleted=0 AND u.is_group=1 AND u.user_name = '".$this->name."'";
$r = $this->db->query($q, true);
$uid = '';
while($a = $this->db->fetchByAssoc($r)) {
$uid = $a['id'];
}
if(strlen($uid) > 0) {
return $uid;
} else {
return false;
}
}
/**
* Returns