PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/zipdownload/zipdownload.php

https://github.com/trimbakgopalghare/roundcubemail
PHP | 340 lines | 227 code | 58 blank | 55 comment | 32 complexity | 86b87de4c51f43e165c6a29460751785 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. * ZipDownload
  4. *
  5. * Plugin to allow the download of all message attachments in one zip file
  6. * and downloading of many messages in one go.
  7. *
  8. * @version 3.0
  9. * @requires php_zip extension (including ZipArchive class)
  10. * @author Philip Weir
  11. * @author Thomas Bruderli
  12. * @author Aleksander Machniak
  13. */
  14. class zipdownload extends rcube_plugin
  15. {
  16. public $task = 'mail';
  17. private $charset = 'ASCII';
  18. /**
  19. * Plugin initialization
  20. */
  21. public function init()
  22. {
  23. // check requirements first
  24. if (!class_exists('ZipArchive', false)) {
  25. rcmail::raise_error(array(
  26. 'code' => 520,
  27. 'file' => __FILE__,
  28. 'line' => __LINE__,
  29. 'message' => "php_zip extension is required for the zipdownload plugin"), true, false);
  30. return;
  31. }
  32. $rcmail = rcmail::get_instance();
  33. $this->load_config();
  34. $this->charset = $rcmail->config->get('zipdownload_charset', RCUBE_CHARSET);
  35. $this->add_texts('localization');
  36. if ($rcmail->config->get('zipdownload_attachments', 1) > -1 && ($rcmail->action == 'show' || $rcmail->action == 'preview')) {
  37. $this->add_hook('template_object_messageattachments', array($this, 'attachment_ziplink'));
  38. }
  39. $this->register_action('plugin.zipdownload.attachments', array($this, 'download_attachments'));
  40. $this->register_action('plugin.zipdownload.messages', array($this, 'download_messages'));
  41. if (!$rcmail->action && $rcmail->config->get('zipdownload_selection')) {
  42. $this->download_menu();
  43. }
  44. }
  45. /**
  46. * Place a link/button after attachments listing to trigger download
  47. */
  48. public function attachment_ziplink($p)
  49. {
  50. $rcmail = rcmail::get_instance();
  51. // only show the link if there is more than the configured number of attachments
  52. if (substr_count($p['content'], '<li') > $rcmail->config->get('zipdownload_attachments', 1)) {
  53. $href = $rcmail->url(array(
  54. '_action' => 'plugin.zipdownload.attachments',
  55. '_mbox' => $rcmail->output->env['mailbox'],
  56. '_uid' => $rcmail->output->env['uid'],
  57. ));
  58. $link = html::a(array('href' => $href, 'class' => 'button zipdownload'),
  59. rcube::Q($this->gettext('downloadall'))
  60. );
  61. // append link to attachments list, slightly different in some skins
  62. switch (rcmail::get_instance()->config->get('skin')) {
  63. case 'classic':
  64. $p['content'] = str_replace('</ul>', html::tag('li', array('class' => 'zipdownload'), $link) . '</ul>', $p['content']);
  65. break;
  66. default:
  67. $p['content'] .= $link;
  68. break;
  69. }
  70. $this->include_stylesheet($this->local_skin_path() . '/zipdownload.css');
  71. }
  72. return $p;
  73. }
  74. /**
  75. * Adds download options menu to the page
  76. */
  77. public function download_menu()
  78. {
  79. $this->include_script('zipdownload.js');
  80. $this->add_label('download');
  81. $rcmail = rcmail::get_instance();
  82. $menu = array();
  83. $ul_attr = array('role' => 'menu', 'aria-labelledby' => 'aria-label-zipdownloadmenu');
  84. if ($rcmail->config->get('skin') != 'classic') {
  85. $ul_attr['class'] = 'toolbarmenu';
  86. }
  87. foreach (array('eml', 'mbox', 'maildir') as $type) {
  88. $menu[] = html::tag('li', null, $rcmail->output->button(array(
  89. 'command' => "download-$type",
  90. 'label' => "zipdownload.download$type",
  91. 'classact' => 'active',
  92. )));
  93. }
  94. $rcmail->output->add_footer(html::div(array('id' => 'zipdownload-menu', 'class' => 'popupmenu', 'aria-hidden' => 'true'),
  95. html::tag('h2', array('class' => 'voice', 'id' => 'aria-label-zipdownloadmenu'), "Message Download Options Menu") .
  96. html::tag('ul', $ul_attr, implode('', $menu))));
  97. }
  98. /**
  99. * Handler for attachment download action
  100. */
  101. public function download_attachments()
  102. {
  103. $rcmail = rcmail::get_instance();
  104. $imap = $rcmail->get_storage();
  105. $temp_dir = $rcmail->config->get('temp_dir');
  106. $tmpfname = tempnam($temp_dir, 'zipdownload');
  107. $tempfiles = array($tmpfname);
  108. $message = new rcube_message(rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GET));
  109. // open zip file
  110. $zip = new ZipArchive();
  111. $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE);
  112. foreach ($message->attachments as $part) {
  113. $pid = $part->mime_id;
  114. $part = $message->mime_parts[$pid];
  115. $filename = $part->filename;
  116. if ($filename === null || $filename === '') {
  117. $ext = (array) rcube_mime::get_mime_extensions($part->mimetype);
  118. $ext = array_shift($ext);
  119. $filename = $rcmail->gettext('messagepart') . ' ' . $pid;
  120. if ($ext) {
  121. $filename .= '.' . $ext;
  122. }
  123. }
  124. $disp_name = $this->_convert_filename($filename);
  125. if ($part->body) {
  126. $orig_message_raw = $part->body;
  127. $zip->addFromString($disp_name, $orig_message_raw);
  128. }
  129. else {
  130. $tmpfn = tempnam($temp_dir, 'zipattach');
  131. $tmpfp = fopen($tmpfn, 'w');
  132. $imap->get_message_part($message->uid, $part->mime_id, $part, null, $tmpfp, true);
  133. $tempfiles[] = $tmpfn;
  134. fclose($tmpfp);
  135. $zip->addFile($tmpfn, $disp_name);
  136. }
  137. }
  138. $zip->close();
  139. $filename = ($message->subject ? $message->subject : 'roundcube') . '.zip';
  140. $this->_deliver_zipfile($tmpfname, $filename);
  141. // delete temporary files from disk
  142. foreach ($tempfiles as $tmpfn) {
  143. unlink($tmpfn);
  144. }
  145. exit;
  146. }
  147. /**
  148. * Handler for message download action
  149. */
  150. public function download_messages()
  151. {
  152. $rcmail = rcmail::get_instance();
  153. if ($rcmail->config->get('zipdownload_selection') && !empty($_POST['_uid'])) {
  154. $messageset = rcmail::get_uids();
  155. if (sizeof($messageset)) {
  156. $this->_download_messages($messageset);
  157. }
  158. }
  159. }
  160. /**
  161. * Helper method to packs all the given messages into a zip archive
  162. *
  163. * @param array List of message UIDs to download
  164. */
  165. private function _download_messages($messageset)
  166. {
  167. $rcmail = rcmail::get_instance();
  168. $imap = $rcmail->get_storage();
  169. $mode = rcube_utils::get_input_value('_mode', rcube_utils::INPUT_POST);
  170. $temp_dir = $rcmail->config->get('temp_dir');
  171. $tmpfname = tempnam($temp_dir, 'zipdownload');
  172. $tempfiles = array($tmpfname);
  173. $folders = count($messageset) > 1;
  174. // @TODO: file size limit
  175. // open zip file
  176. $zip = new ZipArchive();
  177. $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE);
  178. if ($mode == 'mbox') {
  179. $tmpfp = fopen($tmpfname . '.mbox', 'w');
  180. }
  181. foreach ($messageset as $mbox => $uids) {
  182. $imap->set_folder($mbox);
  183. $path = $folders ? str_replace($imap->get_hierarchy_delimiter(), '/', $mbox) . '/' : '';
  184. foreach ($uids as $uid) {
  185. $headers = $imap->get_message_headers($uid);
  186. if ($mode == 'mbox') {
  187. $from = rcube_mime::decode_address_list($headers->from, null, true, $headers->charset, true);
  188. $from = array_shift($from);
  189. // Mbox format header
  190. // @FIXME: \r\n or \n
  191. // @FIXME: date format
  192. $header = sprintf("From %s %s\r\n",
  193. // replace spaces with hyphens
  194. $from ? preg_replace('/\s/', '-', $from) : 'MAILER-DAEMON',
  195. // internaldate
  196. $headers->internaldate
  197. );
  198. fwrite($tmpfp, $header);
  199. // Use stream filter to quote "From " in the message body
  200. stream_filter_register('mbox_filter', 'zipdownload_mbox_filter');
  201. $filter = stream_filter_append($tmpfp, 'mbox_filter');
  202. $imap->get_raw_body($uid, $tmpfp);
  203. stream_filter_remove($filter);
  204. fwrite($tmpfp, "\r\n");
  205. }
  206. else { // maildir
  207. $subject = rcube_mime::decode_mime_string((string)$headers->subject);
  208. $subject = $this->_convert_filename($subject);
  209. $subject = substr($subject, 0, 16);
  210. $disp_name = ($subject ? $subject : 'message_rfc822') . ".eml";
  211. $disp_name = $path . $uid . "_" . $disp_name;
  212. $tmpfn = tempnam($temp_dir, 'zipmessage');
  213. $tmpfp = fopen($tmpfn, 'w');
  214. $imap->get_raw_body($uid, $tmpfp);
  215. $tempfiles[] = $tmpfn;
  216. fclose($tmpfp);
  217. $zip->addFile($tmpfn, $disp_name);
  218. }
  219. }
  220. }
  221. $filename = $folders ? 'messages' : $imap->get_folder();
  222. if ($mode == 'mbox') {
  223. $tempfiles[] = $tmpfname . '.mbox';
  224. fclose($tmpfp);
  225. $zip->addFile($tmpfname . '.mbox', $filename . '.mbox');
  226. }
  227. $zip->close();
  228. $this->_deliver_zipfile($tmpfname, $filename . '.zip');
  229. // delete temporary files from disk
  230. foreach ($tempfiles as $tmpfn) {
  231. unlink($tmpfn);
  232. }
  233. exit;
  234. }
  235. /**
  236. * Helper method to send the zip archive to the browser
  237. */
  238. private function _deliver_zipfile($tmpfname, $filename)
  239. {
  240. $browser = new rcube_browser;
  241. $rcmail = rcmail::get_instance();
  242. $rcmail->output->nocacheing_headers();
  243. if ($browser->ie)
  244. $filename = rawurlencode($filename);
  245. else
  246. $filename = addcslashes($filename, '"');
  247. // send download headers
  248. header("Content-Type: application/octet-stream");
  249. if ($browser->ie) {
  250. header("Content-Type: application/force-download");
  251. }
  252. // don't kill the connection if download takes more than 30 sec.
  253. @set_time_limit(0);
  254. header("Content-Disposition: attachment; filename=\"". $filename ."\"");
  255. header("Content-length: " . filesize($tmpfname));
  256. readfile($tmpfname);
  257. }
  258. /**
  259. * Helper function to convert filenames to the configured charset
  260. */
  261. private function _convert_filename($str)
  262. {
  263. $str = rcube_charset::convert($str, RCUBE_CHARSET, $this->charset);
  264. return strtr($str, array(':' => '', '/' => '-'));
  265. }
  266. }
  267. class zipdownload_mbox_filter extends php_user_filter
  268. {
  269. function filter($in, $out, &$consumed, $closing)
  270. {
  271. while ($bucket = stream_bucket_make_writeable($in)) {
  272. // messages are read line by line
  273. if (preg_match('/^>*From /', $bucket->data)) {
  274. $bucket->data = '>' . $bucket->data;
  275. $bucket->datalen += 1;
  276. }
  277. $consumed += $bucket->datalen;
  278. stream_bucket_append($out, $bucket);
  279. }
  280. return PSFS_PASS_ON;
  281. }
  282. }