1 /*
2 * camel-imapx-status-response.c
3 *
4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 /**
19 * SECTION: camel-imapx-status-response
20 * @include: camel/camel.h
21 * @short_description: Stores an IMAP STATUS respose
22 *
23 * #CamelIMAPXStatusResponse encapsulates an IMAP STATUS response, which
24 * describes the current status of a mailbox in terms of various message
25 * counts and change tracking indicators.
26 **/
27
28 #include "evolution-data-server-config.h"
29
30 #include "camel-imapx-status-response.h"
31
32 #include "camel-imapx-utils.h"
33
34 struct _CamelIMAPXStatusResponsePrivate {
35 gchar *mailbox_name;
36
37 guint32 messages;
38 guint32 recent;
39 guint32 unseen;
40 guint32 uidnext;
41 guint32 uidvalidity;
42 guint64 highestmodseq;
43
44 gboolean have_messages;
45 gboolean have_recent;
46 gboolean have_unseen;
47 gboolean have_uidnext;
48 gboolean have_uidvalidity;
49 gboolean have_highestmodseq;
50 };
51
G_DEFINE_TYPE_WITH_PRIVATE(CamelIMAPXStatusResponse,camel_imapx_status_response,G_TYPE_OBJECT)52 G_DEFINE_TYPE_WITH_PRIVATE (
53 CamelIMAPXStatusResponse,
54 camel_imapx_status_response,
55 G_TYPE_OBJECT)
56
57 static void
58 imapx_status_response_finalize (GObject *object)
59 {
60 CamelIMAPXStatusResponsePrivate *priv;
61
62 priv = CAMEL_IMAPX_STATUS_RESPONSE (object)->priv;
63
64 g_free (priv->mailbox_name);
65
66 /* Chain up to parent's finalize() method. */
67 G_OBJECT_CLASS (camel_imapx_status_response_parent_class)->
68 finalize (object);
69 }
70
71 static void
camel_imapx_status_response_class_init(CamelIMAPXStatusResponseClass * class)72 camel_imapx_status_response_class_init (CamelIMAPXStatusResponseClass *class)
73 {
74 GObjectClass *object_class;
75
76 object_class = G_OBJECT_CLASS (class);
77 object_class->finalize = imapx_status_response_finalize;
78 }
79
80 static void
camel_imapx_status_response_init(CamelIMAPXStatusResponse * response)81 camel_imapx_status_response_init (CamelIMAPXStatusResponse *response)
82 {
83 response->priv = camel_imapx_status_response_get_instance_private (response);
84 }
85
86 /**
87 * camel_imapx_status_response_new:
88 * @stream: a #CamelIMAPXInputStream
89 * @inbox_separator: the separator character for INBOX
90 * @cancellable: a #GCancellable
91 * @error: return location for a #GError, or %NULL
92 *
93 * Attempts to parse an IMAP STATUS response from @stream and, if successful,
94 * stores the response data in a new #CamelIMAPXStatusResponse. If an error
95 * occurs, the function sets @error and returns %NULL.
96 *
97 * Returns: a #CamelIMAPXStatusResponse, or %NULL
98 *
99 * Since: 3.10
100 **/
101 CamelIMAPXStatusResponse *
camel_imapx_status_response_new(CamelIMAPXInputStream * stream,gchar inbox_separator,GCancellable * cancellable,GError ** error)102 camel_imapx_status_response_new (CamelIMAPXInputStream *stream,
103 gchar inbox_separator,
104 GCancellable *cancellable,
105 GError **error)
106 {
107 CamelIMAPXStatusResponse *response;
108 camel_imapx_token_t tok;
109 guchar *token;
110 guint len;
111
112 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (stream), NULL);
113
114 response = g_object_new (CAMEL_TYPE_IMAPX_STATUS_RESPONSE, NULL);
115
116 /* Parse mailbox name. */
117
118 response->priv->mailbox_name = camel_imapx_parse_mailbox (
119 stream, inbox_separator, cancellable, error);
120 if (response->priv->mailbox_name == NULL)
121 goto fail;
122
123 /* Parse status attributes. */
124
125 tok = camel_imapx_input_stream_token (
126 CAMEL_IMAPX_INPUT_STREAM (stream),
127 &token, &len, cancellable, error);
128 if (tok == IMAPX_TOK_ERROR)
129 goto fail;
130 if (tok != '(') {
131 g_set_error (
132 error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
133 "status: expecting '('");
134 goto fail;
135 }
136
137 tok = camel_imapx_input_stream_token (
138 CAMEL_IMAPX_INPUT_STREAM (stream),
139 &token, &len, cancellable, error);
140
141 while (tok == IMAPX_TOK_TOKEN) {
142 guint64 number;
143 gboolean success;
144
145 switch (imapx_tokenise ((gchar *) token, len)) {
146 case IMAPX_MESSAGES:
147 success = camel_imapx_input_stream_number (
148 CAMEL_IMAPX_INPUT_STREAM (stream),
149 &number, cancellable, error);
150 response->priv->messages = (guint32) number;
151 response->priv->have_messages = TRUE;
152 break;
153
154 case IMAPX_RECENT:
155 success = camel_imapx_input_stream_number (
156 CAMEL_IMAPX_INPUT_STREAM (stream),
157 &number, cancellable, error);
158 response->priv->recent = (guint32) number;
159 response->priv->have_recent = TRUE;
160 break;
161
162 case IMAPX_UNSEEN:
163 success = camel_imapx_input_stream_number (
164 CAMEL_IMAPX_INPUT_STREAM (stream),
165 &number, cancellable, error);
166 response->priv->unseen = (guint32) number;
167 response->priv->have_unseen = TRUE;
168 break;
169
170 case IMAPX_UIDNEXT:
171 success = camel_imapx_input_stream_number (
172 CAMEL_IMAPX_INPUT_STREAM (stream),
173 &number, cancellable, error);
174 response->priv->uidnext = (guint32) number;
175 response->priv->have_uidnext = TRUE;
176 break;
177
178 case IMAPX_UIDVALIDITY:
179 success = camel_imapx_input_stream_number (
180 CAMEL_IMAPX_INPUT_STREAM (stream),
181 &number, cancellable, error);
182 response->priv->uidvalidity = (guint32) number;
183 response->priv->have_uidvalidity = TRUE;
184 break;
185
186 /* See RFC 4551 section 3.6 */
187 case IMAPX_HIGHESTMODSEQ:
188 success = camel_imapx_input_stream_number (
189 CAMEL_IMAPX_INPUT_STREAM (stream),
190 &number, cancellable, error);
191 response->priv->highestmodseq = number;
192 response->priv->have_highestmodseq = TRUE;
193 break;
194
195 default:
196 g_set_error (
197 error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
198 "unknown status attribute");
199 success = FALSE;
200 break;
201 }
202
203 if (!success)
204 goto fail;
205
206 tok = camel_imapx_input_stream_token (
207 CAMEL_IMAPX_INPUT_STREAM (stream),
208 &token, &len, cancellable, error);
209 }
210
211 if (tok == IMAPX_TOK_ERROR)
212 goto fail;
213
214 if (tok != ')') {
215 g_set_error (
216 error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
217 "status: expecting ')' or attribute");
218 goto fail;
219 }
220
221 return response;
222
223 fail:
224 g_clear_object (&response);
225
226 return NULL;
227 }
228
229 /**
230 * camel_imapx_status_response_get_mailbox_name:
231 * @response: a #CamelIMAPXStatusResponse
232 *
233 * Returns the mailbox name for @response.
234 *
235 * Returns: the mailbox name
236 *
237 * Since: 3.10
238 **/
239 const gchar *
camel_imapx_status_response_get_mailbox_name(CamelIMAPXStatusResponse * response)240 camel_imapx_status_response_get_mailbox_name (CamelIMAPXStatusResponse *response)
241 {
242 g_return_val_if_fail (CAMEL_IS_IMAPX_STATUS_RESPONSE (response), NULL);
243
244 return response->priv->mailbox_name;
245 }
246
247 /**
248 * camel_imapx_status_response_get_messages:
249 * @response: a #CamelIMAPXStatusResponse
250 * @out_messages: return location for the status value, or %NULL
251 *
252 * If @response includes an updated "MESSAGES" value, write the value to
253 * @out_messages and return %TRUE. Otherwise leave @out_messages unset
254 * and return %FALSE.
255 *
256 * The "MESSAGES" value refers to the number of messages in the mailbox.
257 *
258 * The @out_messages argument can be %NULL, in which case the function
259 * simply returns whether an updated "MESSAGES" value is present.
260 *
261 * Returns: whether @out_messages was set
262 *
263 * Since: 3.10
264 **/
265 gboolean
camel_imapx_status_response_get_messages(CamelIMAPXStatusResponse * response,guint32 * out_messages)266 camel_imapx_status_response_get_messages (CamelIMAPXStatusResponse *response,
267 guint32 *out_messages)
268 {
269 g_return_val_if_fail (
270 CAMEL_IS_IMAPX_STATUS_RESPONSE (response), FALSE);
271
272 if (out_messages != NULL && response->priv->have_messages)
273 *out_messages = response->priv->messages;
274
275 return response->priv->have_messages;
276 }
277
278 /**
279 * camel_imapx_status_response_get_recent:
280 * @response: a #CamelIMAPXStatusResponse
281 * @out_recent: return location for the status value, or %NULL
282 *
283 * If @response includes an updated "RECENT" value, write the value to
284 * @out_recent and return %TRUE. Otherwise leave @out_recent unset and
285 * return %FALSE.
286 *
287 * The "RECENT" value refers to the number of messages with the \Recent
288 * flag set.
289 *
290 * The @out_recent argument can be %NULL, in which case the function
291 * simply returns whether an updated "RECENT" value is present.
292 *
293 * Returns: whether @out_recent was set
294 *
295 * Since: 3.10
296 **/
297 gboolean
camel_imapx_status_response_get_recent(CamelIMAPXStatusResponse * response,guint32 * out_recent)298 camel_imapx_status_response_get_recent (CamelIMAPXStatusResponse *response,
299 guint32 *out_recent)
300 {
301 g_return_val_if_fail (
302 CAMEL_IS_IMAPX_STATUS_RESPONSE (response), FALSE);
303
304 if (out_recent != NULL && response->priv->have_recent)
305 *out_recent = response->priv->recent;
306
307 return response->priv->have_recent;
308 }
309
310 /**
311 * camel_imapx_status_response_get_unseen:
312 * @response: a #CamelIMAPXStatusResponse
313 * @out_unseen: return location for the status value, or %NULL
314 *
315 * If @response includes an updated "UNSEEN" value, write the value to
316 * @out_unseen and return %TRUE. Otherwise leave @out_unseen unset and
317 * return %FALSE.
318 *
319 * The "UNSEEN" value refers to the number of messages which do not have
320 * the \Seen flag set.
321 *
322 * The @out_unseen argument can be %NULL, in which case the function
323 * simply returns whether an updated "UNSEEN" value is present.
324 *
325 * Returns: whether @out_unseen was set
326 *
327 * Since: 3.10
328 **/
329 gboolean
camel_imapx_status_response_get_unseen(CamelIMAPXStatusResponse * response,guint32 * out_unseen)330 camel_imapx_status_response_get_unseen (CamelIMAPXStatusResponse *response,
331 guint32 *out_unseen)
332 {
333 g_return_val_if_fail (
334 CAMEL_IS_IMAPX_STATUS_RESPONSE (response), FALSE);
335
336 if (out_unseen != NULL && response->priv->have_unseen)
337 *out_unseen = response->priv->unseen;
338
339 return response->priv->have_unseen;
340 }
341
342 /**
343 * camel_imapx_status_response_get_uidnext:
344 * @response: a #CamelIMAPXStatusResponse
345 * @out_uidnext: return location for the status value, or %NULL
346 *
347 * If @response includes an updated "UIDNEXT" value, write the value to
348 * @out_uidnext and return %TRUE. Otherwise leave @out_uidnext unset and
349 * return %FALSE.
350 *
351 * The "UIDNEXT" value refers to the next unique identifier value of the
352 * mailbox.
353 *
354 * The @out_uidnext argument can be %NULL, in which case the function
355 * simply returns whether an updated "UIDNEXT" value is present.
356 *
357 * Returns: whether @out_uidnext was set
358 *
359 * Since: 3.10
360 **/
361 gboolean
camel_imapx_status_response_get_uidnext(CamelIMAPXStatusResponse * response,guint32 * out_uidnext)362 camel_imapx_status_response_get_uidnext (CamelIMAPXStatusResponse *response,
363 guint32 *out_uidnext)
364 {
365 g_return_val_if_fail (
366 CAMEL_IS_IMAPX_STATUS_RESPONSE (response), FALSE);
367
368 if (out_uidnext != NULL && response->priv->have_uidnext)
369 *out_uidnext = response->priv->uidnext;
370
371 return response->priv->have_uidnext;
372 }
373
374 /**
375 * camel_imapx_status_response_get_uidvalidity:
376 * @response: a #CamelIMAPXStatusResponse
377 * @out_uidvalidity: return location for the status value, or %NULL
378 *
379 * If @response includes an updated "UIDVALIDITY" value, write the value to
380 * @out_uidvalidity and return %TRUE. Otherwise leave @out_uidvalidity unset
381 * and return %FALSE.
382 *
383 * The "UIDVALIDITY" value refers to the unique identifier validity of the
384 * mailbox.
385 *
386 * The @out_uidvalidity argument can be %NULL, in which case the function
387 * simply returns whether an updated "UIDVALIDITY" value is present.
388 *
389 * Returns: whether @out_uidvalidity was set
390 *
391 * Since: 3.10
392 **/
393 gboolean
camel_imapx_status_response_get_uidvalidity(CamelIMAPXStatusResponse * response,guint32 * out_uidvalidity)394 camel_imapx_status_response_get_uidvalidity (CamelIMAPXStatusResponse *response,
395 guint32 *out_uidvalidity)
396 {
397 g_return_val_if_fail (
398 CAMEL_IS_IMAPX_STATUS_RESPONSE (response), FALSE);
399
400 if (out_uidvalidity != NULL && response->priv->have_uidvalidity)
401 *out_uidvalidity = response->priv->uidvalidity;
402
403 return response->priv->have_uidvalidity;
404 }
405
406 /**
407 * camel_imapx_status_response_get_highestmodseq:
408 * @response: a #CamelIMAPXStatusResponse
409 * @out_highestmodseq: return location for the status value, or %NULL
410 *
411 * If @response includes an updated "HIGHESTMODSEQ" value, write the value to
412 * @out_highestmodseq and return %TRUE. Otherwise leave @out_highestmodseq
413 * unset and return %FALSE.
414 *
415 * The "HIGHESTMODSEQ" value refers to the highest mod-sequence value of
416 * all messages in the mailbox, assuming the server supports the persistent
417 * storage of mod-sequences.
418 *
419 * The @out_highestmodseq argument can be %NULL, in which case the function
420 * simply returns whether an updated "HIGHESTMODSEQ" value is present.
421 *
422 * Returns: whether @out_highestmodseq was set
423 *
424 * Since: 3.10
425 **/
426 gboolean
camel_imapx_status_response_get_highestmodseq(CamelIMAPXStatusResponse * response,guint64 * out_highestmodseq)427 camel_imapx_status_response_get_highestmodseq (CamelIMAPXStatusResponse *response,
428 guint64 *out_highestmodseq)
429 {
430 g_return_val_if_fail (
431 CAMEL_IS_IMAPX_STATUS_RESPONSE (response), FALSE);
432
433 if (out_highestmodseq != NULL && response->priv->have_highestmodseq)
434 *out_highestmodseq = response->priv->highestmodseq;
435
436 return response->priv->have_highestmodseq;
437 }
438
439