1<?php
2/**
3 * Copyright 2014-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 2014-2017 Horde LLC
10 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
11 * @package   Imap_Client
12 */
13
14/**
15 * Query the search charsets available on a server.
16 *
17 * @author    Michael Slusarz <slusarz@horde.org>
18 * @category  Horde
19 * @copyright 2014-2017 Horde LLC
20 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
21 * @package   Imap_Client
22 * @since     2.24.0
23 *
24 * @property-read array $charsets  The list of valid charsets that have been
25 *                                 discovered on the server.
26 */
27class Horde_Imap_Client_Data_SearchCharset
28implements Serializable, SplSubject
29{
30    /**
31     * Base client object.
32     *
33     * @var Horde_Imap_Client_Base
34     */
35    protected $_baseob;
36
37    /**
38     * Charset data.
39     *
40     * @var array
41     */
42    protected $_charsets = array(
43        'US-ASCII' => true
44    );
45
46    /**
47     * Observers.
48     *
49     * @var array
50     */
51    protected $_observers = array();
52
53    /**
54     */
55    public function __get($name)
56    {
57        switch ($name) {
58        case 'charsets':
59            return array_keys(array_filter($this->_charsets));
60        }
61    }
62
63    /**
64     */
65    public function setBaseOb(Horde_Imap_Client_Base $ob)
66    {
67        $this->_baseob = $ob;
68    }
69
70    /**
71     * Query the validity of a charset.
72     *
73     * @param string $charset  The charset to query.
74     * @param boolean $cached  If true, only query cached values.
75     *
76     * @return boolean  True if the charset is valid for searching.
77     */
78    public function query($charset, $cached = false)
79    {
80        $charset = Horde_String::upper($charset);
81
82        if (isset($this->_charsets[$charset])) {
83            return $this->_charsets[$charset];
84        } elseif ($cached) {
85            return null;
86        }
87
88        if (!$this->_baseob) {
89            throw new RuntimeException(
90                'Base object needs to be defined to query for charset.'
91            );
92        }
93
94        /* Use a dummy search query and search for BADCHARSET response. */
95        $query = new Horde_Imap_Client_Search_Query();
96        $query->charset($charset, false);
97        $query->ids($this->_baseob->getIdsOb(1, true));
98        $query->text('a');
99        try {
100            $this->_baseob->search('INBOX', $query, array(
101                'nocache' => true,
102                'sequence' => true
103            ));
104            $this->_charsets[$charset] = true;
105        } catch (Horde_Imap_Client_Exception $e) {
106            $this->_charsets[$charset] = ($e->getCode() !== Horde_Imap_Client_Exception::BADCHARSET);
107        }
108
109        $this->notify();
110
111        return $this->_charsets[$charset];
112    }
113
114    /**
115     * Set the validity of a given charset.
116     *
117     * @param string $charset  The charset.
118     * @param boolean $valid   Is charset valid?
119     */
120    public function setValid($charset, $valid = true)
121    {
122        $charset = Horde_String::upper($charset);
123        $valid = (bool)$valid;
124
125        if (!isset($this->_charsets[$charset]) ||
126            ($this->_charsets[$charset] !== $valid)) {
127            $this->_charsets[$charset] = $valid;
128            $this->notify();
129        }
130    }
131
132    /* SplSubject methods. */
133
134    /**
135     */
136    public function attach(SplObserver $observer)
137    {
138        $this->detach($observer);
139        $this->_observers[] = $observer;
140    }
141
142    /**
143     */
144    public function detach(SplObserver $observer)
145    {
146        if (($key = array_search($observer, $this->_observers, true)) !== false) {
147            unset($this->_observers[$key]);
148        }
149    }
150
151    /**
152     * Notification is triggered internally whenever the object's internal
153     * data storage is altered.
154     */
155    public function notify()
156    {
157        foreach ($this->_observers as $val) {
158            $val->update($this);
159        }
160    }
161
162    /* Serializable methods. */
163
164    /**
165     */
166    public function serialize()
167    {
168        return json_encode($this->_charsets);
169    }
170
171    /**
172     */
173    public function unserialize($data)
174    {
175        $this->_charsets = json_decode($data, true);
176    }
177
178}
179