mirror of
https://github.com/roundcube/roundcubemail.git
synced 2026-02-27 21:04:02 +01:00
278 lines
11 KiB
PHP
278 lines
11 KiB
PHP
<?php
|
|
|
|
/*
|
|
+-----------------------------------------------------------------------+
|
|
| This file is part of the Roundcube Webmail client |
|
|
| |
|
|
| Copyright (C) The Roundcube Dev Team |
|
|
| |
|
|
| Licensed under the GNU General Public License version 3 or |
|
|
| any later version with exceptions for skins & plugins. |
|
|
| See the README file for a full license statement. |
|
|
| |
|
|
| PURPOSE: |
|
|
| Attachment uploads handler for the compose form |
|
|
+-----------------------------------------------------------------------+
|
|
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
|
+-----------------------------------------------------------------------+
|
|
*/
|
|
|
|
class rcmail_action_mail_attachment_upload extends rcmail_action_mail_index
|
|
{
|
|
// only process ajax requests
|
|
protected static $mode = self::MODE_AJAX;
|
|
|
|
protected static $SESSION_KEY;
|
|
protected static $COMPOSE;
|
|
protected static $COMPOSE_ID;
|
|
protected static $file_id;
|
|
|
|
/**
|
|
* Request handler.
|
|
*
|
|
* @param array $args Arguments from the previous step(s)
|
|
*/
|
|
#[Override]
|
|
public function run($args = [])
|
|
{
|
|
$rcmail = rcmail::get_instance();
|
|
|
|
self::init();
|
|
|
|
// clear all stored output properties (like scripts and env vars)
|
|
$rcmail->output->reset();
|
|
|
|
$uploadid = rcube_utils::get_input_string('_uploadid', rcube_utils::INPUT_GPC);
|
|
$uri = rcube_utils::get_input_string('_uri', rcube_utils::INPUT_POST);
|
|
|
|
// handle dropping a reference to an attachment part of some message
|
|
if ($uri) {
|
|
$attachment = null;
|
|
|
|
$url = parse_url($uri);
|
|
|
|
if (!empty($url['query'])) {
|
|
parse_str($url['query'], $params);
|
|
}
|
|
|
|
if (
|
|
!empty($params) && isset($params['_mbox']) && strlen($params['_mbox'])
|
|
&& !empty($params['_uid']) && !empty($params['_part'])
|
|
) {
|
|
// @TODO: at some point we might support drag-n-drop between
|
|
// two different accounts on the same server, for now make sure
|
|
// this is the same server and the same user
|
|
[$host, $port] = rcube_utils::explode(':', $_SERVER['HTTP_HOST']);
|
|
|
|
if (
|
|
$host == $url['host']
|
|
&& $port == ($url['port'] ?? null)
|
|
&& $rcmail->get_user_name() == rawurldecode($url['user'])
|
|
) {
|
|
$message = new rcube_message($params['_uid'], $params['_mbox']);
|
|
|
|
if (!empty($message->headers)) {
|
|
$attachment = rcmail_action_mail_compose::save_attachment($message, $params['_part'], self::$COMPOSE_ID);
|
|
}
|
|
}
|
|
}
|
|
|
|
$plugin = $rcmail->plugins->exec_hook('attachment_from_uri', [
|
|
'attachment' => $attachment,
|
|
'uri' => $uri,
|
|
'compose_id' => self::$COMPOSE_ID,
|
|
]);
|
|
|
|
if ($plugin['attachment']) {
|
|
self::attachment_success($plugin['attachment'], $uploadid);
|
|
} else {
|
|
$rcmail->output->command('display_message', $rcmail->gettext('filelinkerror'), 'error');
|
|
$rcmail->output->command('remove_from_attachment_list', $uploadid);
|
|
}
|
|
|
|
$rcmail->output->send();
|
|
}
|
|
|
|
// handle file(s) upload
|
|
if (isset($_FILES['_attachments']['tmp_name']) && is_array($_FILES['_attachments']['tmp_name'])) {
|
|
$errors = [];
|
|
|
|
foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
|
|
// Process uploaded attachment if there is no error
|
|
$err = $_FILES['_attachments']['error'][$i];
|
|
$inserted = false;
|
|
$attachment = null;
|
|
|
|
if (!$err) {
|
|
$filename = $_FILES['_attachments']['name'][$i];
|
|
$filesize = $_FILES['_attachments']['size'][$i];
|
|
$filetype = rcube_mime::file_content_type($filepath, $filename, $_FILES['_attachments']['type'][$i]);
|
|
|
|
if ($err = self::check_message_size($filesize, $filetype)) {
|
|
if (!in_array($err, $errors)) {
|
|
$rcmail->output->command('display_message', $err, 'error');
|
|
$rcmail->output->command('remove_from_attachment_list', $uploadid);
|
|
$errors[] = $err;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
$attachment = [
|
|
'path' => $filepath,
|
|
'name' => $filename,
|
|
'size' => $filesize,
|
|
'mimetype' => $filetype,
|
|
'group' => self::$COMPOSE_ID,
|
|
];
|
|
|
|
$inserted = $rcmail->insert_uploaded_file($attachment);
|
|
}
|
|
|
|
if (!$err && $inserted) {
|
|
self::attachment_success($attachment, $uploadid);
|
|
} else { // upload failed
|
|
if ($err == \UPLOAD_ERR_INI_SIZE || $err == \UPLOAD_ERR_FORM_SIZE) {
|
|
$size = self::show_bytes(rcube_utils::max_upload_size());
|
|
$msg = $rcmail->gettext(['name' => 'filesizeerror', 'vars' => ['size' => $size]]);
|
|
} elseif (!empty($attachment['error'])) {
|
|
$msg = $attachment['error'];
|
|
} else {
|
|
$msg = $rcmail->gettext('fileuploaderror');
|
|
}
|
|
|
|
if (!empty($attachment['error']) || $err != \UPLOAD_ERR_NO_FILE) {
|
|
if (!in_array($msg, $errors)) {
|
|
$rcmail->output->command('display_message', $msg, 'error');
|
|
$rcmail->output->command('remove_from_attachment_list', $uploadid);
|
|
$errors[] = $msg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} elseif (self::upload_failure()) {
|
|
$rcmail->output->command('remove_from_attachment_list', $uploadid);
|
|
}
|
|
|
|
// send html page with JS calls as response
|
|
$rcmail->output->command('auto_save_start', false);
|
|
$rcmail->output->send('iframe');
|
|
}
|
|
|
|
public static function init()
|
|
{
|
|
self::$COMPOSE_ID = rcube_utils::get_input_string('_id', rcube_utils::INPUT_GPC);
|
|
self::$COMPOSE = null;
|
|
self::$SESSION_KEY = 'compose_data_' . self::$COMPOSE_ID;
|
|
|
|
if (self::$COMPOSE_ID && !empty($_SESSION[self::$SESSION_KEY])) {
|
|
self::$COMPOSE = &$_SESSION[self::$SESSION_KEY];
|
|
}
|
|
|
|
if (!self::$COMPOSE) {
|
|
exit('Invalid session var!');
|
|
}
|
|
|
|
self::$file_id = rcube_utils::get_input_string('_file', rcube_utils::INPUT_GPC);
|
|
self::$file_id = preg_replace('/^rcmfile/', '', self::$file_id) ?: 'unknown';
|
|
}
|
|
|
|
public static function attachment_success($attachment, $uploadid)
|
|
{
|
|
$rcmail = rcmail::get_instance();
|
|
$id = $attachment['id'];
|
|
|
|
if (!empty(self::$COMPOSE['deleteicon']) && is_file(self::$COMPOSE['deleteicon'])) {
|
|
$button = html::img([
|
|
'src' => self::$COMPOSE['deleteicon'],
|
|
'alt' => $rcmail->gettext('delete'),
|
|
]);
|
|
} elseif (!empty(self::$COMPOSE['textbuttons'])) {
|
|
$button = rcube::Q($rcmail->gettext('delete'));
|
|
} else {
|
|
$button = '';
|
|
}
|
|
|
|
$link_content = sprintf(
|
|
'<span class="attachment-name">%s</span><span class="attachment-size">(%s)</span>',
|
|
rcube::Q($attachment['name']), self::show_bytes($attachment['size'])
|
|
);
|
|
|
|
$content_link = html::a([
|
|
'href' => '#load',
|
|
'class' => 'filename',
|
|
'onclick' => sprintf(
|
|
"return %s.command('load-attachment','rcmfile%s', this, event)",
|
|
rcmail_output::JS_OBJECT_NAME,
|
|
$id
|
|
),
|
|
], $link_content);
|
|
|
|
$delete_link = html::a([
|
|
'href' => '#delete',
|
|
'onclick' => sprintf(
|
|
"return %s.command('remove-attachment','rcmfile%s', this, event)",
|
|
rcmail_output::JS_OBJECT_NAME,
|
|
$id
|
|
),
|
|
'title' => $rcmail->gettext('delete'),
|
|
'class' => 'delete',
|
|
'aria-label' => $rcmail->gettext('delete') . ' ' . $attachment['name'],
|
|
], $button);
|
|
|
|
if (!empty(self::$COMPOSE['icon_pos']) && self::$COMPOSE['icon_pos'] == 'left') {
|
|
$content = $delete_link . $content_link;
|
|
} else {
|
|
$content = $content_link . $delete_link;
|
|
}
|
|
|
|
$rcmail->output->command('add2attachment_list', "rcmfile{$id}", [
|
|
'html' => $content,
|
|
'name' => $attachment['name'],
|
|
'mimetype' => $attachment['mimetype'],
|
|
'classname' => rcube_utils::file2class($attachment['mimetype'], $attachment['name']),
|
|
'complete' => true,
|
|
],
|
|
$uploadid
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Checks if the attached file will fit in message size limit.
|
|
* Calculates size of all attachments and compares with the limit.
|
|
*
|
|
* @param int $filesize File size
|
|
* @param string $filetype File mimetype
|
|
*
|
|
* @return ?string Error message if the limit is exceeded
|
|
*/
|
|
public static function check_message_size($filesize, $filetype)
|
|
{
|
|
$rcmail = rcmail::get_instance();
|
|
$limit = parse_bytes($rcmail->config->get('max_message_size'));
|
|
$size = 10 * 1024; // size of message body
|
|
|
|
if (!$limit) {
|
|
return null;
|
|
}
|
|
|
|
// add size of already attached files
|
|
foreach ($rcmail->list_uploaded_files(self::$COMPOSE_ID) as $att) {
|
|
// All attachments are base64-encoded except message/rfc822
|
|
$multip = $att['mimetype'] == 'message/rfc822' ? 1 : 1.33;
|
|
$size += $att['size'] * $multip;
|
|
}
|
|
|
|
// add size of the new attachment
|
|
$multip = $filetype == 'message/rfc822' ? 1 : 1.33;
|
|
$size += $filesize * $multip;
|
|
|
|
if ($size > $limit) {
|
|
$limit = self::show_bytes($limit);
|
|
return $rcmail->gettext(['name' => 'msgsizeerror', 'vars' => ['size' => $limit]]);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|