/** * Function to be executed in script shutdown * Registered with register_shutdown_function() */ public function shutdown() { if (is_object($this->imap)) { $this->imap->close(); } if (is_object($this->smtp)) { $this->smtp->disconnect(); } foreach ($this->books as $book) { if (is_object($book)) { $book->close(); } } // before closing the database connection, write session data if ($_SERVER['REMOTE_ADDR']) { session_write_close(); } // write performance stats to logs/console if ($this->config->get('devel_mode')) { if (function_exists('memory_get_usage')) { $mem = show_bytes(memory_get_usage()); } if (function_exists('memory_get_peak_usage')) { $mem .= '/' . show_bytes(memory_get_peak_usage()); } $log = $this->task . ($this->action ? '/' . $this->action : '') . ($mem ? " [{$mem}]" : ''); if (defined('RCMAIL_START')) { rcube_print_time(RCMAIL_START, $log); } else { console($log); } } }
/** * Parse message body for UUencoded attachments bodies * * @param rcube_message_part $part Message part to decode * @return array */ function uu_decode(&$part) { // @TODO: messages may be huge, hadle body via file if (!isset($part->body)) { $part->body = $this->imap->get_message_part($this->uid, $part->mime_id, $part); } $parts = array(); // FIXME: line length is max.65? $uu_regexp = '/begin [0-7]{3,4} ([^\\n]+)\\n(([\\x21-\\x7E]{0,65}\\n)+)`\\nend/s'; if (preg_match_all($uu_regexp, $part->body, $matches, PREG_SET_ORDER)) { // remove attachments bodies from the message body $part->body = preg_replace($uu_regexp, '', $part->body); // update message content-type $part->ctype_primary = 'multipart'; $part->ctype_secondary = 'mixed'; $part->mimetype = $part->ctype_primary . '/' . $part->ctype_secondary; // add attachments to the structure foreach ($matches as $pid => $att) { $uupart = new rcube_message_part(); $uupart->filename = trim($att[1]); $uupart->encoding = 'stream'; $uupart->body = convert_uudecode($att[2]); $uupart->size = strlen($uupart->body); $uupart->mime_id = 'uu.' . $part->mime_id . '.' . $pid; $ctype = rc_mime_content_type($uupart->body, $uupart->filename, 'application/octet-stream', true); $uupart->mimetype = $ctype; list($uupart->ctype_primary, $uupart->ctype_secondary) = explode('/', $ctype); $parts[] = $uupart; unset($matches[$pid]); } } return $parts; }
/** * Fetches thread data from IMAP server */ private function get_thread_data($mailbox, $mbox_data = array()) { if (empty($mbox_data)) { $mbox_data = $this->imap->folder_data($mailbox); } if ($mbox_data['EXISTS']) { // get all threads (default sort order) return $this->imap->threads_direct($mailbox); } return new rcube_result_thread($mailbox, '* THREAD'); }
/** * Fetches index data from IMAP server */ private function get_index_data($mailbox, $sort_field, $sort_order, $mbox_data = array()) { if (empty($mbox_data)) { $mbox_data = $this->imap->folder_data($mailbox); } if ($mbox_data['EXISTS']) { // fetch sorted sequence numbers $index = $this->imap->index_direct($mailbox, $sort_field, $sort_order); } else { $index = new rcube_result_index($mailbox, '* SORT'); } return $index; }
/** * __construct * * Provide a uid, and parse message structure. * * @param string $uid The message UID. * * @uses rcmail::get_instance() * @uses rcube_imap::decode_mime_string() * @uses self::set_safe() * * @see self::$app, self::$imap, self::$opt, self::$structure */ function __construct($uid) { $this->app = rcmail::get_instance(); $this->imap = $this->app->imap; $this->uid = $uid; $this->headers = $this->imap->get_headers($uid, NULL, true, true); $this->subject = rcube_imap::decode_mime_string($this->headers->subject, $this->headers->charset); list(, $this->sender) = each($this->imap->decode_address_list($this->headers->from)); $this->set_safe(intval($_GET['_safe']) || $_SESSION['safe_messages'][$uid]); $this->opt = array('safe' => $this->is_safe, 'prefer_html' => $this->app->config->get('prefer_html'), 'get_url' => rcmail_url('get', array('_mbox' => $this->imap->get_mailbox_name(), '_uid' => $uid))); if ($this->structure = $this->imap->get_structure($uid, $this->headers->body_structure)) { $this->get_mime_numbers($this->structure); $this->parse_structure($this->structure); } else { $this->body = $this->imap->get_body($uid); } }
/** * Collect the email address of a just-sent email recipients into * the automatic addressbook (if it's not already in another * addressbook). */ public function register_recipients($p) { $rcmail = rcmail::get_instance(); if (!$rcmail->config->get('use_auto_abook', true)) { return; } $headers = $p['headers']; if (!class_exists('rcube_mime')) { // RC < 0.8 compatibility code $IMAP = new rcube_imap(null); $all_recipients = array_merge($IMAP->decode_address_list($headers['To'], null, true, $headers['charset']), $IMAP->decode_address_list($headers['Cc'], null, true, $headers['charset']), $IMAP->decode_address_list($headers['Bcc'], null, true, $headers['charset'])); } else { $all_recipients = array_merge(rcube_mime::decode_address_list($headers['To'], null, true, $headers['charset']), rcube_mime::decode_address_list($headers['Cc'], null, true, $headers['charset']), rcube_mime::decode_address_list($headers['Bcc'], null, true, $headers['charset'])); } require_once dirname(__FILE__) . '/automatic_addressbook_backend.php'; $CONTACTS = new automatic_addressbook_backend($rcmail->db, $rcmail->user->ID); foreach ($all_recipients as $recipient) { // Bcc and Cc can be empty if ($recipient['mailto'] != '') { $contact = array('email' => $recipient['mailto'], 'name' => $recipient['name']); // use email address part for name if (empty($contact['name']) || $contact['name'] == $contact['email']) { $contact['name'] = ucfirst(preg_replace('/[\\.\\-]/', ' ', substr($contact['email'], 0, strpos($contact['email'], '@')))); } /* We only want to add the contact to the collected contacts * address book if it is not already in an addressbook, so we * first lookup in every address source. */ $book_types = (array) $rcmail->config->get('autocomplete_addressbooks', 'sql'); foreach ($book_types as $id) { $abook = $rcmail->get_address_book($id); $previous_entries = $abook->search('email', $contact['email'], false, false); if ($previous_entries->count) { break; } } if (!$previous_entries->count) { $plugin = $rcmail->plugins->exec_hook('contact_create', array('record' => $contact, 'source' => $this->abook_id)); if (!$plugin['abort']) { $CONTACTS->insert($contact, false); } } } } }
/** * Fetches index data from IMAP server */ private function get_index_data($mailbox, $sort_field, $sort_order, $mbox_data = array()) { $data = array(); if (empty($mbox_data)) { $mbox_data = $this->imap->mailbox_data($mailbox); } // Prevent infinite loop. // It happens when rcube_imap::message_index_direct() is called. // There id2uid() is called which will again call get_index() and so on. if (!$sort_field && !$this->skip_deleted) { $this->icache['pending_index_update'] = true; } if ($mbox_data['EXISTS']) { // fetch sorted sequence numbers $data_seq = $this->imap->message_index_direct($mailbox, $sort_field, $sort_order); // fetch UIDs if (!empty($data_seq)) { // Seek in internal cache if (array_key_exists('index', (array) $this->icache[$mailbox]) && array_key_exists('result', (array) $this->icache[$mailbox]['index'])) { $data_uid = $this->icache[$mailbox]['index']['result']; } else { $data_uid = $this->imap->conn->fetchUIDs($mailbox, $data_seq); } // build index if (!empty($data_uid)) { foreach ($data_seq as $seq) { if ($uid = $data_uid[$seq]) { $data[$seq] = $uid; } } } } } // Reset internal flags $this->icache['pending_index_update'] = false; return $data; }
/** * Decode a mime-encoded string to internal charset * * @param string $input Header value * @param string $fallback Fallback charset if none specified * * @return string Decoded string * @static */ public static function decode_mime_string($input, $fallback = null) { // Initialize variable $out = ''; // Iterate instead of recursing, this way if there are too many values we don't have stack overflows // rfc: all line breaks or other characters not found // in the Base64 Alphabet must be ignored by decoding software // delete all blanks between MIME-lines, differently we can // receive unnecessary blanks and broken utf-8 symbols $input = preg_replace("/\\?=\\s+=\\?/", '?==?', $input); // Check if there is stuff to decode if (strpos($input, '=?') !== false) { // Loop through the string to decode all occurences of =? ?= into the variable $out while (($pos = strpos($input, '=?')) !== false) { // Append everything that is before the text to be decoded $out .= substr($input, 0, $pos); // Get the location of the text to decode $end_cs_pos = strpos($input, "?", $pos + 2); $end_en_pos = strpos($input, "?", $end_cs_pos + 1); $end_pos = strpos($input, "?=", $end_en_pos + 1); // Extract the encoded string $encstr = substr($input, $pos + 2, $end_pos - $pos - 2); // Extract the remaining string $input = substr($input, $end_pos + 2); // Decode the string fragement $out .= rcube_imap::_decode_mime_string_part($encstr); } // Deocde the rest (if any) if (strlen($input) != 0) { $out .= rcube_imap::decode_mime_string($input, $fallback); } // return the results return $out; } // no encoding information, use fallback return rcube_charset_convert($input, !empty($fallback) ? $fallback : rcmail::get_instance()->config->get('default_charset', 'ISO-8859-1')); }
/** * Checks wether recipients exist in any of the addressbooks. * * @param array $args Default hook parameters. * * @return void */ public function check_recipients($args) { $rcmail = rcmail::get_instance(); // don't process the sent message, if it's a 'Read Receipt' response if (isset($args['headers']['Content-Type']) && strpos($args['headers']['Content-Type'], 'report-type=disposition-notification') !== false) { return $args; } $rcube_imap = new rcube_imap(null); // build recipients array $recipients = $rcube_imap->decode_address_list($args['headers']['To']); if (isset($args['headers']['Cc'])) { $recipients = array_merge($recipients, $rcube_imap->decode_address_list($args['headers']['Cc'])); } if (isset($args['headers']['Bcc'])) { $recipients = array_merge($recipients, $rcube_imap->decode_address_list($args['headers']['Bcc'])); } // stores contacts that don't exist in current address books $new_contacts = array(); // iterate over recipients and search for them in address books foreach ($recipients as $recipient) { // flag to denote if the current recipient doesn't exist in any of the address books $is_new_contact = true; // if we dont want to list users in same domain as us for some reason // example: globaladdressbook plugin with all domain users inside if (!$this->rcmail->config->get('recipient_to_contact_addressbooks')) { // get current recipient domain $recipient_domain = preg_replace('/^[^@]*@(.*)$/', '$1', $recipient['mailto']); // get identity used to send email $identity = $rcmail->user->get_identity(); // get current identity domain $identity_domain = preg_replace('/^[^@]*@(.*)$/', '$1', $identity['email']); // check if recipient domain match with this identify domain // if match, continue loop and ignore recipient if ($recipient_domain == $identity_domain) { unset($recipient_domain); unset($identity); unset($identity_domain); $is_new_contact = false; continue; } } // interate over over address books and search for a contact with the same email address foreach ($this->addressbooks as $abook_id => $address_source) { $address_book = $this->rcmail->get_address_book($abook_id); $search_result = $address_book->search('email', $recipient['mailto']); // the contact already exist. skip the rest address books and move to next recipient if ($search_result->count > 0) { $is_new_contact = false; break; } } // store the non-existing recipient if ($is_new_contact) { $new_contacts[] = $recipient; } } if (!empty($new_contacts)) { $_SESSION['recipient_to_contact'] = $new_contacts; } }
$imap_port = $RCI->getprop('default_port'); $a_host = parse_url($imap_host); if ($a_host['host']) { $imap_host = $a_host['host']; $imap_ssl = isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl', 'imaps', 'tls')) ? $a_host['scheme'] : null; if (isset($a_host['port'])) { $imap_port = $a_host['port']; } else { if ($imap_ssl && $imap_ssl != 'tls' && (!$imap_port || $imap_port == 143)) { $imap_port = 993; } } } $imap_host = idn_to_ascii($imap_host); $imap_user = idn_to_ascii($_POST['_user']); $imap = new rcube_imap(null); $imap->set_options(array('auth_type' => $RCI->getprop('imap_auth_type'), 'debug' => $RCI->getprop('imap_debug'), 'socket_options' => $RCI->getprop('imap_conn_options'))); if ($imap->connect($imap_host, $imap_user, $_POST['_pass'], $imap_port, $imap_ssl)) { $RCI->pass('IMAP connect', 'SORT capability: ' . ($imap->get_capability('SORT') ? 'yes' : 'no')); $imap->close(); } else { $RCI->fail('IMAP connect', $RCI->get_error()); } } ?> <p><input type="submit" name="imaptest" value="Check login" /></p> </form> <hr />
/** * Copy of rcube_imap::search_index() */ protected function search_index() { $criteria = $this->search; $charset = $this->charset; $imap = $this->worker->get_imap(); if (!$imap->connected()) { trigger_error("No IMAP connection for {$this->folder}", E_USER_WARNING); if ($this->threading) { return new rcube_result_thread($this->folder); } else { return new rcube_result_index($this->folder); } } if ($this->worker->options['skip_deleted'] && !preg_match('/UNDELETED/', $criteria)) { $criteria = 'UNDELETED ' . $criteria; } // unset CHARSET if criteria string is ASCII, this way // SEARCH won't be re-sent after "unsupported charset" response if ($charset && $charset != 'US-ASCII' && is_ascii($criteria)) { $charset = 'US-ASCII'; } if ($this->threading) { $threads = $imap->thread($this->folder, $this->threading, $criteria, true, $charset); // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8, // but I've seen that Courier doesn't support UTF-8) if ($threads->is_error() && $charset && $charset != 'US-ASCII') { $threads = $imap->thread($this->folder, $this->threading, rcube_imap::convert_criteria($criteria, $charset), true, 'US-ASCII'); } return $threads; } if ($this->sort_field) { $messages = $imap->sort($this->folder, $this->sort_field, $criteria, true, $charset); // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8, // but I've seen Courier with disabled UTF-8 support) if ($messages->is_error() && $charset && $charset != 'US-ASCII') { $messages = $imap->sort($this->folder, $this->sort_field, rcube_imap::convert_criteria($criteria, $charset), true, 'US-ASCII'); } } if (!$messages || $messages->is_error()) { $messages = $imap->search($this->folder, ($charset && $charset != 'US-ASCII' ? "CHARSET {$charset} " : '') . $criteria, true); // Error, try with US-ASCII (some servers may support only US-ASCII) if ($messages->is_error() && $charset && $charset != 'US-ASCII') { $messages = $imap->search($this->folder, rcube_imap::convert_criteria($criteria, $charset), true); } } return $messages; }
/** * Checks wether recipients exist in any of the addressbooks. * * @param array $args Default hook parameters. * * @return void */ public function check_recipients($args) { // don't process the sent message, if it's a 'Read Receipt' response if (isset($args['headers']['Content-Type']) && strpos($args['headers']['Content-Type'], 'report-type=disposition-notification') !== false) { return $args; } $rcube_imap = new rcube_imap(null); // build recipients array $recipients = $rcube_imap->decode_address_list($args['headers']['To']); if (isset($args['headers']['Cc'])) { $recipients = array_merge($recipients, $rcube_imap->decode_address_list($args['headers']['Cc'])); } if (isset($args['headers']['Bcc'])) { $recipients = array_merge($recipients, $rcube_imap->decode_address_list($args['headers']['Bcc'])); } // stores contacts that don't exist in current address books $new_contacts = array(); // iterate over recipients and search for them in address books foreach ($recipients as $recipient) { // flag to denote if the current recipient doesn't exist in any of the address books $is_new_contact = true; // interate over over address books and search for a contact with the same email address foreach ($this->addressbooks as $abook_id => $address_source) { $address_book = $this->rcmail->get_address_book($abook_id); $search_result = $address_book->search('email', $recipient['mailto']); // the contact already exist. skip the rest address books and move to next recipient if ($search_result->count > 0) { $is_new_contact = false; break; } } // store the non-existing recipient if ($is_new_contact) { $new_contacts[] = $recipient; } } if (!empty($new_contacts)) { $_SESSION['recipient_to_contact'] = $new_contacts; } }
$imap_port = $RCI->getprop('default_port'); $a_host = parse_url($imap_host); if ($a_host['host']) { $imap_host = $a_host['host']; $imap_ssl = isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl', 'imaps', 'tls')) ? $a_host['scheme'] : null; if (isset($a_host['port'])) { $imap_port = $a_host['port']; } else { if ($imap_ssl && $imap_ssl != 'tls' && (!$imap_port || $imap_port == 143)) { $imap_port = 993; } } } $imap_host = idn_to_ascii($imap_host); $imap_user = idn_to_ascii($_POST['_user']); $imap = new rcube_imap(null); if ($imap->connect($imap_host, $imap_user, $_POST['_pass'], $imap_port, $imap_ssl)) { $RCI->pass('IMAP connect', 'SORT capability: ' . ($imap->get_capability('SORT') ? 'yes' : 'no')); $imap->close(); } else { $RCI->fail('IMAP connect', $RCI->get_error()); } } ?> <p><input type="submit" name="imaptest" value="Check login" /></p> </form> <hr />
/** * Fixes some user preferences according to namespace handling change. * Old Roundcube versions were using folder names with removed namespace prefix. * Now we need to add the prefix on servers where personal namespace has prefix. * * @param rcube_user $user User object */ private function fix_namespace_settings($user) { $prefix = $this->imap->get_namespace('prefix'); $prefix_len = strlen($prefix); if (!$prefix_len) { return; } $prefs = $this->config->all(); if (!empty($prefs['namespace_fixed'])) { return; } // Build namespace prefix regexp $ns = $this->imap->get_namespace(); $regexp = array(); foreach ($ns as $entry) { if (!empty($entry)) { foreach ($entry as $item) { if (strlen($item[0])) { $regexp[] = preg_quote($item[0], '/'); } } } } $regexp = '/^(' . implode('|', $regexp) . ')/'; // Fix preferences $opts = array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox', 'archive_mbox'); foreach ($opts as $opt) { if ($value = $prefs[$opt]) { if ($value != 'INBOX' && !preg_match($regexp, $value)) { $prefs[$opt] = $prefix . $value; } } } if (!empty($prefs['default_imap_folders'])) { foreach ($prefs['default_imap_folders'] as $idx => $name) { if ($name != 'INBOX' && !preg_match($regexp, $name)) { $prefs['default_imap_folders'][$idx] = $prefix . $name; } } } if (!empty($prefs['search_mods'])) { $folders = array(); foreach ($prefs['search_mods'] as $idx => $value) { if ($idx != 'INBOX' && $idx != '*' && !preg_match($regexp, $idx)) { $idx = $prefix . $idx; } $folders[$idx] = $value; } $prefs['search_mods'] = $folders; } if (!empty($prefs['message_threading'])) { $folders = array(); foreach ($prefs['message_threading'] as $idx => $value) { if ($idx != 'INBOX' && !preg_match($regexp, $idx)) { $idx = $prefix . $idx; } $folders[$prefix . $idx] = $value; } $prefs['message_threading'] = $folders; } if (!empty($prefs['collapsed_folders'])) { $folders = explode('&&', $prefs['collapsed_folders']); $count = count($folders); $folders_str = ''; if ($count) { $folders[0] = substr($folders[0], 1); $folders[$count - 1] = substr($folders[$count - 1], 0, -1); } foreach ($folders as $value) { if ($value != 'INBOX' && !preg_match($regexp, $value)) { $value = $prefix . $value; } $folders_str .= '&' . $value . '&'; } $prefs['collapsed_folders'] = $folders_str; } $prefs['namespace_fixed'] = true; // save updated preferences and reset imap settings (default folders) $user->save_prefs($prefs); $this->set_imap_prop(); }
/** * Decode a message header value * * @param string $input Header value * @param boolean $remove_quotas Remove quotes if necessary * @return string Decoded string */ function decode_header($input, $remove_quotes = false) { $str = rcube_imap::decode_mime_string((string) $input, $this->default_charset); if ($str[0] == '"' && $remove_quotes) { $str = str_replace('"', '', $str); } return $str; }