1<?php
2/**
3 * Copyright 2012-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 2012-2017 Horde LLC
10 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
11 * @package   Imap_Client
12 */
13
14/**
15 * Mailbox synchronization results.
16 *
17 * @author    Michael Slusarz <slusarz@horde.org>
18 * @category  Horde
19 * @copyright 2012-2017 Horde LLC
20 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
21 * @package   Imap_Client
22 * @since     2.2.0
23 *
24 * @property-read Horde_Imap_Client_Ids $flagsuids  List of messages with flag
25 *                                                  changes.
26 * @property-read Horde_Imap_Client_Ids $newmsgsuids  List of new messages.
27 * @property-read Horde_Imap_Client_Ids $vanisheduids  List of messages that
28 *                                                     have vanished.
29 */
30class Horde_Imap_Client_Data_Sync
31{
32    /**
33     * Mappings of status() values to sync keys.
34     *
35     * @since 2.8.0
36     *
37     * @var array
38     */
39    public static $map = array(
40        'H' => 'highestmodseq',
41        'M' => 'messages',
42        'U' => 'uidnext',
43        'V' => 'uidvalidity'
44    );
45
46    /**
47     * Are there messages that have had flag changes?
48     *
49     * @var boolean
50     */
51    public $flags = null;
52
53    /**
54     * The previous value of HIGHESTMODSEQ.
55     *
56     * @since 2.8.0
57     *
58     * @var integer
59     */
60    public $highestmodseq = null;
61
62    /**
63     * The synchronized mailbox.
64     *
65     * @var Horde_Imap_Client_Mailbox
66     */
67    public $mailbox;
68
69    /**
70     * The previous number of messages in the mailbox.
71     *
72     * @since 2.8.0
73     *
74     * @var integer
75     */
76    public $messages = null;
77
78    /**
79     * Are there new messages?
80     *
81     * @var boolean
82     */
83    public $newmsgs = null;
84
85    /**
86     * The previous value of UIDNEXT.
87     *
88     * @since 2.8.0
89     *
90     * @var integer
91     */
92    public $uidnext = null;
93
94    /**
95     * The previous value of UIDVALIDITY.
96     *
97     * @since 2.8.0
98     *
99     * @var integer
100     */
101    public $uidvalidity = null;
102
103    /**
104     * The UIDs of messages that are guaranteed to have vanished. This list is
105     * only guaranteed to be available if the server supports QRESYNC or a
106     * list of known UIDs is passed to the sync() method.
107     *
108     * @var Horde_Imap_Client_Ids
109     */
110    public $vanished = null;
111
112    /**
113     * UIDs of messages that have had flag changes.
114     *
115     * @var Horde_Imap_Client_Ids
116     */
117    protected $_flagsuids;
118
119    /**
120     * UIDs of new messages.
121     *
122     * @var Horde_Imap_Client_Ids
123     */
124    protected $_newmsgsuids;
125
126    /**
127     * UIDs of messages that have vanished.
128     *
129     * @var Horde_Imap_Client_Ids
130     */
131    protected $_vanisheduids;
132
133    /**
134     * Constructor.
135     *
136     * @param Horde_Imap_Client_Base $base_ob  Base driver object.
137     * @param mixed $mailbox                   Mailbox to sync.
138     * @param array $sync                      Token sync data.
139     * @param array $curr                      Current sync data.
140     * @param integer $criteria                Mask of criteria to return.
141     * @param Horde_Imap_Client_Ids $ids       List of known UIDs.
142     *
143     * @throws Horde_Imap_Client_Exception
144     * @throws Horde_Imap_Client_Exception_Sync
145     */
146    public function __construct(Horde_Imap_Client_Base $base_ob, $mailbox,
147                                $sync, $curr, $criteria, $ids)
148    {
149        foreach (self::$map as $key => $val) {
150            if (isset($sync[$key])) {
151                $this->$val = $sync[$key];
152            }
153        }
154
155        /* Check uidvalidity. */
156        if (!$this->uidvalidity || ($curr['V'] != $this->uidvalidity)) {
157            throw new Horde_Imap_Client_Exception_Sync('UIDs in cached mailbox have changed.', Horde_Imap_Client_Exception_Sync::UIDVALIDITY_CHANGED);
158        }
159
160        $this->mailbox = $mailbox;
161
162        /* This was a UIDVALIDITY check only. */
163        if (!$criteria) {
164            return;
165        }
166
167        $sync_all = ($criteria & Horde_Imap_Client::SYNC_ALL);
168
169        /* New messages. */
170        if ($sync_all ||
171            ($criteria & Horde_Imap_Client::SYNC_NEWMSGS) ||
172            ($criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS)) {
173            $this->newmsgs = empty($this->uidnext)
174                ? !empty($curr['U'])
175                : (!empty($curr['U']) && ($curr['U'] > $this->uidnext));
176
177            if ($this->newmsgs &&
178                ($sync_all ||
179                 ($criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS))) {
180                $new_ids = empty($this->uidnext)
181                    ? Horde_Imap_Client_Ids::ALL
182                    : ($this->uidnext . ':' . $curr['U']);
183
184                $squery = new Horde_Imap_Client_Search_Query();
185                $squery->ids(new Horde_Imap_Client_Ids($new_ids));
186                $sres = $base_ob->search($mailbox, $squery);
187
188                $this->_newmsgsuids = $sres['match'];
189            }
190        }
191
192        /* Do single status call to get all necessary data. */
193        if ($this->highestmodseq &&
194            ($sync_all ||
195             ($criteria & Horde_Imap_Client::SYNC_FLAGS) ||
196             ($criteria & Horde_Imap_Client::SYNC_FLAGSUIDS) ||
197             ($criteria & Horde_Imap_Client::SYNC_VANISHED) ||
198             ($criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS))) {
199            $status_sync = $base_ob->status($mailbox, Horde_Imap_Client::STATUS_SYNCMODSEQ | Horde_Imap_Client::STATUS_SYNCFLAGUIDS | Horde_Imap_Client::STATUS_SYNCVANISHED);
200
201            if (!is_null($ids)) {
202                $ids = $base_ob->resolveIds($mailbox, $ids);
203            }
204        }
205
206        /* Flag changes. */
207        if ($sync_all || ($criteria & Horde_Imap_Client::SYNC_FLAGS)) {
208            $this->flags = $this->highestmodseq
209                ? ($this->highestmodseq != $curr['H'])
210                : true;
211        }
212
213        if ($sync_all || ($criteria & Horde_Imap_Client::SYNC_FLAGSUIDS)) {
214            if ($this->highestmodseq) {
215                if ($this->highestmodseq == $status_sync['syncmodseq']) {
216                    $this->_flagsuids = is_null($ids)
217                        ? $status_sync['syncflaguids']
218                        : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncflaguids']->ids));
219                } else {
220                    $squery = new Horde_Imap_Client_Search_Query();
221                    $squery->modseq($this->highestmodseq + 1);
222                    $sres = $base_ob->search($mailbox, $squery, array(
223                        'ids' => $ids
224                    ));
225                    $this->_flagsuids = $sres['match'];
226                }
227            } else {
228                /* Without MODSEQ, need to mark all FLAGS as changed. */
229                $this->_flagsuids = $base_ob->resolveIds($mailbox, is_null($ids) ? $base_ob->getIdsOb(Horde_Imap_Client_Ids::ALL) : $ids);
230            }
231        }
232
233        /* Vanished messages. */
234        if ($sync_all ||
235            ($criteria & Horde_Imap_Client::SYNC_VANISHED) ||
236            ($criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS)) {
237            if ($this->highestmodseq &&
238                ($this->highestmodseq == $status_sync['syncmodseq'])) {
239                $vanished = is_null($ids)
240                    ? $status_sync['syncvanished']
241                    : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncvanished']->ids));
242            } else {
243                $vanished = $base_ob->vanished($mailbox, $this->highestmodseq ? $this->highestmodseq : 1, array(
244                    'ids' => $ids
245                ));
246            }
247
248            $this->vanished = (bool)count($vanished);
249            $this->_vanisheduids = $vanished;
250        }
251    }
252
253    /**
254     */
255    public function __get($name)
256    {
257        switch ($name) {
258        case 'flagsuids':
259        case 'newmsgsuids':
260        case 'vanisheduids':
261            return empty($this->{'_' . $name})
262                ? new Horde_Imap_Client_Ids()
263                : $this->{'_' . $name};
264        }
265    }
266
267}
268