', '|', '$',); 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 $safe; // place holder for HTML_Safe class 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->safe = new HTML_Safe(); $this->safe->whiteProtocols[] = "cid"; $this->safe->clear(); $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) { $ret = parent::retrieve($id); $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); $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 = utf8_encode($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 .= utf8_encode(imap_body($this->conn, $uid, FT_UID)); } // else $raw = to_html($raw); $raw = nl2br($raw); //} return $raw; } /** * 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, ":"))); $value = trim(substr($line, strpos($line, ":") + 1)); $value = to_html($value); $header .= ""; $header .= ""; $header .= ""; $header .= ""; } } $header .= "
{$key} {$value} 
"; //} 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)) { $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' ) { $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'] = from_html($overview->$colDef['name']); $overview->$colDef['name'] = $this->cleanContent($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": break; default: if(!empty($set)) { $set .= ","; } $set .= "{$colDef['name']} = ".$this->db->quoted($overview->$colDef['name']); 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; $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}]"); $this->fetchCheckedEmails($fetchedOverview); $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} ]"); } /** * 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); } } } /** * 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); $mailboxes[] = 'INBOX'; 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"); } } else { $GLOBALS['log']->debug("INBOUNDEMAIL: skipping email prefetch - size too large [ {$overview->size} ]"); } } } } /** * 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