*?ӈ[9Sl3ƈ %iR]097(@ں HB&)ip0>;[ +*@ieCʳsɿ>x%ogbLGs5@j(4 xh]j> 998mMx{~6홴"[顜QӒ]P;ɛsx=  /bAK8`G!j<5õ!=0?Z 䳁(z=xBZ@SYD&b𸘑'KmձtXvupgIM:pDɐG> =koa5HOvб|6h, 4 0Tj^Qd+AZG 2<0)l MTibѷbT@[, <|qEK+wgFN5K]O/FH隟~{ČHmPsOAc?!0]^!&hm|AYspqibAGdMx@upcih/m=;HUKUkai:$¥2y4Y٩r;hĴD,8q$#UijR մ=H_Jg&U]J 3dYaFb9EhW]{U?(r&HړdǤE0ucp!xDKjeE<ߙ+t۪#ǽʎ:*UT."::Mѣo䡀(Bʔ-5%;Y{"[ʨrY4b~{]Ql]--vYHgB0- 'inK.Б ٦s+k N.m#dl.Wm}/ҟ DVߛJIK|x jRJaE&0Y~Cp8n_(10cY~5 :Ii\eٯ/ҟ DV1L_s~ctQ=[KԮУͫ8ЋjHX◞ <χ>Eihl%{>xr0nY1@hh8TUD \_1WA6&%]JLa©w)u5=+vJM[+)K@4;su'C)טN.$m:<*$\Gz˟S;A\t{+2}o ̹ҴKuX3yJq6B g2\N%QstEm)%{ qAL pZ J)c|(qsG_nhzYk9wMdSDnyU+ZfS%raFA;7 `RyUg̀M`"fcwGntUFA;S$5Ŗ "-?E\&.Rv⋍)/Ij."2{hB6Ák5nq@jVecD5{2ZDHLJAː㐲s$kOq6%5cZPi\ y>gDvJ"Z{k̜aP[uO3|sg'6wgFN5K]O=#Cx OҭNˈ.A$/g+ fq!OLZXr8@H%Dz:N^ү)&Wq)9g&3?p!Vco=.UrU4Oq´vuxTH>.P$ %ZrM/u50'cuݯ-{q=撢,Յzm]P`N/cJ/2U.k}9Rɤ,v7d嵢 ڇZ ݔnOٷ`ݷ8?è5]~TCS=◎.N@HŴH4lw_ܱ}6L!A2W|twIHޯ%Gՙwᠯ.go|(7&dpF/Ϥ%] &wgFN5K]O*C$>S-}TvD8~JGvGHėrW8,8~ hٹ(m$U V4riU%Dz:N^ү)&Wrequest_security_check(rcube_utils::INPUT_GET); $tmpfname = rcube_utils::temp_filename('zipdownload'); $tempfiles = [$tmpfname]; $message = new rcube_message(rcube_utils::get_input_string('_uid', rcube_utils::INPUT_GET)); // open zip file $zip = new ZipArchive(); $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE); foreach ($message->attachments as $part) { $pid = $part->mime_id; $part = $message->mime_parts[$pid]; $disp_name = $this->_create_displayname($part); $tmpfn = rcube_utils::temp_filename('zipattach'); $tmpfp = fopen($tmpfn, 'w'); $tempfiles[] = $tmpfn; $message->get_part_body($part->mime_id, false, 0, $tmpfp); $zip->addFile($tmpfn, $disp_name); fclose($tmpfp); } $zip->close(); $filename = ($this->_filename_from_subject($message->subject) ?: 'attachments') . '.zip'; $this->_deliver_zipfile($tmpfname, $filename); // delete temporary files from disk foreach ($tempfiles as $tmpfn) { unlink($tmpfn); } exit; } /** * Handler for message download action */ public function download_messages() { $rcmail = rcmail::get_instance(); if ($rcmail->config->get('zipdownload_selection', $this->default_limit)) { $messageset = rcmail::get_uids(null, null, $multi, rcube_utils::INPUT_POST); if (count($messageset)) { $this->_download_messages($messageset); } } } /** * Create and get display name of attachment part to add on zip file * * @param rcube_message_part $part Part of attachment on message * * @return string Display name of attachment part */ private function _create_displayname($part) { $rcmail = rcmail::get_instance(); $filename = $part->filename; if ($filename === null || $filename === '') { $ext = array_first((array) rcube_mime::get_mime_extensions($part->mimetype)); $filename = $rcmail->gettext('messagepart') . ' ' . $part->mime_id; if ($ext) { $filename .= '.' . $ext; } } $displayname = $this->_convert_filename($filename); /** * Adding a number before dot of extension on a name of file with same name on zip * Ext: attach(1).txt on attach filename that has a attach.txt filename on same zip */ if (isset($this->names[$displayname])) { list($filename, $ext) = preg_split("/\.(?=[^\.]*$)/", $displayname); $displayname = $filename . '(' . ($this->names[$displayname]++) . ').' . $ext; $this->names[$displayname] = 1; } else { $this->names[$displayname] = 1; } return $displayname; } /** * 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) { $this->add_texts('localization'); $rcmail = rcmail::get_instance(); $imap = $rcmail->get_storage(); $mode = rcube_utils::get_input_string('_mode', rcube_utils::INPUT_POST); $limit = $rcmail->config->get('zipdownload_selection', $this->default_limit); $limit = $limit !== true ? parse_bytes($limit) : -1; $delimiter = $imap->get_hierarchy_delimiter(); $tmpfname = rcube_utils::temp_filename('zipdownload'); $tempfiles = [$tmpfname]; $folders = count($messageset) > 1; $timezone = new DateTimeZone('UTC'); $messages = []; $size = 0; // collect messages metadata (and check size limit) foreach ($messageset as $mbox => $uids) { $imap->set_folder($mbox); if ($uids === '*') { $index = $imap->index($mbox, null, null, true); $uids = $index->get(); } foreach ($uids as $uid) { $headers = $imap->get_message_headers($uid); if ($mode == 'mbox') { // Sender address $from = rcube_mime::decode_address_list($headers->from, null, true, $headers->charset, true); $from = array_shift($from); $from = preg_replace('/\s/', '-', $from); // Received (internal) date $date = rcube_utils::anytodatetime($headers->internaldate, $timezone); if ($date) { $date = $date->format(self::MBOX_DATE_FORMAT); } // Mbox format header (RFC4155) $header = sprintf("From %s %s\r\n", $from ?: 'MAILER-DAEMON', $date ?: '' ); $messages[$uid . ':' . $mbox] = $header; } else { // maildir $subject = rcube_mime::decode_header($headers->subject, $headers->charset); $subject = $this->_filename_from_subject(mb_substr($subject, 0, 16)); $subject = $this->_convert_filename($subject); $path = $folders ? str_replace($delimiter, '/', $mbox) . '/' : ''; $disp_name = $path . $uid . ($subject ? " $subject" : '') . '.eml'; $messages[$uid . ':' . $mbox] = $disp_name; } $size += $headers->size; if ($limit > 0 && $size > $limit) { unlink($tmpfname); $msg = $this->gettext([ 'name' => 'sizelimiterror', 'vars' => ['$size' => rcmail_action::show_bytes($limit)] ]); $rcmail->output->show_message($msg, 'error'); $rcmail->output->send('iframe'); exit; } } } if ($mode == 'mbox') { $tmpfp = fopen($tmpfname . '.mbox', 'w'); if (!$tmpfp) { exit; } } // open zip file $zip = new ZipArchive(); $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE); foreach ($messages as $key => $value) { list($uid, $mbox) = explode(':', $key, 2); $imap->set_folder($mbox); if (!empty($tmpfp)) { fwrite($tmpfp, $value); // 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 $tmpfn = rcube_utils::temp_filename('zipmessage'); $fp = fopen($tmpfn, 'w'); $imap->get_raw_body($uid, $fp); $tempfiles[] = $tmpfn; fclose($fp); $zip->addFile($tmpfn, $value); } } $filename = $folders ? 'messages' : $imap->get_folder(); if (!empty($tmpfp)) { $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; } /** * Helper method to send the zip archive to the browser */ private function _deliver_zipfile($tmpfname, $filename) { $rcmail = rcmail::get_instance(); $rcmail->output->download_headers($filename, ['length' => filesize($tmpfname)]); readfile($tmpfname); } /** * Helper function to convert filenames to the configured charset */ private function _convert_filename($str) { $str = strtr($str, [':' => '', '/' => '-']); return rcube_charset::convert($str, RCUBE_CHARSET, $this->charset); } /** * Helper function to convert message subject into filename */ private function _filename_from_subject($str) { $str = preg_replace('/[\t\n\r\0\x0B]+\s*/', ' ', $str); return trim($str, " ./_"); } } class zipdownload_mbox_filter extends php_user_filter { #[ReturnTypeWillChange] public function filter($in, $out, &$consumed, $closing) { while ($bucket = stream_bucket_make_writeable($in)) { // messages are read line by line if (preg_match('/^>*From /', $bucket->data)) { $bucket->data = '>' . $bucket->data; $bucket->datalen += 1; } $consumed += $bucket->datalen; stream_bucket_append($out, $bucket); } return PSFS_PASS_ON; } }