]> git.donarmstrong.com Git - roundcube.git/blob - plugins/redundant_attachments/redundant_attachments.php
Imported Upstream version 0.6+dfsg
[roundcube.git] / plugins / redundant_attachments / redundant_attachments.php
1 <?php
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
34 require_once(INSTALL_PATH . 'plugins/filesystem_attachments/filesystem_attachments.php');
35
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
71         // Init SQL cache (disable cache data serialization)
72         $this->cache = $rcmail->get_cache($this->prefix, 'db', 0, false);
73
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);
79         }
80
81         $this->loaded = true;
82     }
83
84     /**
85      * Helper method to generate a unique key for the given attachment file
86      */
87     private function _key($args)
88     {
89         $uname = $args['path'] ? $args['path'] : $args['name'];
90         return $args['group'] . md5(mktime() . $uname . $_SESSION['user_id']);
91     }
92
93     /**
94      * Save a newly uploaded attachment
95      */
96     function upload($args)
97     {
98         $args = parent::upload($args);
99
100         $this->_load_drivers();
101
102         $key  = $this->_key($args);
103         $data = base64_encode(file_get_contents($args['path']));
104
105         $status = $this->cache->write($key, $data);
106
107         if (!$status && $this->mem_cache) {
108             $status = $this->mem_cache->write($key, $data);
109         }
110
111         if ($status) {
112             $args['id'] = $key;
113             $args['status'] = true;
114         }
115
116         return $args;
117     }
118
119     /**
120      * Save an attachment from a non-upload source (draft or forward)
121      */
122     function save($args)
123     {
124         $args = parent::save($args);
125
126         $this->_load_drivers();
127
128         if ($args['path'])
129           $args['data'] = file_get_contents($args['path']);
130
131         $key  = $this->_key($args);
132         $data = base64_encode($args['data']);
133
134         $status = $this->cache->write($key, $data);
135
136         if (!$status && $this->mem_cache) {
137             $status = $this->mem_cache->write($key, $data);
138         }
139
140         if ($status) {
141             $args['id'] = $key;
142             $args['status'] = true;
143         }
144
145         return $args;
146     }
147
148     /**
149      * Remove an attachment from storage
150      * This is triggered by the remove attachment button on the compose screen
151      */
152     function remove($args)
153     {
154         parent::remove($args);
155
156         $this->_load_drivers();
157
158         $status = $this->cache->remove($args['id']);
159
160         if (!$status && $this->mem_cache) {
161             $status = $this->cache->remove($args['id']);
162         }
163
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;
167
168         return $args;
169     }
170
171     /**
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
175      */
176     function display($args)
177     {
178         return $this->get($args);
179     }
180
181     /**
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.
184      */
185     function get($args)
186     {
187         // attempt to get file from local file system
188         $args = parent::get($args);
189
190         if ($args['path'] && ($args['status'] = file_exists($args['path'])))
191           return $args;
192
193         $this->_load_drivers();
194
195         // fetch from database if not found on FS
196         $data = $this->cache->read($args['id']);
197
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']);
201         }
202
203         if ($data) {
204             $args['data'] = base64_decode($data);
205             $args['status'] = true;
206         }
207
208         return $args;
209     }
210
211     /**
212      * Delete all temp files associated with this user
213      */
214     function cleanup($args)
215     {
216         $this->_load_drivers();
217
218         if ($this->cache) {
219             $this->cache->remove($args['group'], true);
220         }
221
222         if ($this->mem_cache) {
223             $this->mem_cache->remove($args['group'], true);
224         }
225
226         parent::cleanup($args);
227
228         $args['status'] = true;
229
230         return $args;
231     }
232 }