/** * Get content of a specific part of this message * * @param string $mime_id Part ID * @param boolean $formatted Enables formatting of text/* parts bodies * @param int $max_bytes Only return/read this number of bytes * @param mixed $mode NULL to return a string, -1 to print body * or file pointer to save the body into * * @return string|bool Part content or operation status */ public function get_part_body($mime_id, $formatted = false, $max_bytes = 0, $mode = null) { if (!($part = $this->mime_parts[$mime_id])) { return; } // allow plugins to modify part body $plugin = $this->app->plugins->exec_hook('message_part_body', array('object' => $this, 'part' => $part)); // only text parts can be formatted $formatted = $formatted && $part->ctype_primary == 'text'; // part body not fetched yet... save in memory if it's small enough if ($part->body === null && is_numeric($mime_id) && $part->size < self::BODY_MAX_SIZE) { $this->storage->set_folder($this->folder); // Warning: body here should be always unformatted $part->body = $this->storage->get_message_part($this->uid, $mime_id, $part, null, null, true, 0, false); } // body stored in message structure (winmail/inline-uuencode) if ($part->body !== null || $part->encoding == 'stream') { $body = $part->body; if ($formatted && $body) { $body = self::format_part_body($body, $part, $this->headers->charset); } if ($max_bytes && strlen($body) > $max_bytes) { $body = substr($body, 0, $max_bytes); } if (is_resource($mode)) { if ($body !== false) { fwrite($mode, $body); rewind($mode); } return $body !== false; } if ($mode === -1) { if ($body !== false) { print $body; } return $body !== false; } return $body; } // get the body from IMAP $this->storage->set_folder($this->folder); $body = $this->storage->get_message_part($this->uid, $mime_id, $part, $mode === -1, is_resource($mode) ? $mode : null, !($mode && $formatted), $max_bytes, $mode && $formatted); if (is_resource($mode)) { rewind($mode); return $body !== false; } if (!$mode && $body && $formatted) { $body = self::format_part_body($body, $part, $this->headers->charset); } return $body; }
/** * 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)) { $this->storage->set_folder($this->folder); $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part); } $parts = array(); // uuencode regexp $uu_regexp = '/^(begin [0-7]{3,4} ([^\\n]+)\\n)(([\\x21-\\x60]{0,65}\\n){0,2})([\\x21-\\x60]{0,65}|`\\nend)\\s*\\n/sm'; if (preg_match_all($uu_regexp, $part->body, $matches, PREG_SET_ORDER)) { $uu_endstring = "`\nend\n"; // add attachments to the structure foreach ($matches as $pid => $att) { // make sure we're looking at a uuencoded file, and not a false positive $uu_lines = explode("\n", $att[3]); foreach ($uu_lines as $uu_line) { if (strlen($uu_line) == 0) { continue; } $line_len = ord(substr($uu_line, 0, 1)) - 32 & 0x3f; $max_code_len = floor(($line_len + 2) / 3) * 4; $min_code_len = ceil($line_len / 3 * 4); if (strlen($uu_line) - 1 < $min_code_len or strlen($uu_line) - 1 > $max_code_len) { // illegal uuencode, break out of 'foreach $matches' loop break 2; } } $startpos = strpos($part->body, $att[0]) + strlen($att[1]); $endpos = strpos($part->body, $uu_endstring); $filebody = substr($part->body, $startpos, $endpos - $startpos); // remove attachments bodies from the message body $uu_startpos = $startpos - strlen($att[1]); $part->body = substr_replace($part->body, "", $uu_startpos, $endpos + strlen($uu_endstring) - $uu_startpos); $uupart = new rcube_message_part(); $uupart->filename = trim($att[2]); $uupart->encoding = 'stream'; $uupart->body = convert_uudecode($filebody); $uupart->size = strlen($uupart->body); $uupart->mime_id = 'uu.' . $part->mime_id . '.' . $pid; $ctype = rcube_mime::file_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]); } // mark body as modified so it will not be cached by rcube_imap_cache $part->body_modified = true; } return $parts; }
/** * Function to be executed in script shutdown * Registered with register_shutdown_function() */ public function shutdown() { foreach ($this->shutdown_functions as $function) { call_user_func($function); } if (is_object($this->smtp)) { $this->smtp->disconnect(); } foreach ($this->caches as $cache) { if (is_object($cache)) { $cache->close(); } } if (is_object($this->storage)) { if ($this->expunge_cache) { $this->storage->expunge_cache(); } $this->storage->close(); } }
/** * 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)) { $this->storage->set_folder($this->folder); $part->body = $this->storage->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/s'; if (preg_match_all($uu_regexp, $part->body, $matches, PREG_SET_ORDER)) { // update message content-type $part->ctype_primary = 'multipart'; $part->ctype_secondary = 'mixed'; $part->mimetype = $part->ctype_primary . '/' . $part->ctype_secondary; $uu_endstring = "`\nend\n"; // add attachments to the structure foreach ($matches as $pid => $att) { $startpos = strpos($part->body, $att[1]) + strlen($att[1]) + 1; // "\n" $endpos = strpos($part->body, $uu_endstring); $filebody = substr($part->body, $startpos, $endpos - $startpos); // remove attachments bodies from the message body $part->body = substr_replace($part->body, "", $startpos, $endpos + strlen($uu_endstring) - $startpos); $uupart = new rcube_message_part(); $uupart->filename = trim($att[1]); $uupart->encoding = 'stream'; $uupart->body = convert_uudecode($filebody); $uupart->size = strlen($uupart->body); $uupart->mime_id = 'uu.' . $part->mime_id . '.' . $pid; $ctype = rcube_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]); } // remove attachments bodies from the message body $part->body = preg_replace($uu_regexp, '', $part->body); } return $parts; }
/** * When you're going to sleep the script execution for a longer time * it is good to close all external connections (sql, memcache, SMTP, IMAP). * * No action is required on wake up, all connections will be * re-established automatically. */ public function sleep() { foreach ($this->caches as $cache) { if (is_object($cache)) { $cache->close(); } } if ($this->storage) { $this->storage->close(); } if ($this->db) { $this->db->closeConnection(); } if ($this->memcache) { $this->memcache->close(); // after close() need to re-init memcache $this->memcache_init(); } if ($this->smtp) { $this->smtp->disconnect(); } }
/** * Function to be executed in script shutdown * Registered with register_shutdown_function() */ public function shutdown() { foreach ($this->shutdown_functions as $function) { call_user_func($function); } // write session data as soon as possible and before // closing database connection, don't do this before // registered shutdown functions, they may need the session // Note: this will run registered gc handlers (ie. cache gc) if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) { $this->session->write_close(); } if (is_object($this->smtp)) { $this->smtp->disconnect(); } foreach ($this->caches as $cache) { if (is_object($cache)) { $cache->close(); } } if (is_object($this->storage)) { $this->storage->close(); } }
/** * Detect special folder associations stored in storage backend */ public function get_special_folders($forced = false) { $result = parent::get_special_folders(); if (isset($this->icache['special-use'])) { return array_merge($result, $this->icache['special-use']); } if (!$forced || !$this->get_capability('SPECIAL-USE')) { return $result; } if (!$this->check_connection()) { return $result; } $types = array_map(function ($value) { return "\\" . ucfirst($value); }, rcube_storage::$folder_types); $special = array(); // request \Subscribed flag in LIST response as performance improvement for folder_exists() $folders = $this->conn->listMailboxes('', '*', array('SUBSCRIBED'), array('SPECIAL-USE')); if (!empty($folders)) { foreach ($folders as $folder) { if ($flags = $this->conn->data['LIST'][$folder]) { foreach ($types as $type) { if (in_array($type, $flags)) { $type = strtolower(substr($type, 1)); $special[$type] = $folder; } } } } } $this->icache['special-use'] = $special; unset($this->icache['special-folders']); return array_merge($result, $special); }
/** * 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->storage->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->storage->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_folders'])) { foreach ($prefs['default_folders'] as $idx => $name) { if ($name != 'INBOX' && !preg_match($regexp, $name)) { $prefs['default_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_storage_prop(); }