comparison plugins/redundant_attachments/redundant_attachments.php @ 0:1e000243b222

vanilla 1.3.3 distro, I hope
author Charlie Root
date Thu, 04 Jan 2018 15:50:29 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1e000243b222
1 <?php
2
3 /**
4 * Redundant attachments
5 *
6 * This plugin provides a redundant storage for temporary uploaded
7 * attachment files. They are stored in both the database backend
8 * as well as on the local file system.
9 *
10 * It provides also memcache store as a fallback (see config file).
11 *
12 * This plugin relies on the core filesystem_attachments plugin
13 * and combines it with the functionality of the database_attachments plugin.
14 *
15 * @author Thomas Bruederli <roundcube@gmail.com>
16 * @author Aleksander Machniak <machniak@kolabsys.com>
17 *
18 * Copyright (C) 2011, The Roundcube Dev Team
19 * Copyright (C) 2011, Kolab Systems AG
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License version 2
23 * as published by the Free Software Foundation.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License along
31 * with this program; if not, write to the Free Software Foundation, Inc.,
32 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33 */
34
35 if (class_exists('filesystem_attachments', false) && !defined('TESTS_DIR')) {
36 die("Configuration issue. There can be only one enabled plugin for attachments handling");
37 }
38
39 require_once(RCUBE_PLUGINS_DIR . 'filesystem_attachments/filesystem_attachments.php');
40
41 class redundant_attachments extends filesystem_attachments
42 {
43 // A prefix for the cache key used in the session and in the key field of the cache table
44 const PREFIX = "ATTACH";
45
46 // rcube_cache instance for SQL DB
47 private $cache;
48
49 // rcube_cache instance for memcache
50 private $mem_cache;
51
52 private $loaded;
53
54
55 /**
56 * Loads plugin configuration and initializes cache object(s)
57 */
58 private function _load_drivers()
59 {
60 if ($this->loaded) {
61 return;
62 }
63
64 $rcmail = rcube::get_instance();
65
66 // load configuration
67 $this->load_config();
68
69 $ttl = 12 * 60 * 60; // 12 hours
70 $ttl = $rcmail->config->get('redundant_attachments_cache_ttl', $ttl);
71 $prefix = self::PREFIX;
72
73 if ($id = session_id()) {
74 $prefix .= $id;
75 }
76
77 // Init SQL cache (disable cache data serialization)
78 $this->cache = $rcmail->get_cache($prefix, 'db', $ttl, false);
79
80 // Init memcache (fallback) cache
81 if ($rcmail->config->get('redundant_attachments_memcache')) {
82 $this->mem_cache = $rcmail->get_cache($prefix, 'memcache', $ttl, false);
83 }
84
85 $this->loaded = true;
86 }
87
88 /**
89 * Helper method to generate a unique key for the given attachment file
90 */
91 private function _key($args)
92 {
93 $uname = $args['path'] ?: $args['name'];
94 return $args['group'] . md5(time() . $uname . $_SESSION['user_id']);
95 }
96
97 /**
98 * Save a newly uploaded attachment
99 */
100 function upload($args)
101 {
102 $args = parent::upload($args);
103
104 $this->_load_drivers();
105
106 $key = $this->_key($args);
107 $data = base64_encode(file_get_contents($args['path']));
108
109 $status = $this->cache->write($key, $data);
110
111 if (!$status && $this->mem_cache) {
112 $status = $this->mem_cache->write($key, $data);
113 }
114
115 if ($status) {
116 $args['id'] = $key;
117 $args['status'] = true;
118 }
119
120 return $args;
121 }
122
123 /**
124 * Save an attachment from a non-upload source (draft or forward)
125 */
126 function save($args)
127 {
128 $args = parent::save($args);
129
130 $this->_load_drivers();
131
132 $data = $args['path'] ? file_get_contents($args['path']) : $args['data'];
133
134 $args['data'] = null;
135
136 $key = $this->_key($args);
137 $data = base64_encode($data);
138
139 $status = $this->cache->write($key, $data);
140
141 if (!$status && $this->mem_cache) {
142 $status = $this->mem_cache->write($key, $data);
143 }
144
145 if ($status) {
146 $args['id'] = $key;
147 $args['status'] = true;
148 }
149
150 return $args;
151 }
152
153 /**
154 * Remove an attachment from storage
155 * This is triggered by the remove attachment button on the compose screen
156 */
157 function remove($args)
158 {
159 parent::remove($args);
160
161 $this->_load_drivers();
162
163 $status = $this->cache->remove($args['id']);
164
165 if (!$status && $this->mem_cache) {
166 $status = $this->cache->remove($args['id']);
167 }
168
169 // we cannot trust the result of any of the methods above
170 // assume true, attachments will be removed on cleanup
171 $args['status'] = true;
172
173 return $args;
174 }
175
176 /**
177 * When composing an html message, image attachments may be shown
178 * For this plugin, $this->get() will check the file and
179 * return it's contents
180 */
181 function display($args)
182 {
183 return $this->get($args);
184 }
185
186 /**
187 * When displaying or sending the attachment the file contents are fetched
188 * using this method. This is also called by the attachment_display hook.
189 */
190 function get($args)
191 {
192 // attempt to get file from local file system
193 $args = parent::get($args);
194
195 if ($args['path'] && ($args['status'] = file_exists($args['path'])))
196 return $args;
197
198 $this->_load_drivers();
199
200 // fetch from database if not found on FS
201 $data = $this->cache->read($args['id']);
202
203 // fetch from memcache if not found on FS and DB
204 if (($data === false || $data === null) && $this->mem_cache) {
205 $data = $this->mem_cache->read($args['id']);
206 }
207
208 if ($data) {
209 $args['data'] = base64_decode($data);
210 $args['status'] = true;
211 }
212
213 return $args;
214 }
215
216 /**
217 * Delete all temp files associated with this user
218 */
219 function cleanup($args)
220 {
221 $this->_load_drivers();
222
223 if ($this->cache) {
224 $this->cache->remove($args['group'], true);
225 }
226
227 if ($this->mem_cache) {
228 $this->mem_cache->remove($args['group'], true);
229 }
230
231 parent::cleanup($args);
232
233 $args['status'] = true;
234
235 return $args;
236 }
237 }