1<?php
2
3/**
4 +-----------------------------------------------------------------------+
5 | This file is part of the Roundcube Webmail client                     |
6 |                                                                       |
7 | Copyright (C) The Roundcube Dev Team                                  |
8 | Copyright (C) Kolab Systems AG                                        |
9 |                                                                       |
10 | Licensed under the GNU General Public License version 3 or            |
11 | any later version with exceptions for skins & plugins.                |
12 | See the README file for a full license statement.                     |
13 |                                                                       |
14 | PURPOSE:                                                              |
15 |   Mail Storage Engine                                                 |
16 +-----------------------------------------------------------------------+
17 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
18 | Author: Aleksander Machniak <alec@alec.pl>                            |
19 +-----------------------------------------------------------------------+
20*/
21
22/**
23 * Abstract class for accessing mail messages storage server
24 *
25 * @package    Framework
26 * @subpackage Storage
27 */
28abstract class rcube_storage
29{
30    /**
31     * Instance of connection object e.g. rcube_imap_generic
32     *
33     * @var mixed
34     */
35    public $conn;
36
37    /**
38     * List of supported special folder types
39     *
40     * @var array
41     */
42    public static $folder_types = ['drafts', 'sent', 'junk', 'trash'];
43
44    protected $folder          = 'INBOX';
45    protected $default_charset = 'ISO-8859-1';
46    protected $options         = ['auth_type' => 'check', 'language' => 'en_US'];
47    protected $page_size       = 10;
48    protected $list_page       = 1;
49    protected $threading       = false;
50    protected $search_set;
51
52    /**
53     * Internal (in-memory) cache
54     *
55     * @var array
56     */
57    protected $icache = [];
58
59    /**
60     * All (additional) headers used (in any way) by Roundcube
61     * Not listed here: DATE, FROM, TO, CC, REPLY-TO, SUBJECT, CONTENT-TYPE, LIST-POST
62     * (used for messages listing) are hardcoded in rcube_imap_generic::fetchHeaders()
63     *
64     * @var array
65     */
66    protected $all_headers = [
67        'CONTENT-TRANSFER-ENCODING',
68        'BCC',
69        'IN-REPLY-TO',
70        'MAIL-FOLLOWUP-TO',
71        'MAIL-REPLY-TO',
72        'MESSAGE-ID',
73        'REFERENCES',
74        'RESENT-BCC',
75        'RETURN-PATH',
76        'SENDER',
77        'X-DRAFT-INFO',
78    ];
79
80    const UNKNOWN       = 0;
81    const NOPERM        = 1;
82    const READONLY      = 2;
83    const TRYCREATE     = 3;
84    const INUSE         = 4;
85    const OVERQUOTA     = 5;
86    const ALREADYEXISTS = 6;
87    const NONEXISTENT   = 7;
88    const CONTACTADMIN  = 8;
89
90    const DUAL_USE_FOLDERS = 'X-DUAL-USE-FOLDERS';
91
92
93    /**
94     * Connect to the server
95     *
96     * @param  string   $host    Host to connect
97     * @param  string   $user    Username for IMAP account
98     * @param  string   $pass    Password for IMAP account
99     * @param  integer  $port    Port to connect to
100     * @param  string   $use_ssl SSL schema (either ssl or tls) or null if plain connection
101     *
102     * @return bool True on success, False on failure
103     */
104    abstract function connect($host, $user, $pass, $port = 143, $use_ssl = null);
105
106    /**
107     * Close connection. Usually done on script shutdown
108     */
109    abstract function close();
110
111    /**
112     * Checks connection state.
113     *
114     * @return bool True on success, False on failure
115     */
116    abstract function is_connected();
117
118    /**
119     * Check connection state, connect if not connected.
120     *
121     * @return bool Connection state.
122     */
123    abstract function check_connection();
124
125    /**
126     * Returns code of last error
127     *
128     * @return int Error code
129     */
130    abstract function get_error_code();
131
132    /**
133     * Returns message of last error
134     *
135     * @return string Error message
136     */
137    abstract function get_error_str();
138
139    /**
140     * Returns code of last command response
141     *
142     * @return int Response code (class constant)
143     */
144    abstract function get_response_code();
145
146    /**
147     * Set connection and class options
148     *
149     * @param array $opt Options array
150     */
151    public function set_options($opt)
152    {
153        $this->options = array_merge($this->options, (array) $opt);
154    }
155
156    /**
157     * Get connection/class option
158     *
159     * @param string $name Option name
160     *
161     * @param mixed Option value
162     */
163    public function get_option($name)
164    {
165        return $this->options[$name];
166    }
167
168    /**
169     * Activate/deactivate debug mode.
170     *
171     * @param bool $dbg True if conversation with the server should be logged
172     */
173    abstract function set_debug($dbg = true);
174
175    /**
176     * Set default message charset.
177     *
178     * This will be used for message decoding if a charset specification is not available
179     *
180     * @param string $cs Charset string
181     */
182    public function set_charset($cs)
183    {
184        $this->default_charset = $cs;
185    }
186
187    /**
188     * Set internal folder reference.
189     * All operations will be performed on this folder.
190     *
191     * @param string $folder Folder name
192     */
193    public function set_folder($folder)
194    {
195        if ($this->folder === $folder) {
196            return;
197        }
198
199        $this->folder = $folder;
200    }
201
202    /**
203     * Returns the currently used folder name
204     *
205     * @return string Name of the folder
206     */
207    public function get_folder()
208    {
209        return $this->folder;
210    }
211
212    /**
213     * Set internal list page number.
214     *
215     * @param int $page Page number to list
216     */
217    public function set_page($page)
218    {
219        if ($page = intval($page)) {
220            $this->list_page = $page;
221        }
222    }
223
224    /**
225     * Gets internal list page number.
226     *
227     * @return int Page number
228     */
229    public function get_page()
230    {
231        return $this->list_page;
232    }
233
234    /**
235     * Set internal page size
236     *
237     * @param int $size Number of messages to display on one page
238     */
239    public function set_pagesize($size)
240    {
241        $this->page_size = (int) $size;
242    }
243
244    /**
245     * Get internal page size
246     *
247     * @return int Number of messages to display on one page
248     */
249    public function get_pagesize()
250    {
251        return $this->page_size;
252    }
253
254    /**
255     * Save a search result for future message listing methods.
256     *
257     * @param mixed $set Search set in driver specific format
258     */
259    abstract function set_search_set($set);
260
261    /**
262     * Return the saved search set.
263     *
264     * @return array Search set in driver specific format, NULL if search wasn't initialized
265     */
266    abstract function get_search_set();
267
268    /**
269     * Returns the storage server's (IMAP) capability
270     *
271     * @param string $cap Capability name
272     *
273     * @return mixed Capability value or True if supported, False if not
274     */
275    abstract function get_capability($cap);
276
277    /**
278     * Sets threading flag to the best supported THREAD algorithm.
279     * Enable/Disable threaded mode.
280     *
281     * @param bool $enable True to enable threading
282     *
283     * @return mixed Threading algorithm or False if THREAD is not supported
284     */
285    public function set_threading($enable = false)
286    {
287        $this->threading = false;
288
289        if ($enable && ($caps = $this->get_capability('THREAD'))) {
290            $methods = ['REFS', 'REFERENCES', 'ORDEREDSUBJECT'];
291            $methods = array_intersect($methods, $caps);
292
293            $this->threading = array_first($methods);
294        }
295
296        return $this->threading;
297    }
298
299    /**
300     * Get current threading flag.
301     *
302     * @return mixed Threading algorithm or False if THREAD is not supported or disabled
303     */
304    public function get_threading()
305    {
306        return $this->threading;
307    }
308
309    /**
310     * Checks the PERMANENTFLAGS capability of the current folder
311     * and returns true if the given flag is supported by the server.
312     *
313     * @param string $flag Permanentflag name
314     *
315     * @return bool True if this flag is supported
316     */
317    abstract function check_permflag($flag);
318
319    /**
320     * Returns the delimiter that is used by the server
321     * for folder hierarchy separation.
322     *
323     * @return string Delimiter string
324     */
325    abstract function get_hierarchy_delimiter();
326
327    /**
328     * Get namespace
329     *
330     * @param string $name Namespace array index: personal, other, shared, prefix
331     *
332     * @return array Namespace data
333     */
334    abstract function get_namespace($name = null);
335
336    /**
337     * Get messages count for a specific folder.
338     *
339     * @param string $folder  Folder name
340     * @param string $mode    Mode for count [ALL|THREADS|UNSEEN|RECENT|EXISTS]
341     * @param bool   $force   Force reading from server and update cache
342     * @param bool   $status  Enables storing folder status info (max UID/count),
343     *                        required for folder_status()
344     *
345     * @return int Number of messages
346     */
347    abstract function count($folder = null, $mode = 'ALL', $force = false, $status = true);
348
349    /**
350     * Public method for listing message flags
351     *
352     * @param string $folder  Folder name
353     * @param array  $uids    Message UIDs
354     * @param int    $mod_seq Optional MODSEQ value
355     *
356     * @return array Indexed array with message flags
357     */
358    abstract function list_flags($folder, $uids, $mod_seq = null);
359
360    /**
361     * Public method for listing headers.
362     *
363     * @param   string   $folder     Folder name
364     * @param   int      $page       Current page to list
365     * @param   string   $sort_field Header field to sort by
366     * @param   string   $sort_order Sort order [ASC|DESC]
367     * @param   int      $slice      Number of slice items to extract from result array
368     *
369     * @return  array    Indexed array with message header objects
370     */
371    abstract function list_messages($folder = null, $page = null, $sort_field = null, $sort_order = null, $slice = 0);
372
373    /**
374     * Return sorted list of message UIDs
375     *
376     * @param string $folder     Folder to get index from
377     * @param string $sort_field Sort column
378     * @param string $sort_order Sort order [ASC, DESC]
379     *
380     * @return rcube_result_index|rcube_result_thread List of messages (UIDs)
381     */
382    abstract function index($folder = null, $sort_field = null, $sort_order = null);
383
384    /**
385     * Invoke search request to the server.
386     *
387     * @param  string  $folder     Folder name to search in
388     * @param  string  $str        Search criteria
389     * @param  string  $charset    Search charset
390     * @param  string  $sort_field Header field to sort by
391     *
392     * @todo: Search criteria should be provided in non-IMAP format, e.g. array
393     */
394    abstract function search($folder = null, $str = 'ALL', $charset = null, $sort_field = null);
395
396    /**
397     * Direct (real and simple) search request (without result sorting and caching).
398     *
399     * @param  string  $folder  Folder name to search in
400     * @param  string  $str     Search string
401     *
402     * @return rcube_result_index  Search result (UIDs)
403     */
404    abstract function search_once($folder = null, $str = 'ALL');
405
406    /**
407     * Refresh saved search set
408     *
409     * @return array Current search set
410     */
411    abstract function refresh_search();
412
413
414    /* --------------------------------
415     *        messages management
416     * --------------------------------*/
417
418    /**
419     * Fetch message headers and body structure from the server and build
420     * an object structure.
421     *
422     * @param int     $uid     Message UID to fetch
423     * @param string  $folder  Folder to read from
424     *
425     * @return object rcube_message_header Message data
426     */
427    abstract function get_message($uid, $folder = null);
428
429    /**
430     * Return message headers object of a specific message
431     *
432     * @param int     $id       Message sequence ID or UID
433     * @param string  $folder   Folder to read from
434     * @param bool    $force    True to skip cache
435     *
436     * @return rcube_message_header Message headers
437     */
438    abstract function get_message_headers($uid, $folder = null, $force = false);
439
440    /**
441     * Fetch message body of a specific message from the server
442     *
443     * @param  int                $uid    Message UID
444     * @param  string             $part   Part number
445     * @param  rcube_message_part $o_part Part object created by get_structure()
446     * @param  mixed              $print  True to print part, resource to write part contents in
447     * @param  resource           $fp     File pointer to save the message part
448     * @param  bool               $skip_charset_conv Disables charset conversion
449     *
450     * @return string Message/part body if not printed
451     */
452    abstract function get_message_part($uid, $part = 1, $o_part = null, $print = null, $fp = null, $skip_charset_conv = false);
453
454    /**
455     * Fetch message body of a specific message from the server
456     *
457     * @param int $uid Message UID
458     *
459     * @return string $part Message/part body
460     * @see    rcube_imap::get_message_part()
461     */
462    public function get_body($uid, $part = 1)
463    {
464        $headers = $this->get_message_headers($uid);
465        return rcube_charset::convert($this->get_message_part($uid, $part, null),
466            $headers->charset ?: $this->default_charset);
467    }
468
469    /**
470     * Returns the whole message source as string (or saves to a file)
471     *
472     * @param int      $uid  Message UID
473     * @param resource $fp   File pointer to save the message
474     * @param string   $part Optional message part ID
475     *
476     * @return string Message source string
477     */
478    abstract function get_raw_body($uid, $fp = null, $part = null);
479
480    /**
481     * Returns the message headers as string
482     *
483     * @param int    $uid  Message UID
484     * @param string $part Optional message part ID
485     *
486     * @return string Message headers string
487     */
488    abstract function get_raw_headers($uid, $part = null);
489
490    /**
491     * Sends the whole message source to stdout
492     *
493     * @param int  $uid       Message UID
494     * @param bool $formatted Enables line-ending formatting
495     */
496    abstract function print_raw_body($uid, $formatted = true);
497
498    /**
499     * Set message flag to one or several messages
500     *
501     * @param mixed  $uids       Message UIDs as array or comma-separated string, or '*'
502     * @param string $flag       Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
503     * @param string $folder     Folder name
504     * @param bool   $skip_cache True to skip message cache clean up
505     *
506     * @return bool Operation status
507     */
508    abstract function set_flag($uids, $flag, $folder = null, $skip_cache = false);
509
510    /**
511     * Remove message flag for one or several messages
512     *
513     * @param mixed  $uids    Message UIDs as array or comma-separated string, or '*'
514     * @param string $flag    Flag to unset: SEEN, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
515     * @param string $folder  Folder name
516     *
517     * @return bool Operation status
518     * @see set_flag
519     */
520    public function unset_flag($uids, $flag, $folder = null)
521    {
522        return $this->set_flag($uids, 'UN'.$flag, $folder);
523    }
524
525    /**
526     * Append a mail message (source) to a specific folder.
527     *
528     * @param string       $folder  Target folder
529     * @param string|array $message The message source string or filename
530     *                              or array (of strings and file pointers)
531     * @param string       $headers Headers string if $message contains only the body
532     * @param bool         $is_file True if $message is a filename
533     * @param array        $flags   Message flags
534     * @param mixed        $date    Message internal date
535     *
536     * @return int|bool Appended message UID or True on success, False on error
537     */
538    abstract function save_message($folder, &$message, $headers = '', $is_file = false, $flags = [], $date = null);
539
540    /**
541     * Move message(s) from one folder to another.
542     *
543     * @param mixed  $uids  Message UIDs as array or comma-separated string, or '*'
544     * @param string $to    Target folder
545     * @param string $from  Source folder
546     *
547     * @return bool True on success, False on error
548     */
549    abstract function move_message($uids, $to, $from = null);
550
551    /**
552     * Copy message(s) from one mailbox to another.
553     *
554     * @param mixed  $uids  Message UIDs as array or comma-separated string, or '*'
555     * @param string $to    Target folder
556     * @param string $from  Source folder
557     *
558     * @return bool True on success, False on error
559     */
560    abstract function copy_message($uids, $to, $from = null);
561
562    /**
563     * Mark message(s) as deleted and expunge.
564     *
565     * @param mixed  $uids    Message UIDs as array or comma-separated string, or '*'
566     * @param string $folder  Source folder
567     *
568     * @return bool True on success, False on error
569     */
570    abstract function delete_message($uids, $folder = null);
571
572    /**
573     * Expunge message(s) and clear the cache.
574     *
575     * @param mixed   $uids        Message UIDs as array or comma-separated string, or '*'
576     * @param string  $folder      Folder name
577     * @param bool    $clear_cache False if cache should not be cleared
578     *
579     * @return bool True on success, False on error
580     */
581    abstract function expunge_message($uids, $folder = null, $clear_cache = true);
582
583    /**
584     * Parse message UIDs input
585     *
586     * @param mixed $uids UIDs array or comma-separated list or '*' or '1:*'
587     *
588     * @return array Two elements array with UIDs converted to list and ALL flag
589     */
590    protected function parse_uids($uids)
591    {
592        $all = false;
593
594        if ($uids === '*' || $uids === '1:*') {
595            if (empty($this->search_set)) {
596                $uids = '1:*';
597                $all = true;
598            }
599            // get UIDs from current search set
600            else {
601                $uids = implode(',', $this->search_set->get());
602            }
603        }
604        else {
605            if (is_array($uids)) {
606                $uids = implode(',', $uids);
607            }
608            else if (strpos($uids, ':')) {
609                $uids = implode(',', rcube_imap_generic::uncompressMessageSet($uids));
610            }
611
612            if (preg_match('/[^0-9,]/', $uids)) {
613                $uids = '';
614            }
615        }
616
617        return [$uids, $all];
618    }
619
620
621    /* --------------------------------
622     *        folder management
623     * --------------------------------*/
624
625    /**
626     * Get a list of subscribed folders.
627     *
628     * @param   string  $root      Optional root folder
629     * @param   string  $name      Optional name pattern
630     * @param   string  $filter    Optional filter
631     * @param   string  $rights    Optional ACL requirements
632     * @param   bool    $skip_sort Enable to return unsorted list (for better performance)
633     *
634     * @return  array   List of folders
635     */
636    abstract function list_folders_subscribed($root = '', $name = '*', $filter = null, $rights = null, $skip_sort = false);
637
638    /**
639     * Get a list of all folders available on the server.
640     *
641     * @param string  $root      IMAP root dir
642     * @param string  $name      Optional name pattern
643     * @param mixed   $filter    Optional filter
644     * @param string  $rights    Optional ACL requirements
645     * @param bool    $skip_sort Enable to return unsorted list (for better performance)
646     *
647     * @return array Indexed array with folder names
648     */
649    abstract function list_folders($root = '', $name = '*', $filter = null, $rights = null, $skip_sort = false);
650
651    /**
652     * Subscribe to a specific folder(s)
653     *
654     * @param array $folders Folder name(s)
655     *
656     * @return bool True on success
657     */
658    abstract function subscribe($folders);
659
660    /**
661     * Unsubscribe folder(s)
662     *
663     * @param array $folders Folder name(s)
664     *
665     * @return bool True on success
666     */
667    abstract function unsubscribe($folders);
668
669    /**
670     * Create a new folder on the server.
671     *
672     * @param string  $folder    New folder name
673     * @param bool    $subscribe True if the new folder should be subscribed
674     * @param string  $type      Optional folder type (junk, trash, drafts, sent, archive)
675     * @param bool    $noselect  Make the folder \NoSelect folder by adding hierarchy
676     *                           separator at the end (useful for server that do not support
677     *                           both folders and messages as folder children)
678     *
679     * @return bool True on success, False on error
680     */
681    abstract function create_folder($folder, $subscribe = false, $type = null, $noselect = false);
682
683    /**
684     * Set a new name to an existing folder
685     *
686     * @param string $folder   Folder to rename
687     * @param string $new_name New folder name
688     *
689     * @return bool True on success, False on error
690     */
691    abstract function rename_folder($folder, $new_name);
692
693    /**
694     * Remove a folder from the server.
695     *
696     * @param string $folder Folder name
697     *
698     * @return bool True on success, False on error
699     */
700    abstract function delete_folder($folder);
701
702    /**
703     * Send expunge command and clear the cache.
704     *
705     * @param string  $folder      Folder name
706     * @param bool    $clear_cache False if cache should not be cleared
707     *
708     * @return bool True on success, False on error
709     */
710    public function expunge_folder($folder = null, $clear_cache = true)
711    {
712        return $this->expunge_message('*', $folder, $clear_cache);
713    }
714
715    /**
716     * Remove all messages in a folder..
717     *
718     * @param string  $folder  Folder name
719     *
720     * @return bool True on success, False on error
721     */
722    public function clear_folder($folder = null)
723    {
724        return $this->delete_message('*', $folder);
725    }
726
727    /**
728     * Checks if folder exists and is subscribed
729     *
730     * @param string  $folder       Folder name
731     * @param bool    $subscription Enable subscription checking
732     *
733     * @return bool True if folder exists, False otherwise
734     */
735    abstract function folder_exists($folder, $subscription = false);
736
737    /**
738     * Get folder size (size of all messages in a folder)
739     *
740     * @param string $folder Folder name
741     *
742     * @return int Folder size in bytes, False on error
743     */
744    abstract function folder_size($folder);
745
746    /**
747     * Returns the namespace where the folder is in
748     *
749     * @param string $folder Folder name
750     *
751     * @return string One of 'personal', 'other' or 'shared'
752     */
753    abstract function folder_namespace($folder);
754
755    /**
756     * Gets folder attributes (from LIST response, e.g. \Noselect, \Noinferiors).
757     *
758     * @param string $folder  Folder name
759     * @param bool   $force   Set to True if attributes should be refreshed
760     *
761     * @return array Options list
762     */
763    abstract function folder_attributes($folder, $force = false);
764
765    /**
766     * Gets connection (and current folder) data: UIDVALIDITY, EXISTS, RECENT,
767     * PERMANENTFLAGS, UIDNEXT, UNSEEN
768     *
769     * @param string $folder Folder name
770     *
771     * @return array Data
772     */
773    abstract function folder_data($folder);
774
775    /**
776     * Returns extended information about the folder.
777     *
778     * @param string $folder Folder name
779     *
780     * @return array Data
781     */
782    abstract function folder_info($folder);
783
784    /**
785     * Returns current status of a folder (compared to the last time use)
786     *
787     * @param string $folder Folder name
788     * @param array  $diff   Difference data
789     *
790     * @return int Folder status
791     */
792    abstract function folder_status($folder = null, &$diff = []);
793
794    /**
795     * Synchronizes messages cache.
796     *
797     * @param string $folder Folder name
798     */
799    abstract function folder_sync($folder);
800
801    /**
802     * Modify folder name according to namespace.
803     * For output it removes prefix of the personal namespace if it's possible.
804     * For input it adds the prefix. Use it before creating a folder in root
805     * of the folders tree.
806     *
807     * @param string $folder  Folder name
808     * @param string $mode    Mode name (out/in)
809     *
810     * @return string Folder name
811     */
812    abstract function mod_folder($folder, $mode = 'out');
813
814    /**
815     * Check if the folder name is valid
816     *
817     * @param string $folder Folder name (UTF-8)
818     * @param string &$char  First forbidden character found
819     *
820     * @return bool True if the name is valid, False otherwise
821     */
822    public function folder_validate($folder, &$char = null)
823    {
824        $delim = $this->get_hierarchy_delimiter();
825
826        if (strpos($folder, $delim) !== false) {
827            $char = $delim;
828            return false;
829        }
830
831        return true;
832    }
833
834    /**
835     * Create all folders specified as default
836     */
837    public function create_default_folders()
838    {
839        $rcube = rcube::get_instance();
840
841        // create default folders if they do not exist
842        foreach (self::$folder_types as $type) {
843            if ($folder = $rcube->config->get($type . '_mbox')) {
844                if (!$this->folder_exists($folder)) {
845                    $this->create_folder($folder, true, $type);
846                }
847                else if (!$this->folder_exists($folder, true)) {
848                    $this->subscribe($folder);
849                }
850            }
851        }
852    }
853
854    /**
855     * Check if specified folder is a special folder
856     */
857    public function is_special_folder($name)
858    {
859        return $name == 'INBOX' || in_array($name, $this->get_special_folders());
860    }
861
862    /**
863     * Return configured special folders
864     */
865    public function get_special_folders($forced = false)
866    {
867        // getting config might be expensive, store special folders in memory
868        if (!isset($this->icache['special-folders'])) {
869            $rcube = rcube::get_instance();
870            $this->icache['special-folders'] = [];
871
872            foreach (self::$folder_types as $type) {
873                if ($folder = $rcube->config->get($type . '_mbox')) {
874                    $this->icache['special-folders'][$type] = $folder;
875                }
876            }
877        }
878
879        return $this->icache['special-folders'];
880    }
881
882    /**
883     * Set special folder associations stored in backend
884     */
885    public function set_special_folders($specials)
886    {
887        // should be overridden by storage class if backend supports special folders (SPECIAL-USE)
888        unset($this->icache['special-folders']);
889    }
890
891    /**
892     * Get mailbox quota information.
893     *
894     * @param string $folder  Folder name
895     *
896     * @return mixed Quota info or False if not supported
897     */
898    abstract function get_quota($folder = null);
899
900
901    /* -----------------------------------------
902     *   ACL and METADATA methods
903     * ----------------------------------------*/
904
905    /**
906     * Changes the ACL on the specified folder (SETACL)
907     *
908     * @param string $folder  Folder name
909     * @param string $user    User name
910     * @param string $acl     ACL string
911     *
912     * @return bool True on success, False on failure
913     */
914    abstract function set_acl($folder, $user, $acl);
915
916    /**
917     * Removes any <identifier,rights> pair for the
918     * specified user from the ACL for the specified
919     * folder (DELETEACL).
920     *
921     * @param string $folder  Folder name
922     * @param string $user    User name
923     *
924     * @return bool True on success, False on failure
925     */
926    abstract function delete_acl($folder, $user);
927
928    /**
929     * Returns the access control list for a folder (GETACL).
930     *
931     * @param string $folder Folder name
932     *
933     * @return array User-rights array on success, NULL on error
934     */
935    abstract function get_acl($folder);
936
937    /**
938     * Returns information about what rights can be granted to the
939     * user (identifier) in the ACL for the folder (LISTRIGHTS).
940     *
941     * @param string $folder  Folder name
942     * @param string $user    User name
943     *
944     * @return array List of user rights
945     */
946    abstract function list_rights($folder, $user);
947
948    /**
949     * Returns the set of rights that the current user has to a folder (MYRIGHTS).
950     *
951     * @param string $folder Folder name
952     *
953     * @return array MYRIGHTS response on success, NULL on error
954     */
955    abstract function my_rights($folder);
956
957    /**
958     * Sets metadata/annotations (SETMETADATA/SETANNOTATION)
959     *
960     * @param string $folder  Folder name (empty for server metadata)
961     * @param array  $entries Entry-value array (use NULL value as NIL)
962     *
963     * @return bool True on success, False on failure
964     */
965    abstract function set_metadata($folder, $entries);
966
967    /**
968     * Unsets metadata/annotations (SETMETADATA/SETANNOTATION)
969     *
970     * @param string $folder  Folder name (empty for server metadata)
971     * @param array  $entries Entry names array
972     *
973     * @return bool True on success, False on failure
974     */
975    abstract function delete_metadata($folder, $entries);
976
977    /**
978     * Returns folder metadata/annotations (GETMETADATA/GETANNOTATION).
979     *
980     * @param string $folder   Folder name (empty for server metadata)
981     * @param array  $entries  Entries
982     * @param array  $options  Command options (with MAXSIZE and DEPTH keys)
983     * @param bool   $force    Disables cache use
984     *
985     * @return array Metadata entry-value hash array on success, NULL on error
986     */
987    abstract function get_metadata($folder, $entries, $options = [], $force = false);
988
989    /* -----------------------------------------
990     *   Cache related functions
991     * ----------------------------------------*/
992
993    /**
994     * Clears the cache.
995     *
996     * @param string  $key         Cache key name or pattern
997     * @param bool    $prefix_mode Enable it to clear all keys starting
998     *                             with prefix specified in $key
999     */
1000    abstract function clear_cache($key = null, $prefix_mode = false);
1001
1002    /**
1003     * Returns cached value
1004     *
1005     * @param string $key Cache key
1006     *
1007     * @return mixed Cached value
1008     */
1009    abstract function get_cache($key);
1010
1011    /**
1012     * Delete outdated cache entries
1013     */
1014    abstract function cache_gc();
1015}
1016