1 <?php 2 /** 3 * Copyright 2013-2017 Horde LLC (http://www.horde.org/) 4 * 5 * See the enclosed file LICENSE for license information (LGPL). If you 6 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 7 * 8 * @category Horde 9 * @copyright 2013-2017 Horde LLC 10 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 11 * @package Imap_Client 12 */ 13 14 /** 15 * A MongoDB database implementation for caching IMAP/POP data. 16 * 17 * Requires the Horde_Mongo class. 18 * 19 * @author Michael Slusarz <slusarz@horde.org> 20 * @category Horde 21 * @copyright 2013-2017 Horde LLC 22 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 23 * @package Imap_Client 24 */ 25 class Horde_Imap_Client_Cache_Backend_Mongo 26 extends Horde_Imap_Client_Cache_Backend 27 implements Horde_Mongo_Collection_Index 28 { 29 /** Mongo collection names. */ 30 const BASE = 'horde_imap_client_cache_data'; 31 const MD = 'horde_imap_client_cache_metadata'; 32 const MSG = 'horde_imap_client_cache_message'; 33 34 /** Mongo field names: BASE collection. */ 35 const BASE_HOSTSPEC = 'hostspec'; 36 const BASE_MAILBOX = 'mailbox'; 37 const BASE_MODIFIED = 'modified'; 38 const BASE_PORT = 'port'; 39 const BASE_UID = 'data'; 40 const BASE_USERNAME = 'username'; 41 42 /** Mongo field names: MD collection. */ 43 const MD_DATA = 'data'; 44 const MD_FIELD = 'field'; 45 const MD_UID = 'uid'; 46 47 /** Mongo field names: MSG collection. */ 48 const MSG_DATA = 'data'; 49 const MSG_MSGUID = 'msguid'; 50 const MSG_UID = 'uid'; 51 52 /** 53 * The MongoDB object for the cache data. 54 * 55 * @var MongoDB 56 */ 57 protected $_db; 58 59 /** 60 * The list of indices. 61 * 62 * @var array 63 */ 64 protected $_indices = array( 65 self::BASE => array( 66 'base_index_1' => array( 67 self::BASE_HOSTSPEC => 1, 68 self::BASE_MAILBOX => 1, 69 self::BASE_PORT => 1, 70 self::BASE_USERNAME => 1, 71 ) 72 ), 73 self::MSG => array( 74 'msg_index_1' => array( 75 self::MSG_MSGUID => 1, 76 self::MSG_UID => 1 77 ) 78 ) 79 ); 80 81 /** 82 * Constructor. 83 * 84 * @param array $params Configuration parameters: 85 * <pre> 86 * - REQUIRED parameters: 87 * - mongo_db: (Horde_Mongo_Client) A MongoDB client object. 88 * </pre> 89 */ 90 public function __construct(array $params = array()) 91 { 92 if (!isset($params['mongo_db'])) { 93 throw new InvalidArgumentException('Missing mongo_db parameter.'); 94 } 95 96 parent::__construct($params); 97 } 98 99 /** 100 */ 101 protected function _initOb() 102 { 103 $this->_db = $this->_params['mongo_db']->selectDB(null); 104 } 105 106 /** 107 */ 108 public function get($mailbox, $uids, $fields, $uidvalid) 109 { 110 $this->getMetaData($mailbox, $uidvalid, array('uidvalid')); 111 112 if (!($uid = $this->_getUid($mailbox))) { 113 return array(); 114 } 115 116 $out = array(); 117 $query = array( 118 self::MSG_MSGUID => array('$in' => array_map('strval', $uids)), 119 self::MSG_UID => $uid 120 ); 121 122 try { 123 $cursor = $this->_db->selectCollection(self::MSG)->find( 124 $query, 125 array(self::MSG_DATA => true, self::MSG_MSGUID => true) 126 ); 127 foreach ($cursor as $val) { 128 try { 129 $out[$val[self::MSG_MSGUID]] = $this->_value($val[self::MSG_DATA]); 130 } catch (Exception $e) {} 131 } 132 } catch (MongoException $e) {} 133 134 return $out; 135 } 136 137 /** 138 */ 139 public function getCachedUids($mailbox, $uidvalid) 140 { 141 $this->getMetaData($mailbox, $uidvalid, array('uidvalid')); 142 143 if (!($uid = $this->_getUid($mailbox))) { 144 return array(); 145 } 146 147 $out = array(); 148 $query = array( 149 self::MSG_UID => $uid 150 ); 151 152 try { 153 $cursor = $this->_db->selectCollection(self::MSG)->find( 154 $query, array(self::MSG_MSGUID => true) 155 ); 156 foreach ($cursor as $val) { 157 $out[] = $val[self::MSG_MSGUID]; 158 } 159 } catch (MongoException $e) {} 160 161 return $out; 162 } 163 164 /** 165 */ 166 public function set($mailbox, $data, $uidvalid) 167 { 168 if ($uid = $this->_getUid($mailbox)) { 169 $res = $this->get($mailbox, array_keys($data), array(), $uidvalid); 170 } else { 171 $res = array(); 172 $uid = $this->_createUid($mailbox); 173 } 174 175 $coll = $this->_db->selectCollection(self::MSG); 176 177 foreach ($data as $key => $val) { 178 try { 179 if (isset($res[$key])) { 180 $coll->update(array( 181 self::MSG_MSGUID => strval($key), 182 self::MSG_UID => $uid 183 ), array( 184 self::MSG_DATA => $this->_value(array_merge($res[$key], $val)), 185 self::MSG_MSGUID => strval($key), 186 self::MSG_UID => $uid 187 )); 188 } else { 189 $doc = array( 190 self::MSG_DATA => $this->_value($val), 191 self::MSG_MSGUID => strval($key), 192 self::MSG_UID => $uid 193 ); 194 $coll->insert($doc); 195 } 196 } catch (MongoException $e) {} 197 } 198 199 /* Update modified time. */ 200 try { 201 $this->_db->selectCollection(self::BASE)->update( 202 array(self::BASE_UID => $uid), 203 array(self::BASE_MODIFIED => time()) 204 ); 205 } catch (MongoException $e) {} 206 207 /* Update uidvalidity. */ 208 $this->setMetaData($mailbox, array('uidvalid' => $uidvalid)); 209 } 210 211 /** 212 */ 213 public function getMetaData($mailbox, $uidvalid, $entries) 214 { 215 if (!($uid = $this->_getUid($mailbox))) { 216 return array(); 217 } 218 219 $out = array(); 220 $query = array( 221 self::MD_UID => $uid 222 ); 223 224 if (!empty($entries)) { 225 $entries[] = 'uidvalid'; 226 $query[self::MD_FIELD] = array( 227 '$in' => array_unique($entries) 228 ); 229 } 230 231 try { 232 $cursor = $this->_db->selectCollection(self::MD)->find( 233 $query, 234 array(self::MD_DATA => true, self::MD_FIELD => true) 235 ); 236 foreach ($cursor as $val) { 237 try { 238 $out[$val[self::MD_FIELD]] = $this->_value($val[self::MD_DATA]); 239 } catch (Exception $e) {} 240 } 241 242 if (is_null($uidvalid) || 243 !isset($out['uidvalid']) || 244 ($out['uidvalid'] == $uidvalid)) { 245 return $out; 246 } 247 248 $this->deleteMailbox($mailbox); 249 } catch (MongoException $e) {} 250 251 return array(); 252 } 253 254 /** 255 */ 256 public function setMetaData($mailbox, $data) 257 { 258 if (!($uid = $this->_getUid($mailbox))) { 259 $uid = $this->_createUid($mailbox); 260 } 261 262 $coll = $this->_db->selectCollection(self::MD); 263 264 foreach ($data as $key => $val) { 265 try { 266 $coll->update( 267 array( 268 self::MD_FIELD => $key, 269 self::MD_UID => $uid 270 ), 271 array( 272 self::MD_DATA => $this->_value($val), 273 self::MD_FIELD => $key, 274 self::MD_UID => $uid 275 ), 276 array('upsert' => true) 277 ); 278 } catch (MongoException $e) {} 279 } 280 } 281 282 /** 283 */ 284 public function deleteMsgs($mailbox, $uids) 285 { 286 if (!empty($uids) && ($uid = $this->_getUid($mailbox))) { 287 try { 288 $this->_db->selectCollection(self::MSG)->remove(array( 289 self::MSG_MSGUID => array( 290 '$in' => array_map('strval', $uids) 291 ), 292 self::MSG_UID => $uid 293 )); 294 } catch (MongoException $e) {} 295 } 296 } 297 298 /** 299 */ 300 public function deleteMailbox($mailbox) 301 { 302 if (!($uid = $this->_getUid($mailbox))) { 303 return; 304 } 305 306 foreach (array(self::BASE, self::MD, self::MSG) as $val) { 307 try { 308 $this->_db->selectCollection($val) 309 ->remove(array('uid' => $uid)); 310 } catch (MongoException $e) {} 311 } 312 } 313 314 /** 315 */ 316 public function clear($lifetime) 317 { 318 if (is_null($lifetime)) { 319 foreach (array(self::BASE, self::MD, self::MSG) as $val) { 320 $this->_db->selectCollection($val)->drop(); 321 } 322 return; 323 } 324 325 $query = array( 326 self::BASE_MODIFIED => array('$lt' => (time() - $lifetime)) 327 ); 328 $uids = array(); 329 330 try { 331 $cursor = $this->_db->selectCollection(self::BASE)->find($query); 332 foreach ($cursor as $val) { 333 $uids[] = strval($val['_id']); 334 } 335 } catch (MongoException $e) {} 336 337 if (empty($uids)) { 338 return; 339 } 340 341 foreach (array(self::BASE, self::MD, self::MSG) as $val) { 342 try { 343 $this->_db->selectCollection($val) 344 ->remove(array('uid' => array('$in' => $uids))); 345 } catch (MongoException $e) {} 346 } 347 } 348 349 /** 350 * Return the UID for a mailbox/user/server combo. 351 * 352 * @param string $mailbox Mailbox name. 353 * 354 * @return string UID from base table. 355 */ 356 protected function _getUid($mailbox) 357 { 358 $query = array( 359 self::BASE_HOSTSPEC => $this->_params['hostspec'], 360 self::BASE_MAILBOX => $mailbox, 361 self::BASE_PORT => $this->_params['port'], 362 self::BASE_USERNAME => $this->_params['username'] 363 ); 364 365 try { 366 if ($result = $this->_db->selectCollection(self::BASE)->findOne($query)) { 367 return strval($result['_id']); 368 } 369 } catch (MongoException $e) {} 370 371 return null; 372 } 373 374 /** 375 * Create and return the UID for a mailbox/user/server combo. 376 * 377 * @param string $mailbox Mailbox name. 378 * 379 * @return string UID from base table. 380 */ 381 protected function _createUid($mailbox) 382 { 383 $doc = array( 384 self::BASE_HOSTSPEC => $this->_params['hostspec'], 385 self::BASE_MAILBOX => $mailbox, 386 self::BASE_PORT => $this->_params['port'], 387 self::BASE_USERNAME => $this->_params['username'] 388 ); 389 $this->_db->selectCollection(self::BASE)->insert($doc); 390 391 return $this->_getUid($mailbox); 392 } 393 394 /** 395 * Convert data from/to storage format. 396 * 397 * @param mixed|MongoBinData $data The data object. 398 * 399 * @return mixed|MongoBinData The converted data. 400 */ 401 protected function _value($data) 402 { 403 static $compress; 404 405 if (!isset($compress)) { 406 $compress = new Horde_Compress_Fast(); 407 } 408 409 return ($data instanceof MongoBinData) 410 ? @unserialize($compress->decompress($data->bin)) 411 : new MongoBinData( 412 $compress->compress(serialize($data)), MongoBinData::BYTE_ARRAY 413 ); 414 } 415 416 /* Horde_Mongo_Collection_Index methods. */ 417 418 /** 419 */ 420 public function checkMongoIndices() 421 { 422 foreach ($this->_indices as $key => $val) { 423 if (!$this->_params['mongo_db']->checkIndices($key, $val)) { 424 return false; 425 } 426 } 427 428 return true; 429 } 430 431 /** 432 */ 433 public function createMongoIndices() 434 { 435 foreach ($this->_indices as $key => $val) { 436 $this->_params['mongo_db']->createIndices($key, $val); 437 } 438 } 439 440 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body