diff options
Diffstat (limited to 'program/lib/Roundcube/rcube_uploads.php')
-rw-r--r-- | program/lib/Roundcube/rcube_uploads.php | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/program/lib/Roundcube/rcube_uploads.php b/program/lib/Roundcube/rcube_uploads.php new file mode 100644 index 000000000..4f9133280 --- /dev/null +++ b/program/lib/Roundcube/rcube_uploads.php @@ -0,0 +1,285 @@ +<?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: | + | This class provides API for access to uploaded files information. | + +-----------------------------------------------------------------------+ + | Author: Aleksander Machniak <alec@alec.pl> | + +-----------------------------------------------------------------------+ +*/ + +/** + * A trait providing access to metadata of files uploaded in a session. + * + * @package Framework + * @subpackage Core + */ +trait rcube_uploads +{ + /** + * Get an uploaded file information. + * + * @param int $id Upload ID + * + * @return array|null Hash array with file upload metadata, NULL if not found + */ + public function get_uploaded_file($id) + { + if (!($session_id = $this->get_session_id())) { + return null; + } + + $sql_result = $this->db->query( + "SELECT * FROM " . $this->db->table_name('uploads', true) + . " WHERE `session_id` = ? AND `upload_id` = ?", + $session_id, $id + ); + + if ($sql_arr = $this->db->fetch_assoc($sql_result)) { + $sql_arr['id'] = $sql_arr['upload_id']; + $sql_arr += json_decode($sql_arr['metadata'], true); + unset($sql_arr['upload_id'], $sql_arr['session_id'], $sql_arr['metadata']); + + return $sql_arr; + } + } + + /** + * Return a list of all uploaded files (in the current session). + * + * @param string $group The upload context group + * + * @return array List of uploaded files + */ + public function list_uploaded_files($group) + { + if (!($session_id = $this->get_session_id())) { + return []; + } + + $sql_result = $this->db->query( + "SELECT * FROM " . $this->db->table_name('uploads', true) + . " WHERE `session_id` = ? AND `group` = ?" + . " ORDER BY `created`", + $session_id, $group + ); + + $result = []; + + while ($sql_arr = $this->db->fetch_assoc($sql_result)) { + $sql_arr['id'] = $sql_arr['upload_id']; + $sql_arr += json_decode($sql_arr['metadata'], true); + unset($sql_arr['upload_id'], $sql_arr['session_id'], $sql_arr['metadata']); + $result[] = $sql_arr; + } + + return $result; + } + + /** + * Update a specific upload record. + * + * @param int $id Upload ID + * @param array $data Hash array with col->value pairs to update + * + * @return bool True if saved successfully, false if nothing changed + */ + public function update_uploaded_file($id, $data) + { + if (!($file = $this->get_uploaded_file($id))) { + return false; + } + + $metadata = $this->prepare_upload_metadata(array_merge($file, $data)); + + $sql = "UPDATE " . $this->db->table_name('uploads', true) + . " SET `metadata` = ?" + . " WHERE `upload_id` = ? AND `session_id` = ?"; + + $update = $this->db->query($sql, $metadata, $id, $this->get_session_id()); + + return $this->db->affected_rows($update) > 0; + } + + /** + * Create a new uploaded file record in the session. + * + * @param array $data Hash array with col->value pairs to save + * It must include: id, group, name, data (or path), size. + * It can include: mimetype, charset, content_id or any other properties. + * It will be modified by the attachment's handling plugin(s). + * @param string $hook Optional plugin API hook name (attachment_upload or attachment_save) + * + * @return bool True on success or False on error + */ + public function insert_uploaded_file(&$data, $hook = null) + { + if (!($session_id = $this->get_session_id())) { + return false; + } + + $data = $this->plugins->exec_hook($hook ?: 'attachment_upload', $data); + + if (empty($data['status']) || !empty($data['abort'])) { + return false; + } + + $metadata = $this->prepare_upload_metadata($data); + + $sql = "INSERT INTO " . $this->db->table_name('uploads', true) + . " (`created`, `session_id`, `upload_id`, `group`, `metadata`)" + . " VALUES (" . $this->db->now() . ", ?, ?, ?, ?)"; + + $insert = $this->db->query($sql, $session_id, $data['id'], $data['group'] ?? null, $metadata); + + return $this->db->affected_rows($insert) > 0; + } + + /** + * Delete an upload record + * + * @param int $id Upload ID + * + * @return bool True if deleted successfully, False otherwise + */ + public function delete_uploaded_file($id) + { + if (!($session_id = $this->get_session_id())) { + return false; + } + + if ($attachment = $this->get_uploaded_file($id)) { + $attachment = $this->plugins->exec_hook('attachment_delete', $attachment); + } + + if (empty($attachment['status'])) { + return false; + } + + $this->db->query( + "DELETE FROM " . $this->db->table_name('uploads', true) + . " WHERE `session_id` = ? AND `upload_id` = ?", + $session_id, + $id + ); + + return $this->db->affected_rows() > 0; + } + + /** + * Delete all upload records (in the current session) by group name. + * + * @param string $group Group name + * + * @return bool True if deleted successfully, False otherwise + */ + public function delete_uploaded_files($group) + { + if (!($session_id = $this->get_session_id())) { + return false; + } + + $this->plugins->exec_hook('attachments_cleanup', ['group' => $group]); + + $this->db->query( + "DELETE FROM " . $this->db->table_name('uploads', true) + . " WHERE `session_id` = ? AND `group` = ?", + $session_id, + $group + ); + + return $this->db->affected_rows() > 0; + } + + /** + * Outputs uploaded file content (with image thumbnails support) + * + * @param array $file Uploaded file data + * @param bool $thumbnail Generate and return a thumbnail image + */ + public function display_uploaded_file($file, $thumbnail = false) + { + $file = $this->plugins->exec_hook('attachment_display', $file); + + if (!empty($file['status'])) { + if (empty($file['size'])) { + $file['size'] = !empty($file['data']) ? strlen($file['data']) : @filesize($file['path']); + } + + // generate image thumbnail for file browser in HTML editor + if ($thumbnail) { + $thumbnail_size = 80; + $mimetype = $file['mimetype']; + $file_ident = $file['id'] . ':' . $file['mimetype'] . ':' . $file['size']; + $thumb_name = 'thumb' . md5($file_ident . ':' . $this->user->ID . ':' . $thumbnail_size); + $cache_file = rcube_utils::temp_filename($thumb_name, false, false); + + // render thumbnail image if not done yet + if (!is_file($cache_file)) { + if (empty($file['path'])) { + $orig_name = $filename = $cache_file . '.tmp'; + file_put_contents($orig_name, $file['data']); + } + else { + $filename = $file['path']; + } + + $image = new rcube_image($filename); + if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) { + $mimetype = 'image/' . $imgtype; + + if (!empty($orig_name)) { + unlink($orig_name); + } + } + } + + if (is_file($cache_file)) { + // cache for 1h + $this->output->future_expire_header(3600); + header('Content-Type: ' . $mimetype); + header('Content-Length: ' . filesize($cache_file)); + + readfile($cache_file); + exit; + } + } + + header('Content-Type: ' . $file['mimetype']); + header('Content-Length: ' . $file['size']); + + if (isset($file['data']) && is_string($file['data'])) { + echo $file['data']; + } + else if (!empty($file['path'])) { + readfile($file['path']); + } + } + } + + /** + * Prepare upload metadata to store in DB + */ + protected function prepare_upload_metadata($data) + { + // Remove unwanted properties + $data = array_diff_key($data, array_fill_keys(['id', 'group', 'status', 'abort', 'error', 'data', 'created'], 1)); + + // Remove null values + $data = array_filter($data, function ($v) { return !is_null($v); }); + + // Convert to string + $data = json_encode($data, JSON_INVALID_UTF8_IGNORE); + + return $data; + } +} |