Aleksander Machniak
2015-05-19 7fdc341a228abbbbf5139d8bf0386bd3462f053a
commit | author | age
48e9c1 1 <?php
T 2 /**
3  * Redundant attachments
4  *
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.
8  *
9  * It provides also memcache store as a fallback (see config file).
10  *
11  * This plugin relies on the core filesystem_attachments plugin
12  * and combines it with the functionality of the database_attachments plugin.
13  *
14  * @author Thomas Bruederli <roundcube@gmail.com>
15  * @author Aleksander Machniak <machniak@kolabsys.com>
16  *
17  * Copyright (C) 2011, The Roundcube Dev Team
18  * Copyright (C) 2011, Kolab Systems AG
19  *
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.
23  *
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.
28  *
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.
32  */
33
e2e2e8 34 require_once(RCUBE_PLUGINS_DIR . 'filesystem_attachments/filesystem_attachments.php');
48e9c1 35
T 36 class redundant_attachments extends filesystem_attachments
37 {
38     // A prefix for the cache key used in the session and in the key field of the cache table
39     private $prefix = "ATTACH";
40
41     // rcube_cache instance for SQL DB
42     private $cache;
43
44     // rcube_cache instance for memcache
45     private $mem_cache;
46
47     private $loaded;
48
49     /**
50      * Default constructor
51      */
52     function init()
53     {
54         parent::init();
55     }
56
57     /**
58      * Loads plugin configuration and initializes cache object(s)
59      */
60     private function _load_drivers()
61     {
62         if ($this->loaded) {
63             return;
64         }
65
66         $rcmail = rcmail::get_instance();
67
68         // load configuration
69         $this->load_config();
70
df9d00 71         $ttl = 12 * 60 * 60; // 12 hours
AM 72         $ttl = $rcmail->config->get('redundant_attachments_cache_ttl', $ttl);
73
48e9c1 74         // Init SQL cache (disable cache data serialization)
df9d00 75         $this->cache = $rcmail->get_cache($this->prefix, 'db', $ttl, false);
48e9c1 76
T 77         // Init memcache (fallback) cache
78         if ($rcmail->config->get('redundant_attachments_memcache')) {
79             $this->mem_cache = $rcmail->get_cache($this->prefix, 'memcache', $ttl, false);
80         }
81
82         $this->loaded = true;
83     }
84
85     /**
86      * Helper method to generate a unique key for the given attachment file
87      */
88     private function _key($args)
89     {
90         $uname = $args['path'] ? $args['path'] : $args['name'];
91         return $args['group'] . md5(mktime() . $uname . $_SESSION['user_id']);
92     }
93
94     /**
95      * Save a newly uploaded attachment
96      */
97     function upload($args)
98     {
99         $args = parent::upload($args);
100
101         $this->_load_drivers();
102
103         $key  = $this->_key($args);
104         $data = base64_encode(file_get_contents($args['path']));
105
106         $status = $this->cache->write($key, $data);
107
108         if (!$status && $this->mem_cache) {
109             $status = $this->mem_cache->write($key, $data);
110         }
111
112         if ($status) {
113             $args['id'] = $key;
114             $args['status'] = true;
115         }
116
117         return $args;
118     }
119
120     /**
121      * Save an attachment from a non-upload source (draft or forward)
122      */
123     function save($args)
124     {
125         $args = parent::save($args);
126
127         $this->_load_drivers();
128
cccf8a 129         $data = $args['path'] ? file_get_contents($args['path']) : $args['data'];
AM 130
7fdc34 131         $args['data'] = null;
48e9c1 132
T 133         $key  = $this->_key($args);
cccf8a 134         $data = base64_encode($data);
48e9c1 135
T 136         $status = $this->cache->write($key, $data);
137
138         if (!$status && $this->mem_cache) {
139             $status = $this->mem_cache->write($key, $data);
140         }
141
142         if ($status) {
143             $args['id'] = $key;
144             $args['status'] = true;
145         }
146
147         return $args;
148     }
149
150     /**
151      * Remove an attachment from storage
152      * This is triggered by the remove attachment button on the compose screen
153      */
154     function remove($args)
155     {
156         parent::remove($args);
157
158         $this->_load_drivers();
159
160         $status = $this->cache->remove($args['id']);
161
162         if (!$status && $this->mem_cache) {
163             $status = $this->cache->remove($args['id']);
164         }
165
166         // we cannot trust the result of any of the methods above
167         // assume true, attachments will be removed on cleanup
168         $args['status'] = true;
169
170         return $args;
171     }
172
173     /**
174      * When composing an html message, image attachments may be shown
175      * For this plugin, $this->get() will check the file and
176      * return it's contents
177      */
178     function display($args)
179     {
180         return $this->get($args);
181     }
182
183     /**
184      * When displaying or sending the attachment the file contents are fetched
185      * using this method. This is also called by the attachment_display hook.
186      */
187     function get($args)
188     {
189         // attempt to get file from local file system
190         $args = parent::get($args);
191
192         if ($args['path'] && ($args['status'] = file_exists($args['path'])))
193           return $args;
194
195         $this->_load_drivers();
196
197         // fetch from database if not found on FS
198         $data = $this->cache->read($args['id']);
199
200         // fetch from memcache if not found on FS and DB
201         if (($data === false || $data === null) && $this->mem_cache) {
202             $data = $this->mem_cache->read($args['id']);
203         }
204
205         if ($data) {
206             $args['data'] = base64_decode($data);
207             $args['status'] = true;
208         }
209
210         return $args;
211     }
212
213     /**
214      * Delete all temp files associated with this user
215      */
216     function cleanup($args)
217     {
218         $this->_load_drivers();
219
220         if ($this->cache) {
221             $this->cache->remove($args['group'], true);
222         }
223
224         if ($this->mem_cache) {
225             $this->mem_cache->remove($args['group'], true);
226         }
227
228         parent::cleanup($args);
229
230         $args['status'] = true;
231
232         return $args;
233     }
234 }