3 * Redundant attachments
5 * This plugin provides a redundant storage for temporary uploaded
6 * attachment files. They are stored in both the database backend
7 * as well as on the local file system.
9 * It provides also memcache store as a fallback (see config file).
11 * This plugin relies on the core filesystem_attachments plugin
12 * and combines it with the functionality of the database_attachments plugin.
14 * @author Thomas Bruederli <roundcube@gmail.com>
15 * @author Aleksander Machniak <machniak@kolabsys.com>
17 * Copyright (C) 2011, The Roundcube Dev Team
18 * Copyright (C) 2011, Kolab Systems AG
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License version 2
22 * as published by the Free Software Foundation.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License along
30 * with this program; if not, write to the Free Software Foundation, Inc.,
31 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34 require_once(INSTALL_PATH . 'plugins/filesystem_attachments/filesystem_attachments.php');
36 class redundant_attachments extends filesystem_attachments
38 // A prefix for the cache key used in the session and in the key field of the cache table
39 private $prefix = "ATTACH";
41 // rcube_cache instance for SQL DB
44 // rcube_cache instance for memcache
58 * Loads plugin configuration and initializes cache object(s)
60 private function _load_drivers()
66 $rcmail = rcmail::get_instance();
71 // Init SQL cache (disable cache data serialization)
72 $this->cache = $rcmail->get_cache($this->prefix, 'db', 0, false);
74 // Init memcache (fallback) cache
75 if ($rcmail->config->get('redundant_attachments_memcache')) {
76 $ttl = 12 * 60 * 60; // 12 hours
77 $ttl = (int) $rcmail->config->get('redundant_attachments_memcache_ttl', $ttl);
78 $this->mem_cache = $rcmail->get_cache($this->prefix, 'memcache', $ttl, false);
85 * Helper method to generate a unique key for the given attachment file
87 private function _key($args)
89 $uname = $args['path'] ? $args['path'] : $args['name'];
90 return $args['group'] . md5(mktime() . $uname . $_SESSION['user_id']);
94 * Save a newly uploaded attachment
96 function upload($args)
98 $args = parent::upload($args);
100 $this->_load_drivers();
102 $key = $this->_key($args);
103 $data = base64_encode(file_get_contents($args['path']));
105 $status = $this->cache->write($key, $data);
107 if (!$status && $this->mem_cache) {
108 $status = $this->mem_cache->write($key, $data);
113 $args['status'] = true;
120 * Save an attachment from a non-upload source (draft or forward)
124 $args = parent::save($args);
126 $this->_load_drivers();
129 $args['data'] = file_get_contents($args['path']);
131 $key = $this->_key($args);
132 $data = base64_encode($args['data']);
134 $status = $this->cache->write($key, $data);
136 if (!$status && $this->mem_cache) {
137 $status = $this->mem_cache->write($key, $data);
142 $args['status'] = true;
149 * Remove an attachment from storage
150 * This is triggered by the remove attachment button on the compose screen
152 function remove($args)
154 parent::remove($args);
156 $this->_load_drivers();
158 $status = $this->cache->remove($args['id']);
160 if (!$status && $this->mem_cache) {
161 $status = $this->cache->remove($args['id']);
164 // we cannot trust the result of any of the methods above
165 // assume true, attachments will be removed on cleanup
166 $args['status'] = true;
172 * When composing an html message, image attachments may be shown
173 * For this plugin, $this->get() will check the file and
174 * return it's contents
176 function display($args)
178 return $this->get($args);
182 * When displaying or sending the attachment the file contents are fetched
183 * using this method. This is also called by the attachment_display hook.
187 // attempt to get file from local file system
188 $args = parent::get($args);
190 if ($args['path'] && ($args['status'] = file_exists($args['path'])))
193 $this->_load_drivers();
195 // fetch from database if not found on FS
196 $data = $this->cache->read($args['id']);
198 // fetch from memcache if not found on FS and DB
199 if (($data === false || $data === null) && $this->mem_cache) {
200 $data = $this->mem_cache->read($args['id']);
204 $args['data'] = base64_decode($data);
205 $args['status'] = true;
212 * Delete all temp files associated with this user
214 function cleanup($args)
216 $this->_load_drivers();
219 $this->cache->remove($args['group'], true);
222 if ($this->mem_cache) {
223 $this->mem_cache->remove($args['group'], true);
226 parent::cleanup($args);
228 $args['status'] = true;