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