/** * __construct * * Provide a uid, and parse message structure. * * @param string $uid The message UID. * @param string $folder Folder name * * @see self::$app, self::$storage, self::$opt, self::$parts */ function __construct($uid, $folder = null) { $this->uid = $uid; $this->app = rcube::get_instance(); $this->storage = $this->app->get_storage(); $this->folder = strlen($folder) ? $folder : $this->storage->get_folder(); $this->storage->set_options(array('all_headers' => true)); // Set current folder $this->storage->set_folder($this->folder); $this->headers = $this->storage->get_message($uid); if (!$this->headers) { return; } $this->mime = new rcube_mime($this->headers->charset); $this->subject = $this->mime->decode_mime_string($this->headers->subject); list(, $this->sender) = each($this->mime->decode_address_list($this->headers->from, 1)); $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' => $this->app->url(array('action' => 'get', 'mbox' => $this->storage->get_folder(), 'uid' => $uid))); if (!empty($this->headers->structure)) { $this->get_mime_numbers($this->headers->structure); $this->parse_structure($this->headers->structure); } else { $this->body = $this->storage->get_body($uid); } // notify plugins and let them analyze this structured message object $this->app->plugins->exec_hook('message_load', array('object' => $this)); }
/** * Test decoding of header values * Uses rcube_mime::decode_mime_string() */ function test_header_decode_qp() { $test = array('quoted-printable (1)' => array('in' => '=?utf-8?Q?Certifica=C3=A7=C3=A3??=', 'out' => 'Certifica=C3=A7=C3=A3?'), 'quoted-printable (2)' => array('in' => '=?utf-8?Q?Certifica=?= =?utf-8?Q?C3=A7=C3=A3?=', 'out' => 'Certifica=C3=A7=C3=A3'), 'quoted-printable (3)' => array('in' => '=?utf-8?Q??= =?utf-8?Q??=', 'out' => ''), 'quoted-printable (4)' => array('in' => '=?utf-8?Q??= a =?utf-8?Q??=', 'out' => ' a '), 'quoted-printable (5)' => array('in' => '=?utf-8?Q?a?= =?utf-8?Q?b?=', 'out' => 'ab'), 'quoted-printable (6)' => array('in' => '=?utf-8?Q? ?= =?utf-8?Q?a?=', 'out' => ' a'), 'quoted-printable (7)' => array('in' => '=?utf-8?Q?___?= =?utf-8?Q?a?=', 'out' => ' a')); foreach ($test as $idx => $item) { $res = rcube_mime::decode_mime_string($item['in'], 'UTF-8'); $res = quoted_printable_encode($res); $this->assertEquals($item['out'], $res, "Header decoding for: " . $idx); } }
/** * Recursive method to convert a Mail_mimeDecode part into a rcube_message_part object * * @param object A message part struct * @param int Part count * @param string Parent MIME ID * * @return object rcube_message_part */ private static function structure_part($part, $count = 0, $parent = '') { $struct = new rcube_message_part(); $struct->mime_id = $part->mime_id ? $part->mime_id : (empty($parent) ? (string) $count : "{$parent}.{$count}"); $struct->headers = $part->headers; $struct->ctype_primary = $part->ctype_primary; $struct->ctype_secondary = $part->ctype_secondary; $struct->mimetype = $part->ctype_primary . '/' . $part->ctype_secondary; $struct->ctype_parameters = $part->ctype_parameters; if ($part->headers['content-transfer-encoding']) { $struct->encoding = $part->headers['content-transfer-encoding']; } if ($part->ctype_parameters['charset']) { $struct->charset = $part->ctype_parameters['charset']; } $part_charset = $struct->charset ? $struct->charset : self::get_charset(); // determine filename if (($filename = $part->d_parameters['filename']) || ($filename = $part->ctype_parameters['name'])) { $struct->filename = rcube_mime::decode_mime_string($filename, $part_charset); } // copy part body and convert it to UTF-8 if necessary $struct->body = $part->ctype_primary == 'text' || !$part->ctype_parameters['charset'] ? rcube_charset::convert($part->body, $part_charset) : $part->body; $struct->size = strlen($part->body); $struct->disposition = $part->disposition; foreach ((array) $part->parts as $child_part) { $struct->parts[] = self::structure_part($child_part, ++$count, $struct->mime_id); } return $struct; }
/** * Given a header, this function will decode it according to RFC2047. * Probably not *exactly* conformant, but it does pass all the given * examples (in RFC2047). * * @param string $input Input header value to decode * * @return string Decoded header value */ protected function decodeHeader($input) { return rcube_mime::decode_mime_string($input, $this->params['default_charset']); }
public static function decode_mime_string($input, $fallback = null) { return rcube_mime::decode_mime_string($input, $fallback); }
/** * Helper method to packs all the given messages into a zip archive * * @param array List of message UIDs to download */ private function _download_messages($uids) { $rcmail = rcmail::get_instance(); $imap = $rcmail->storage; $temp_dir = $rcmail->config->get('temp_dir'); $tmpfname = tempnam($temp_dir, 'zipdownload'); $tempfiles = array($tmpfname); // open zip file $zip = new ZipArchive(); $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE); foreach ($uids as $uid) { $headers = $imap->get_message_headers($uid); $subject = rcube_mime::decode_mime_string((string) $headers->subject); $subject = $this->_convert_filename($subject); $subject = substr($subject, 0, 16); if (isset($subject) && $subject != "") { $disp_name = $subject . ".eml"; } else { $disp_name = "message_rfc822.eml"; } $disp_name = $uid . "_" . $disp_name; $tmpfn = tempnam($temp_dir, 'zipmessage'); $tmpfp = fopen($tmpfn, 'w'); $imap->get_raw_body($uid, $tmpfp); $tempfiles[] = $tmpfn; fclose($tmpfp); $zip->addFile($tmpfn, $disp_name); } $zip->close(); $this->_deliver_zipfile($tmpfname, $imap->get_folder() . '.zip'); // delete temporary files from disk foreach ($tempfiles as $tmpfn) { unlink($tmpfn); } exit; }
/** * Helper method to packs all the given messages into a zip archive * * @param array List of message UIDs to download */ private function _download_messages($messageset) { $rcmail = rcmail::get_instance(); $imap = $rcmail->get_storage(); $mode = rcube_utils::get_input_value('_mode', rcube_utils::INPUT_POST); $temp_dir = $rcmail->config->get('temp_dir'); $tmpfname = tempnam($temp_dir, 'zipdownload'); $tempfiles = array($tmpfname); $folders = count($messageset) > 1; // @TODO: file size limit // open zip file $zip = new ZipArchive(); $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE); if ($mode == 'mbox') { $tmpfp = fopen($tmpfname . '.mbox', 'w'); } foreach ($messageset as $mbox => $uids) { $imap->set_folder($mbox); $path = $folders ? str_replace($imap->get_hierarchy_delimiter(), '/', $mbox) . '/' : ''; foreach ($uids as $uid) { $headers = $imap->get_message_headers($uid); if ($mode == 'mbox') { $from = rcube_mime::decode_address_list($headers->from, null, true, $headers->charset, true); $from = array_shift($from); // Mbox format header // @FIXME: \r\n or \n // @FIXME: date format $header = sprintf("From %s %s\r\n", $from ? preg_replace('/\\s/', '-', $from) : 'MAILER-DAEMON', $headers->internaldate); fwrite($tmpfp, $header); // Use stream filter to quote "From " in the message body stream_filter_register('mbox_filter', 'zipdownload_mbox_filter'); $filter = stream_filter_append($tmpfp, 'mbox_filter'); $imap->get_raw_body($uid, $tmpfp); stream_filter_remove($filter); fwrite($tmpfp, "\r\n"); } else { // maildir $subject = rcube_mime::decode_mime_string((string) $headers->subject); $subject = $this->_convert_filename($subject); $subject = substr($subject, 0, 16); $disp_name = ($subject ? $subject : 'message_rfc822') . ".eml"; $disp_name = $path . $uid . "_" . $disp_name; $tmpfn = tempnam($temp_dir, 'zipmessage'); $tmpfp = fopen($tmpfn, 'w'); $imap->get_raw_body($uid, $tmpfp); $tempfiles[] = $tmpfn; fclose($tmpfp); $zip->addFile($tmpfn, $disp_name); } } } $filename = $folders ? 'messages' : $imap->get_folder(); if ($mode == 'mbox') { $tempfiles[] = $tmpfname . '.mbox'; fclose($tmpfp); $zip->addFile($tmpfname . '.mbox', $filename . '.mbox'); } $zip->close(); $this->_deliver_zipfile($tmpfname, $filename . '.zip'); // delete temporary files from disk foreach ($tempfiles as $tmpfn) { unlink($tmpfn); } exit; }
private function archive($uids, $verbose = true) { global $E_SESSION; $rcmail = rcmail::get_instance(); $path = getcwd(); chdir(str_replace(array('/modules/CRM/Roundcube/RC', '\\modules\\CRM\\Roundcube\\RC'), '', $path)); $msgs = array(); if (!is_array($uids)) { $uids = $uids->get(); } foreach ($uids as $uid) { $msg = new rcube_message($uid); if ($msg === null || empty($msg->headers)) { if ($verbose) { $rcmail->output->command('display_message', 'messageopenerror', 'error'); } return false; } else { $msgs[$uid] = $msg; } } $map = array(); foreach ($msgs as $k => $msg) { $sends = $rcmail->storage->decode_address_list($msg->headers->to); $map[$k] = array(); foreach ($sends as $send) { $addr = $send['mailto']; $ret = $this->look_contact($addr); $map[$k] = array_merge($map[$k], $ret); } $addr = $rcmail->storage->decode_address_list($msg->headers->from); if ($addr) { $addr = array_shift($addr); } if (!isset($addr['mailto']) || !$addr['mailto']) { $map[$k] = false; continue; } $ret = $this->look_contact($addr['mailto']); $map[$k] = array_merge($map[$k], $ret); } if (!isset($_SESSION['force_archive'])) { $_SESSION['force_archive'] = array(); } foreach ($map as $k => $ret) { if (!$ret && !isset($_SESSION['force_archive'][$k]) && $verbose) { $_SESSION['force_archive'][$k] = 1; $rcmail->output->command('display_message', $this->gettext('contactnotfound'), 'error'); return false; } } $attachments_dir = DATA_DIR . '/CRM_Roundcube/attachments/'; $epesi_mails = array(); if (!file_exists($attachments_dir)) { mkdir($attachments_dir); } foreach ($msgs as $k => $msg) { $contacts = $map[$k]; $mime_map = array(); foreach ($msg->mime_parts as $mid => $m) { $mime_map[$m->mime_id] = md5($k . microtime(true) . $mid); } if ($msg->has_html_part()) { // $body = $msg->first_html_part(); foreach ($msg->mime_parts as $mime_id => $part) { $mimetype = strtolower($part->ctype_primary . '/' . $part->ctype_secondary); if ($mimetype == 'text/html') { $body = $rcmail->storage->get_message_part($msg->uid, $mime_id, $part); if (isset($part->replaces)) { $cid_map = $part->replaces; } else { $cid_map = array(); } break; } } foreach ($cid_map as $k => &$v) { if (preg_match('/_part=(.*?)&/', $v, $matches)) { $mid = $matches[1]; if (isset($mime_map[$mid])) { $v = 'get.php?' . http_build_query(array('mail_id' => '__MAIL_ID__', 'mime_id' => $mime_map[$mid])); } } else { unset($cid_map[$k]); } } $body = rcmail_wash_html($body, array('safe' => true, 'inline_html' => true), $cid_map); } else { $body = '<pre>' . $msg->first_text_part() . '</pre>'; } $date = rcube_imap_generic::strToTime($msg->get_header('DATE')); $headers = array(); foreach ($msg->headers as $k => $v) { if (is_string($v) && $k != 'from' && $k != 'to' && $k != 'body_structure') { $headers[] = $k . ': ' . rcube_mime::decode_mime_string((string) $v); } } $message_id = str_replace(array('<', '>'), '', $msg->get_header('MESSAGE-ID')); if (Utils_RecordBrowserCommon::get_records_count('rc_mails', array('message_id' => $message_id)) > 0) { $rcmail->output->command('display_message', $this->gettext('archived_duplicate'), 'warning'); return false; } $employee = DB::GetOne('SELECT id FROM contact_data_1 WHERE active=1 AND f_login=%d', array($E_SESSION['user'])); $data = array('message_id' => $message_id, 'references' => $msg->get_header('REFERENCES'), 'contacts' => $contacts, 'date' => $date, 'subject' => substr($msg->subject, 0, 256), 'body' => $body, 'headers_data' => implode("\n", $headers), 'from' => $rcmail->storage->decode_header($msg->headers->from), 'to' => $rcmail->storage->decode_header($msg->headers->to), 'employee' => $employee); $id = Utils_RecordBrowserCommon::new_record('rc_mails', $data); $epesi_mails[] = $id; foreach ($contacts as $c) { list($rs, $con_id) = explode(':', $c); if ($rs == 'P') { Utils_WatchdogCommon::new_event('contact', $con_id, 'N_New mail'); } else { Utils_WatchdogCommon::new_event('company', $con_id, 'N_New mail'); } } Utils_WatchdogCommon::new_event('contact', $employee, 'N_New mail'); /*DB::Execute('INSERT INTO rc_mails_data_1(created_on,created_by,f_contacts,f_date,f_employee,f_subject,f_body,f_headers_data,f_direction) VALUES(%T,%d,%s,%T,%d,%s,%s,%s,%b)',array( time(),$E_SESSION['user'],$contacts,$date,$employee,substr($msg->subject,0,256),$body,implode("\n",$headers),$sent_mbox)); $id = DB::Insert_ID('rc_mails_data_1','id');*/ foreach ($msg->mime_parts as $mid => $m) { if (!$m->disposition) { continue; } if (isset($cid_map['cid:' . $m->content_id])) { $attachment = 0; } else { $attachment = 1; } DB::Execute('INSERT INTO rc_mails_attachments(mail_id,type,name,mime_id,attachment) VALUES(%d,%s,%s,%s,%b)', array($id, $m->mimetype, $m->filename, $mime_map[$m->mime_id], $attachment)); if (!file_exists($attachments_dir . $id)) { mkdir($attachments_dir . $id); } $fp = fopen($attachments_dir . $id . '/' . $mime_map[$m->mime_id], 'w'); $msg->get_part_content($m->mime_id, $fp); fclose($fp); } } //$rcmail->output->command('delete_messages'); $E_SESSION['rc_mails_cp'] = $epesi_mails; chdir($path); return true; }
/** * Set attachment filename from message part structure * * @param rcube_message_part $part Part object * @param string $headers Part's raw headers */ protected function set_part_filename(&$part, $headers = null) { if (!empty($part->d_parameters['filename'])) { $filename_mime = $part->d_parameters['filename']; } else { if (!empty($part->d_parameters['filename*'])) { $filename_encoded = $part->d_parameters['filename*']; } else { if (!empty($part->ctype_parameters['name*'])) { $filename_encoded = $part->ctype_parameters['name*']; } else { if (!empty($part->d_parameters['filename*0'])) { $i = 0; while (isset($part->d_parameters['filename*' . $i])) { $filename_mime .= $part->d_parameters['filename*' . $i]; $i++; } // some servers (eg. dovecot-1.x) have no support for parameter value continuations // we must fetch and parse headers "manually" if ($i < 2) { if (!$headers) { $headers = $this->conn->fetchPartHeader($this->folder, $this->msg_uid, true, $part->mime_id); } $filename_mime = ''; $i = 0; while (preg_match('/filename\\*' . $i . '\\s*=\\s*"*([^"\\n;]+)[";]*/', $headers, $matches)) { $filename_mime .= $matches[1]; $i++; } } } else { if (!empty($part->d_parameters['filename*0*'])) { $i = 0; while (isset($part->d_parameters['filename*' . $i . '*'])) { $filename_encoded .= $part->d_parameters['filename*' . $i . '*']; $i++; } if ($i < 2) { if (!$headers) { $headers = $this->conn->fetchPartHeader($this->folder, $this->msg_uid, true, $part->mime_id); } $filename_encoded = ''; $i = 0; $matches = array(); while (preg_match('/filename\\*' . $i . '\\*\\s*=\\s*"*([^"\\n;]+)[";]*/', $headers, $matches)) { $filename_encoded .= $matches[1]; $i++; } } } else { if (!empty($part->ctype_parameters['name*0'])) { $i = 0; while (isset($part->ctype_parameters['name*' . $i])) { $filename_mime .= $part->ctype_parameters['name*' . $i]; $i++; } if ($i < 2) { if (!$headers) { $headers = $this->conn->fetchPartHeader($this->folder, $this->msg_uid, true, $part->mime_id); } $filename_mime = ''; $i = 0; $matches = array(); while (preg_match('/\\s+name\\*' . $i . '\\s*=\\s*"*([^"\\n;]+)[";]*/', $headers, $matches)) { $filename_mime .= $matches[1]; $i++; } } } else { if (!empty($part->ctype_parameters['name*0*'])) { $i = 0; while (isset($part->ctype_parameters['name*' . $i . '*'])) { $filename_encoded .= $part->ctype_parameters['name*' . $i . '*']; $i++; } if ($i < 2) { if (!$headers) { $headers = $this->conn->fetchPartHeader($this->folder, $this->msg_uid, true, $part->mime_id); } $filename_encoded = ''; $i = 0; $matches = array(); while (preg_match('/\\s+name\\*' . $i . '\\*\\s*=\\s*"*([^"\\n;]+)[";]*/', $headers, $matches)) { $filename_encoded .= $matches[1]; $i++; } } } else { if (!empty($part->ctype_parameters['name'])) { $filename_mime = $part->ctype_parameters['name']; } else { if (!empty($part->headers['content-description'])) { $filename_mime = $part->headers['content-description']; } else { return; } } } } } } } } } // decode filename if (!empty($filename_mime)) { if (!empty($part->charset)) { $charset = $part->charset; } else { if (!empty($this->struct_charset)) { $charset = $this->struct_charset; } else { $charset = rcube_charset::detect($filename_mime, $this->default_charset); } } $part->filename = rcube_mime::decode_mime_string($filename_mime, $charset); } else { if (!empty($filename_encoded)) { // decode filename according to RFC 2231, Section 4 if (preg_match("/^([^']*)'[^']*'(.*)\$/", $filename_encoded, $fmatches)) { $filename_charset = $fmatches[1]; $filename_encoded = $fmatches[2]; } $part->filename = rcube_charset::convert(urldecode($filename_encoded), $filename_charset); } } }
/** * Helper method to packs all the given messages into a zip archive * * @param array List of message UIDs to download */ private function _download_messages($messageset) { $rcmail = rcmail::get_instance(); $imap = $rcmail->get_storage(); $mode = rcube_utils::get_input_value('_mode', rcube_utils::INPUT_POST); $temp_dir = $rcmail->config->get('temp_dir'); $tmpfname = tempnam($temp_dir, 'zipdownload'); $tempfiles = array($tmpfname); $folders = count($messageset) > 1; // @TODO: file size limit // open zip file $zip = new ZipArchive(); $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE); if ($mode == 'mbox') { $tmpfp = fopen($tmpfname . '.mbox', 'w'); } $start = time(); if (!is_array($_SESSION['zipdownload_uids'])) { $_SESSION['zipdownload_uids'] = array(); } $count = 0; foreach ($messageset as $mbox => $uids) { $imap->set_folder($mbox); $path = $folders ? str_replace($imap->get_hierarchy_delimiter(), '/', $mbox) . '/' : ''; if ($uids === '*') { $index = $imap->index($mbox, null, null, true); $uids = $index->get(); } foreach ($uids as $uid) { if (isset($_SESSION['zipdownload_uids'][$mbox . '|' . $uid])) { continue; } $count++; $headers = $imap->get_message_headers($uid); if ($mode == 'mbox') { $from = rcube_mime::decode_address_list($headers->from, null, true, $headers->charset, true); $from = array_shift($from); // Mbox format header // @FIXME: \r\n or \n // @FIXME: date format $header = sprintf("From %s %s\r\n", $from ? preg_replace('/\\s/', '-', $from) : 'MAILER-DAEMON', $headers->internaldate); fwrite($tmpfp, $header); // Use stream filter to quote "From " in the message body stream_filter_register('mbox_filter', '_zipdownload_mbox_filter'); $filter = stream_filter_append($tmpfp, 'mbox_filter'); $imap->get_raw_body($uid, $tmpfp); stream_filter_remove($filter); fwrite($tmpfp, "\r\n"); } else { // maildir $subject = rcube_mime::decode_mime_string((string) $headers->subject); $subject = $this->_convert_filename($subject); $subject = substr($subject, 0, 16); $disp_name = ($subject ? $subject : 'message_rfc822') . ".eml"; $disp_name = $path . $uid . "_" . $disp_name; $tmpfn = tempnam($temp_dir, 'zipmessage'); $tmpfp = fopen($tmpfn, 'w'); $imap->get_raw_body($uid, $tmpfp); $tempfiles[] = $tmpfn; fclose($tmpfp); $zip->addFile($tmpfn, $disp_name); } $_SESSION['zipdownload_uids'][$mbox . '|' . $uid] = 1; if ($to = $rcmail->config->get('zipdownload_resume', 25)) { if (time() > $start + $to) { if (isset($_SESSION['zipdownload_count'])) { $_SESSION['zipdownload']++; } else { $_SESSION['zipdownload'] = 0; } break; } } } } $filename = $folders ? 'messages' : $imap->get_folder(); if ($mode == 'mbox') { $tempfiles[] = $tmpfname . '.mbox'; fclose($tmpfp); $zip->addFile($tmpfname . '.mbox', $filename . '.mbox'); } $zip->close(); if ($count > 0) { $this->_deliver_zipfile($tmpfname, $filename . ($_SESSION['zipdownload_count'] > 0 ? '(' . $_SESSION['zipdownload_count'] . ')' : '') . '.zip'); // delete temporary files from disk foreach ($tempfiles as $tmpfn) { unlink($tmpfn); } } else { echo html::tag('script', array('type' => 'text/javascript'), 'parent.rcmail.http_post("plugin.zipdownload.abort", "_mbox' . $mbox . '");' . "\r\n" . 'parent.rcmail.set_busy(false, "loading", parent.lock);'); } exit; }