You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. /**
  3. +-----------------------------------------------------------------------+
  4. | program/steps/mail/attachments.inc |
  5. | |
  6. | This file is part of the Roundcube Webmail client |
  7. | Copyright (C) 2005-2013, The Roundcube Dev Team |
  8. | |
  9. | Licensed under the GNU General Public License version 3 or |
  10. | any later version with exceptions for skins & plugins. |
  11. | See the README file for a full license statement. |
  12. | |
  13. | PURPOSE: |
  14. | Upload, remove, display attachments in compose form |
  15. | |
  16. +-----------------------------------------------------------------------+
  17. | Author: Thomas Bruederli <roundcube@gmail.com> |
  18. +-----------------------------------------------------------------------+
  19. */
  20. // Upload progress update
  21. if (!empty($_GET['_progress'])) {
  22. $RCMAIL->upload_progress();
  23. }
  24. $COMPOSE_ID = rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC);
  25. $COMPOSE = null;
  26. if ($COMPOSE_ID && $_SESSION['compose_data_' . $COMPOSE_ID]) {
  27. $SESSION_KEY = 'compose_data_' . $COMPOSE_ID;
  28. $COMPOSE =& $_SESSION[$SESSION_KEY];
  29. }
  30. if (!$COMPOSE) {
  31. die("Invalid session var!");
  32. }
  33. $file_id = rcube_utils::get_input_value('_file', rcube_utils::INPUT_GPC);
  34. $file_id = preg_replace('/^rcmfile/', '', $file_id) ?: 'unknown';
  35. // remove an attachment
  36. if ($RCMAIL->action == 'remove-attachment') {
  37. if ($attachment = $COMPOSE['attachments'][$file_id]) {
  38. $attachment = $RCMAIL->plugins->exec_hook('attachment_delete', $attachment);
  39. }
  40. if ($attachment['status']) {
  41. if (is_array($COMPOSE['attachments'][$file_id])) {
  42. $RCMAIL->session->remove($SESSION_KEY . '.attachments.' . $file_id);
  43. $OUTPUT->command('remove_from_attachment_list', "rcmfile$file_id");
  44. }
  45. }
  46. $OUTPUT->send();
  47. exit;
  48. }
  49. // rename an attachment
  50. if ($RCMAIL->action == 'rename-attachment') {
  51. $filename = rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST);
  52. $filename = trim($filename);
  53. if (strlen($filename)
  54. && ($attachment = $COMPOSE['attachments'][$file_id])
  55. && is_array($attachment)
  56. ) {
  57. $attachment['name'] = $filename;
  58. $RCMAIL->session->remove($SESSION_KEY . '.attachments. ' . $file_id);
  59. $RCMAIL->session->append($SESSION_KEY . '.attachments', $attachment['id'], $attachment);
  60. $OUTPUT->command('rename_attachment_handler', "rcmfile$file_id", $filename);
  61. }
  62. $OUTPUT->send();
  63. exit;
  64. }
  65. if ($RCMAIL->action == 'display-attachment') {
  66. $RCMAIL->display_uploaded_file($COMPOSE['attachments'][$file_id]);
  67. exit;
  68. }
  69. /***** attachment upload action *****/
  70. // clear all stored output properties (like scripts and env vars)
  71. $OUTPUT->reset();
  72. $uploadid = rcube_utils::get_input_value('_uploadid', rcube_utils::INPUT_GPC);
  73. $uri = rcube_utils::get_input_value('_uri', rcube_utils::INPUT_POST);
  74. // handle dropping a reference to an attachment part of some message
  75. if ($uri) {
  76. $url = parse_url($uri);
  77. parse_str($url['query'], $params);
  78. if (strlen($params['_mbox']) && $params['_uid'] && $params['_part']) {
  79. // @TODO: at some point we might support drag-n-drop between
  80. // two different accounts on the same server, for now make sure
  81. // this is the same server and the same user
  82. list($host, $port) = explode(':', $_SERVER['HTTP_HOST']);
  83. if ($host == $url['host'] && $port == $url['port']
  84. && $RCMAIL->get_user_name() == rawurldecode($url['user'])
  85. ) {
  86. $message = new rcube_message($params['_uid'], $params['_mbox']);
  87. if ($message && !empty($message->headers)) {
  88. $attachment = rcmail_save_attachment($message, $params['_part'], $COMPOSE_ID);
  89. }
  90. }
  91. }
  92. $plugin = $RCMAIL->plugins->exec_hook('attachment_from_uri', array(
  93. 'attachment' => $attachment, 'uri' => $uri, 'compose_id' => $COMPOSE_ID));
  94. if ($plugin['attachment']) {
  95. rcmail_attachment_success($plugin['attachment'], $uploadid);
  96. }
  97. else {
  98. $OUTPUT->command('display_message', $RCMAIL->gettext('filelinkerror'), 'error');
  99. $OUTPUT->command('remove_from_attachment_list', $uploadid);
  100. }
  101. $OUTPUT->send();
  102. return;
  103. }
  104. // handle file(s) upload
  105. if (is_array($_FILES['_attachments']['tmp_name'])) {
  106. $multiple = count($_FILES['_attachments']['tmp_name']) > 1;
  107. $errors = array();
  108. foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
  109. // Process uploaded attachment if there is no error
  110. $err = $_FILES['_attachments']['error'][$i];
  111. if (!$err) {
  112. $filename = $_FILES['_attachments']['name'][$i];
  113. $filesize = $_FILES['_attachments']['size'][$i];
  114. $filetype = rcube_mime::file_content_type($filepath, $filename, $_FILES['_attachments']['type'][$i]);
  115. if ($err = rcmail_check_message_size($filesize, $filetype)) {
  116. if (!in_array($err, $errors)) {
  117. $OUTPUT->command('display_message', $err, 'error');
  118. $OUTPUT->command('remove_from_attachment_list', $uploadid);
  119. $errors[] = $err;
  120. }
  121. continue;
  122. }
  123. $attachment = $RCMAIL->plugins->exec_hook('attachment_upload', array(
  124. 'path' => $filepath,
  125. 'name' => $filename,
  126. 'size' => $filesize,
  127. 'mimetype' => $filetype,
  128. 'group' => $COMPOSE_ID,
  129. ));
  130. }
  131. if (!$err && $attachment['status'] && !$attachment['abort']) {
  132. // store new attachment in session
  133. unset($attachment['status'], $attachment['abort']);
  134. $RCMAIL->session->append($SESSION_KEY . '.attachments', $attachment['id'], $attachment);
  135. rcmail_attachment_success($attachment, $uploadid);
  136. }
  137. else { // upload failed
  138. if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
  139. $size = $RCMAIL->show_bytes(rcube_utils::max_upload_size());
  140. $msg = $RCMAIL->gettext(array('name' => 'filesizeerror', 'vars' => array('size' => $size)));
  141. }
  142. else if ($attachment['error']) {
  143. $msg = $attachment['error'];
  144. }
  145. else {
  146. $msg = $RCMAIL->gettext('fileuploaderror');
  147. }
  148. if ($attachment['error'] || $err != UPLOAD_ERR_NO_FILE) {
  149. if (!in_array($msg, $errors)) {
  150. $OUTPUT->command('display_message', $msg, 'error');
  151. $OUTPUT->command('remove_from_attachment_list', $uploadid);
  152. $errors[] = $msg;
  153. }
  154. }
  155. }
  156. }
  157. }
  158. else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  159. // if filesize exceeds post_max_size then $_FILES array is empty,
  160. // show filesizeerror instead of fileuploaderror
  161. if ($maxsize = ini_get('post_max_size')) {
  162. $msg = $RCMAIL->gettext(array(
  163. 'name' => 'filesizeerror',
  164. 'vars' => array('size' => $RCMAIL->show_bytes(parse_bytes($maxsize)))
  165. ));
  166. }
  167. else {
  168. $msg = $RCMAIL->gettext('fileuploaderror');
  169. }
  170. $OUTPUT->command('display_message', $msg, 'error');
  171. $OUTPUT->command('remove_from_attachment_list', $uploadid);
  172. }
  173. // send html page with JS calls as response
  174. $OUTPUT->command('auto_save_start', false);
  175. $OUTPUT->send('iframe');
  176. function rcmail_attachment_success($attachment, $uploadid)
  177. {
  178. global $RCMAIL, $COMPOSE;
  179. $id = $attachment['id'];
  180. if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) {
  181. $button = html::img(array(
  182. 'src' => $icon,
  183. 'alt' => $RCMAIL->gettext('delete')
  184. ));
  185. }
  186. else if ($COMPOSE['textbuttons']) {
  187. $button = rcube::Q($RCMAIL->gettext('delete'));
  188. }
  189. else {
  190. $button = '';
  191. }
  192. $link_content = sprintf('%s <span class="attachment-size"> (%s)</span>',
  193. rcube::Q($attachment['name']), $RCMAIL->show_bytes($attachment['size']));
  194. $content_link = html::a(array(
  195. 'href' => "#load",
  196. 'class' => 'filename',
  197. 'onclick' => sprintf("return %s.command('load-attachment','rcmfile%s', this, event)", rcmail_output::JS_OBJECT_NAME, $id),
  198. ), $link_content);
  199. $delete_link = html::a(array(
  200. 'href' => "#delete",
  201. 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this, event)", rcmail_output::JS_OBJECT_NAME, $id),
  202. 'title' => $RCMAIL->gettext('delete'),
  203. 'class' => 'delete',
  204. 'aria-label' => $RCMAIL->gettext('delete') . ' ' . $attachment['name'],
  205. ), $button);
  206. $content = $COMPOSE['icon_pos'] == 'left' ? $delete_link.$content_link : $content_link.$delete_link;
  207. $RCMAIL->output->command('add2attachment_list', "rcmfile$id", array(
  208. 'html' => $content,
  209. 'name' => $attachment['name'],
  210. 'mimetype' => $attachment['mimetype'],
  211. 'classname' => rcube_utils::file2class($attachment['mimetype'], $attachment['name']),
  212. 'complete' => true), $uploadid);
  213. }
  214. /**
  215. * Checks if the attached file will fit in message size limit.
  216. * Calculates size of all attachments and compares with the limit.
  217. *
  218. * @param int $filesize File size
  219. * @param string $filetype File mimetype
  220. *
  221. * @return string Error message if the limit is exceeded
  222. */
  223. function rcmail_check_message_size($filesize, $filetype)
  224. {
  225. global $RCMAIL, $COMPOSE;
  226. $limit = parse_bytes($RCMAIL->config->get('max_message_size'));
  227. $size = 10 * 1024; // size of message body
  228. if (!$limit) {
  229. return;
  230. }
  231. // add size of already attached files
  232. foreach ((array) $COMPOSE['attachments'] as $att) {
  233. // All attachments are base64-encoded except message/rfc822 (see sendmail.inc)
  234. $multip = $att['mimetype'] == 'message/rfc822' ? 1 : 1.33;
  235. $size += $att['size'] * $multip;
  236. }
  237. // add size of the new attachment
  238. $multip = $filetype == 'message/rfc822' ? 1 : 1.33;
  239. $size += $filesize * $multip;
  240. if ($size > $limit) {
  241. $limit = $RCMAIL->show_bytes($limit);
  242. return $RCMAIL->gettext(array('name' => 'msgsizeerror', 'vars' => array('size' => $limit)));
  243. }
  244. }