I#27 - [IMAPx] Ignore DavMail's CR/LF in BODYSTRUCTURE response
[evolution-data-server.git] / src / camel / providers / imapx / camel-imapx-input-stream.c
blobf47b08f7df7d683dc7f67578e2007435af406c27
1 /*
2 * camel-imapx-input-stream.h
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.
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.
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/>.
18 #include "evolution-data-server-config.h"
20 #include <string.h>
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <errno.h>
25 #include <glib/gi18n-lib.h>
27 #include <camel/camel.h>
29 #include "camel-imapx-server.h"
30 #include "camel-imapx-utils.h"
32 #include "camel-imapx-input-stream.h"
34 #define CAMEL_IMAPX_INPUT_STREAM_GET_PRIVATE(obj) \
35 (G_TYPE_INSTANCE_GET_PRIVATE \
36 ((obj), CAMEL_TYPE_IMAPX_INPUT_STREAM, CamelIMAPXInputStreamPrivate))
38 struct _CamelIMAPXInputStreamPrivate {
39 guchar *buf, *ptr, *end;
40 guint literal;
42 guint unget;
43 camel_imapx_token_t unget_tok;
44 guchar *unget_token;
45 guint unget_len;
47 guchar *tokenbuf;
48 guint bufsize;
51 /* Forward Declarations */
52 static void camel_imapx_input_stream_pollable_init
53 (GPollableInputStreamInterface *iface);
55 G_DEFINE_TYPE_WITH_CODE (
56 CamelIMAPXInputStream,
57 camel_imapx_input_stream,
58 G_TYPE_FILTER_INPUT_STREAM,
59 G_IMPLEMENT_INTERFACE (
60 G_TYPE_POLLABLE_INPUT_STREAM,
61 camel_imapx_input_stream_pollable_init))
63 static gint
64 imapx_input_stream_fill (CamelIMAPXInputStream *is,
65 GCancellable *cancellable,
66 GError **error)
68 GInputStream *base_stream;
69 gint left = 0;
71 if (g_cancellable_is_cancelled (cancellable))
72 return -1;
74 base_stream = g_filter_input_stream_get_base_stream (
75 G_FILTER_INPUT_STREAM (is));
77 if (error && *error) {
78 g_warning ("%s: Avoiding GIO call with a filled error '%s'", G_STRFUNC, (*error)->message);
79 error = NULL;
82 left = is->priv->end - is->priv->ptr;
83 memcpy (is->priv->buf, is->priv->ptr, left);
84 is->priv->end = is->priv->buf + left;
85 is->priv->ptr = is->priv->buf;
86 left = g_input_stream_read (
87 base_stream,
88 is->priv->end,
89 is->priv->bufsize - (is->priv->end - is->priv->buf),
90 cancellable, error);
91 if (left > 0) {
92 is->priv->end += left;
93 return is->priv->end - is->priv->ptr;
94 } else {
95 /* If returning zero, camel_stream_read() doesn't consider
96 * that to be an error. But we do -- we should only be here
97 * if we *know* there are data to receive. So set the error
98 * accordingly */
99 if (!left)
100 g_set_error (
101 error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
102 _("Source stream returned no data"));
103 return -1;
107 static void
108 imapx_input_stream_finalize (GObject *object)
110 CamelIMAPXInputStreamPrivate *priv;
112 priv = CAMEL_IMAPX_INPUT_STREAM_GET_PRIVATE (object);
114 g_free (priv->buf);
115 g_free (priv->tokenbuf);
117 /* Chain up to parent's finalize() method. */
118 G_OBJECT_CLASS (camel_imapx_input_stream_parent_class)->
119 finalize (object);
122 static gssize
123 imapx_input_stream_read (GInputStream *stream,
124 gpointer buffer,
125 gsize count,
126 GCancellable *cancellable,
127 GError **error)
129 CamelIMAPXInputStreamPrivate *priv;
130 GInputStream *base_stream;
131 gssize max;
133 priv = CAMEL_IMAPX_INPUT_STREAM_GET_PRIVATE (stream);
135 base_stream = g_filter_input_stream_get_base_stream (
136 G_FILTER_INPUT_STREAM (stream));
138 if (priv->literal == 0 || count == 0)
139 return 0;
141 max = priv->end - priv->ptr;
142 if (max > 0) {
143 max = MIN (max, priv->literal);
144 max = MIN (max, count);
145 memcpy (buffer, priv->ptr, max);
146 priv->ptr += max;
147 } else {
148 if (error && *error) {
149 g_warning ("%s: Avoiding GIO call with a filled error '%s'", G_STRFUNC, (*error)->message);
150 error = NULL;
153 max = MIN (priv->literal, count);
154 max = g_input_stream_read (
155 base_stream, buffer, max, cancellable, error);
156 if (max <= 0)
157 return max;
160 priv->literal -= max;
162 return max;
165 static gboolean
166 imapx_input_stream_can_poll (GPollableInputStream *pollable_stream)
168 GInputStream *base_stream;
170 base_stream = g_filter_input_stream_get_base_stream (
171 G_FILTER_INPUT_STREAM (pollable_stream));
173 pollable_stream = G_POLLABLE_INPUT_STREAM (base_stream);
175 return g_pollable_input_stream_can_poll (pollable_stream);
178 static gboolean
179 imapx_input_stream_is_readable (GPollableInputStream *pollable_stream)
181 GInputStream *base_stream;
183 base_stream = g_filter_input_stream_get_base_stream (
184 G_FILTER_INPUT_STREAM (pollable_stream));
186 pollable_stream = G_POLLABLE_INPUT_STREAM (base_stream);
188 return g_pollable_input_stream_is_readable (pollable_stream);
191 static GSource *
192 imapx_input_stream_create_source (GPollableInputStream *pollable_stream,
193 GCancellable *cancellable)
195 GInputStream *base_stream;
197 base_stream = g_filter_input_stream_get_base_stream (
198 G_FILTER_INPUT_STREAM (pollable_stream));
200 pollable_stream = G_POLLABLE_INPUT_STREAM (base_stream);
202 return g_pollable_input_stream_create_source (
203 pollable_stream, cancellable);
206 static gssize
207 imapx_input_stream_read_nonblocking (GPollableInputStream *pollable_stream,
208 gpointer buffer,
209 gsize count,
210 GError **error)
212 GInputStream *base_stream;
214 base_stream = g_filter_input_stream_get_base_stream (
215 G_FILTER_INPUT_STREAM (pollable_stream));
217 pollable_stream = G_POLLABLE_INPUT_STREAM (base_stream);
219 if (error && *error) {
220 g_warning ("%s: Avoiding GIO call with a filled error '%s'", G_STRFUNC, (*error)->message);
221 error = NULL;
224 /* XXX The function takes a GCancellable but the class method
225 * does not. Should be okay to pass NULL here since this
226 * is just a pass-through. */
227 return g_pollable_input_stream_read_nonblocking (
228 pollable_stream, buffer, count, NULL, error);
231 static void
232 camel_imapx_input_stream_class_init (CamelIMAPXInputStreamClass *class)
234 GObjectClass *object_class;
235 GInputStreamClass *input_stream_class;
237 g_type_class_add_private (
238 class, sizeof (CamelIMAPXInputStreamPrivate));
240 object_class = G_OBJECT_CLASS (class);
241 object_class->finalize = imapx_input_stream_finalize;
243 input_stream_class = G_INPUT_STREAM_CLASS (class);
244 input_stream_class->read_fn = imapx_input_stream_read;
247 static void
248 camel_imapx_input_stream_pollable_init (GPollableInputStreamInterface *iface)
250 iface->can_poll = imapx_input_stream_can_poll;
251 iface->is_readable = imapx_input_stream_is_readable;
252 iface->create_source = imapx_input_stream_create_source;
253 iface->read_nonblocking = imapx_input_stream_read_nonblocking;
256 static void
257 camel_imapx_input_stream_init (CamelIMAPXInputStream *is)
259 is->priv = CAMEL_IMAPX_INPUT_STREAM_GET_PRIVATE (is);
261 /* +1 is room for appending a 0 if we need to for a token */
262 is->priv->bufsize = 4096;
263 is->priv->buf = g_malloc (is->priv->bufsize + 1);
264 is->priv->ptr = is->priv->end = is->priv->buf;
265 is->priv->tokenbuf = g_malloc (is->priv->bufsize + 1);
268 static void
269 camel_imapx_input_stream_grow (CamelIMAPXInputStream *is,
270 guint len,
271 guchar **bufptr,
272 guchar **tokptr)
274 guchar *oldtok = is->priv->tokenbuf;
275 guchar *oldbuf = is->priv->buf;
277 do {
278 is->priv->bufsize <<= 1;
279 } while (is->priv->bufsize <= len);
281 is->priv->tokenbuf = g_realloc (
282 is->priv->tokenbuf,
283 is->priv->bufsize + 1);
284 if (tokptr)
285 *tokptr = is->priv->tokenbuf + (*tokptr - oldtok);
286 if (is->priv->unget)
287 is->priv->unget_token =
288 is->priv->tokenbuf +
289 (is->priv->unget_token - oldtok);
291 is->priv->buf = g_realloc (is->priv->buf, is->priv->bufsize + 1);
292 is->priv->ptr = is->priv->buf + (is->priv->ptr - oldbuf);
293 is->priv->end = is->priv->buf + (is->priv->end - oldbuf);
294 if (bufptr)
295 *bufptr = is->priv->buf + (*bufptr - oldbuf);
298 G_DEFINE_QUARK (camel-imapx-error-quark, camel_imapx_error)
301 * camel_imapx_input_stream_new:
302 * @base_stream: a pollable #GInputStream
304 * Creates a new #CamelIMAPXInputStream which wraps @base_stream and
305 * parses incoming IMAP lines into tokens.
307 * Returns: a #CamelIMAPXInputStream
309 * Since: 3.12
311 GInputStream *
312 camel_imapx_input_stream_new (GInputStream *base_stream)
314 /* We implement GPollableInputStream by forwarding method calls
315 * to the base stream, so the base stream needs to be pollable. */
316 g_return_val_if_fail (G_IS_POLLABLE_INPUT_STREAM (base_stream), NULL);
318 return g_object_new (
319 CAMEL_TYPE_IMAPX_INPUT_STREAM,
320 "base-stream", base_stream, NULL);
323 /* Returns if there is any data buffered that is ready for processing */
324 gint
325 camel_imapx_input_stream_buffered (CamelIMAPXInputStream *is)
327 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), 0);
329 return is->priv->end - is->priv->ptr;
332 /* FIXME: these should probably handle it themselves,
333 * and get rid of the token interface? */
334 gboolean
335 camel_imapx_input_stream_atom (CamelIMAPXInputStream *is,
336 guchar **data,
337 guint *lenp,
338 GCancellable *cancellable,
339 GError **error)
341 camel_imapx_token_t tok;
342 guchar *p, c;
344 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), FALSE);
345 g_return_val_if_fail (data != NULL, FALSE);
346 g_return_val_if_fail (lenp != NULL, FALSE);
348 /* this is only 'approximate' atom */
349 tok = camel_imapx_input_stream_token (is, data, lenp, cancellable, error);
351 switch (tok) {
352 case IMAPX_TOK_ERROR:
353 return FALSE;
355 case IMAPX_TOK_TOKEN:
356 p = *data;
357 while ((c = *p))
358 *p++ = toupper(c);
359 return TRUE;
361 case IMAPX_TOK_INT:
362 return TRUE;
364 default:
365 g_set_error (
366 error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
367 "expecting atom");
368 return FALSE;
372 /* gets an atom, a quoted_string, or a literal */
373 gboolean
374 camel_imapx_input_stream_astring (CamelIMAPXInputStream *is,
375 guchar **data,
376 GCancellable *cancellable,
377 GError **error)
379 camel_imapx_token_t tok;
380 guchar *p, *e, *start, c;
381 guint len, inlen;
382 gint ret;
384 g_return_val_if_fail (CAMEL_IMAPX_INPUT_STREAM (is), FALSE);
385 g_return_val_if_fail (data != NULL, FALSE);
387 p = is->priv->ptr;
388 e = is->priv->end;
390 if (is->priv->unget) {
391 tok = camel_imapx_input_stream_token (is, data, &len, cancellable, error);
392 } else {
393 /* skip whitespace/prefill buffer */
394 do {
395 while (p >= e ) {
396 is->priv->ptr = p;
397 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
398 return FALSE;
399 p = is->priv->ptr;
400 e = is->priv->end;
402 c = *p++;
403 } while (c == ' ' || c == '\r');
405 if (c == '\"' || c == '{') {
406 tok = camel_imapx_input_stream_token (is, data, &len, cancellable, error);
407 } else {
408 guchar *o, *oe;
410 tok = IMAPX_TOK_STRING;
412 /* <any %x01-7F except "(){ " / %x00-1F / %x7F > */
413 o = is->priv->tokenbuf;
414 oe = is->priv->tokenbuf + is->priv->bufsize - 1;
415 *o++ = c;
416 while (1) {
417 while (p < e) {
418 c = *p++;
419 if (c <= 0x1f || c == 0x7f || c == '(' || c == ')' || c == '{' || c == ' ') {
420 if (c == ' ' || c == '\r')
421 is->priv->ptr = p;
422 else
423 is->priv->ptr = p - 1;
424 *o = 0;
425 *data = is->priv->tokenbuf;
426 return TRUE;
429 if (o >= oe) {
430 camel_imapx_input_stream_grow (is, 0, &p, &o);
431 oe = is->priv->tokenbuf + is->priv->bufsize - 1;
432 e = is->priv->end;
434 *o++ = c;
436 is->priv->ptr = p;
437 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
438 return FALSE;
439 p = is->priv->ptr;
440 e = is->priv->end;
445 switch (tok) {
446 case IMAPX_TOK_ERROR:
447 return FALSE;
449 case IMAPX_TOK_TOKEN:
450 case IMAPX_TOK_STRING:
451 case IMAPX_TOK_INT:
452 return TRUE;
454 case IMAPX_TOK_LITERAL:
455 if (len >= is->priv->bufsize)
456 camel_imapx_input_stream_grow (is, len, NULL, NULL);
457 p = is->priv->tokenbuf;
458 camel_imapx_input_stream_set_literal (is, len);
459 do {
460 ret = camel_imapx_input_stream_getl (
461 is, &start, &inlen, cancellable, error);
462 if (ret < 0)
463 return FALSE;
464 memcpy (p, start, inlen);
465 p += inlen;
466 } while (ret > 0);
467 *p = 0;
468 *data = is->priv->tokenbuf;
469 return TRUE;
471 default:
472 g_set_error (
473 error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
474 "expecting astring");
475 return FALSE;
479 /* check for NIL or (small) quoted_string or literal */
480 gboolean
481 camel_imapx_input_stream_nstring (CamelIMAPXInputStream *is,
482 guchar **data,
483 GCancellable *cancellable,
484 GError **error)
486 camel_imapx_token_t tok;
487 guchar *p, *start;
488 guint len, inlen;
489 gint ret;
491 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), FALSE);
492 g_return_val_if_fail (data != NULL, FALSE);
494 tok = camel_imapx_input_stream_token (is, data, &len, cancellable, error);
496 switch (tok) {
497 case IMAPX_TOK_ERROR:
498 return FALSE;
500 case IMAPX_TOK_STRING:
501 return TRUE;
503 case IMAPX_TOK_LITERAL:
504 if (len >= is->priv->bufsize)
505 camel_imapx_input_stream_grow (is, len, NULL, NULL);
506 p = is->priv->tokenbuf;
507 camel_imapx_input_stream_set_literal (is, len);
508 do {
509 ret = camel_imapx_input_stream_getl (
510 is, &start, &inlen, cancellable, error);
511 if (ret < 0)
512 return FALSE;
513 memcpy (p, start, inlen);
514 p += inlen;
515 } while (ret > 0);
516 *p = 0;
517 *data = is->priv->tokenbuf;
518 return TRUE;
520 case IMAPX_TOK_TOKEN:
521 p = *data;
522 if (toupper (p[0]) == 'N' &&
523 toupper (p[1]) == 'I' &&
524 toupper (p[2]) == 'L' &&
525 p[3] == 0) {
526 *data = NULL;
527 return TRUE;
529 /* fall through */
531 default:
532 g_set_error (
533 error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
534 "expecting nstring");
535 return FALSE;
539 /* parse an nstring as a GBytes */
540 gboolean
541 camel_imapx_input_stream_nstring_bytes (CamelIMAPXInputStream *is,
542 GBytes **out_bytes,
543 gboolean with_progress,
544 GCancellable *cancellable,
545 GError **error)
547 camel_imapx_token_t tok;
548 guchar *token;
549 guint len;
550 GOutputStream *output_stream;
551 gssize bytes_written;
552 gboolean success;
554 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), FALSE);
555 g_return_val_if_fail (out_bytes != NULL, FALSE);
557 *out_bytes = NULL;
559 tok = camel_imapx_input_stream_token (
560 is, &token, &len, cancellable, error);
562 switch (tok) {
563 case IMAPX_TOK_ERROR:
564 return FALSE;
566 case IMAPX_TOK_STRING:
567 *out_bytes = g_bytes_new (token, len);
568 return TRUE;
570 case IMAPX_TOK_LITERAL:
571 /* If len is big, we could
572 * automatically use a file backing. */
573 camel_imapx_input_stream_set_literal (is, len);
574 output_stream = g_memory_output_stream_new_resizable ();
575 if (with_progress && len > 1024) {
576 bytes_written = imapx_splice_with_progress (output_stream, G_INPUT_STREAM (is),
577 len, cancellable, error);
578 if (!g_output_stream_close (output_stream, cancellable, error))
579 bytes_written = -1;
580 } else {
581 bytes_written = g_output_stream_splice (output_stream, G_INPUT_STREAM (is),
582 G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, cancellable, error);
584 success = (bytes_written >= 0);
585 if (success) {
586 *out_bytes =
587 g_memory_output_stream_steal_as_bytes (
588 G_MEMORY_OUTPUT_STREAM (output_stream));
590 g_object_unref (output_stream);
591 return success;
593 case IMAPX_TOK_TOKEN:
594 if (toupper (token[0]) == 'N' &&
595 toupper (token[1]) == 'I' &&
596 toupper (token[2]) == 'L' &&
597 token[3] == 0) {
598 *out_bytes = NULL;
599 return TRUE;
601 /* fall through */
603 default:
604 g_set_error (
605 error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
606 "nstring: token not string");
607 return FALSE;
611 gboolean
612 camel_imapx_input_stream_number (CamelIMAPXInputStream *is,
613 guint64 *number,
614 GCancellable *cancellable,
615 GError **error)
617 camel_imapx_token_t tok;
618 guchar *token;
619 guint len;
621 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), FALSE);
622 g_return_val_if_fail (number != NULL, FALSE);
624 tok = camel_imapx_input_stream_token (
625 is, &token, &len, cancellable, error);
627 switch (tok) {
628 case IMAPX_TOK_ERROR:
629 return FALSE;
631 case IMAPX_TOK_INT:
632 *number = g_ascii_strtoull ((gchar *) token, 0, 10);
633 return TRUE;
635 default:
636 g_set_error (
637 error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
638 "expecting number");
639 return FALSE;
643 gboolean
644 camel_imapx_input_stream_text (CamelIMAPXInputStream *is,
645 guchar **text,
646 GCancellable *cancellable,
647 GError **error)
649 GByteArray *build = g_byte_array_new ();
650 guchar *token;
651 guint len;
652 gint tok;
654 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), FALSE);
655 g_return_val_if_fail (text != NULL, FALSE);
657 while (is->priv->unget > 0) {
658 switch (is->priv->unget_tok) {
659 case IMAPX_TOK_TOKEN:
660 case IMAPX_TOK_STRING:
661 case IMAPX_TOK_INT:
662 g_byte_array_append (
663 build, (guint8 *)
664 is->priv->unget_token,
665 is->priv->unget_len);
666 g_byte_array_append (
667 build, (guint8 *) " ", 1);
668 default: /* invalid, but we'll ignore */
669 break;
671 is->priv->unget--;
674 do {
675 tok = camel_imapx_input_stream_gets (
676 is, &token, &len, cancellable, error);
677 if (tok < 0) {
678 *text = NULL;
679 g_byte_array_free (build, TRUE);
680 return FALSE;
682 if (len)
683 g_byte_array_append (build, token, len);
684 } while (tok > 0);
686 g_byte_array_append (build, (guint8 *) "", 1);
687 *text = build->data;
688 g_byte_array_free (build, FALSE);
690 return TRUE;
693 /* Get one token from the imap stream */
694 camel_imapx_token_t
695 camel_imapx_input_stream_token (CamelIMAPXInputStream *is,
696 guchar **data,
697 guint *len,
698 GCancellable *cancellable,
699 GError **error)
701 register guchar c, *oe;
702 guchar *o, *p, *e;
703 guint literal;
704 gint digits;
705 gboolean is_literal8 = FALSE;
707 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), IMAPX_TOK_ERROR);
708 g_return_val_if_fail (data != NULL, IMAPX_TOK_ERROR);
709 g_return_val_if_fail (len != NULL, IMAPX_TOK_ERROR);
711 if (is->priv->unget > 0) {
712 is->priv->unget--;
713 *data = is->priv->unget_token;
714 *len = is->priv->unget_len;
715 return is->priv->unget_tok;
718 *data = NULL;
719 *len = 0;
721 if (is->priv->literal > 0 && !g_cancellable_is_cancelled (cancellable))
722 g_warning (
723 "stream_token called with literal %d",
724 is->priv->literal);
726 p = is->priv->ptr;
727 e = is->priv->end;
729 /* skip whitespace/prefill buffer */
730 do {
731 while (p >= e ) {
732 is->priv->ptr = p;
733 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
734 return IMAPX_TOK_ERROR;
735 p = is->priv->ptr;
736 e = is->priv->end;
738 c = *p++;
739 } while (c == ' ' || c == '\r');
741 if (c == '~') {
742 if (p >= e) {
743 is->priv->ptr = p;
744 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
745 return IMAPX_TOK_ERROR;
746 p = is->priv->ptr;
747 e = is->priv->end;
750 if (*p == '{') {
751 c = *p++;
752 is_literal8 = TRUE;
756 /*strchr("\n*()[]+", c)*/
757 if (imapx_is_token_char (c)) {
758 is->priv->ptr = p;
759 return c;
760 } else if (c == '{') {
761 literal = 0;
762 *data = p;
763 while (1) {
764 while (p < e) {
765 c = *p++;
766 if (isdigit (c) && literal < (UINT_MAX / 10)) {
767 literal = literal * 10 + (c - '0');
768 } else if (is_literal8 && c == '+') {
769 if (p >= e) {
770 is->priv->ptr = p;
771 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
772 return IMAPX_TOK_ERROR;
773 p = is->priv->ptr;
774 e = is->priv->end;
777 /* The '+' can be only at the end of the literal8 token */
778 if (*p != '}')
779 goto protocol_error;
780 } else if (c == '}') {
781 while (1) {
782 while (p < e) {
783 c = *p++;
784 if (c == '\n') {
785 *len = literal;
786 is->priv->ptr = p;
787 is->priv->literal = literal;
788 return IMAPX_TOK_LITERAL;
791 is->priv->ptr = p;
792 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
793 return IMAPX_TOK_ERROR;
794 p = is->priv->ptr;
795 e = is->priv->end;
797 } else {
798 goto protocol_error;
801 is->priv->ptr = p;
802 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
803 return IMAPX_TOK_ERROR;
804 p = is->priv->ptr;
805 e = is->priv->end;
807 } else if (c == '"') {
808 o = is->priv->tokenbuf;
809 oe = is->priv->tokenbuf + is->priv->bufsize - 1;
810 while (1) {
811 while (p < e) {
812 c = *p++;
813 if (c == '\\') {
814 while (p >= e) {
815 is->priv->ptr = p;
816 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
817 return IMAPX_TOK_ERROR;
818 p = is->priv->ptr;
819 e = is->priv->end;
821 c = *p++;
822 } else if (c == '\"') {
823 is->priv->ptr = p;
824 *o = 0;
825 *data = is->priv->tokenbuf;
826 *len = o - is->priv->tokenbuf;
827 return IMAPX_TOK_STRING;
829 if (o >= oe) {
830 camel_imapx_input_stream_grow (is, 0, &p, &o);
831 oe = is->priv->tokenbuf + is->priv->bufsize - 1;
832 e = is->priv->end;
834 *o++ = c;
836 is->priv->ptr = p;
837 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
838 return IMAPX_TOK_ERROR;
839 p = is->priv->ptr;
840 e = is->priv->end;
842 } else {
843 o = is->priv->tokenbuf;
844 oe = is->priv->tokenbuf + is->priv->bufsize - 1;
845 digits = isdigit (c);
846 *o++ = c;
847 while (1) {
848 while (p < e) {
849 c = *p++;
850 /*if (strchr(" \r\n*()[]+", c) != NULL) {*/
851 if (imapx_is_notid_char (c)) {
852 if (c == ' ' || c == '\r')
853 is->priv->ptr = p;
854 else
855 is->priv->ptr = p - 1;
856 *o = 0;
857 *data = is->priv->tokenbuf;
858 *len = o - is->priv->tokenbuf;
859 return digits ? IMAPX_TOK_INT : IMAPX_TOK_TOKEN;
862 if (o >= oe) {
863 camel_imapx_input_stream_grow (is, 0, &p, &o);
864 oe = is->priv->tokenbuf + is->priv->bufsize - 1;
865 e = is->priv->end;
867 digits &= isdigit (c);
868 *o++ = c;
870 is->priv->ptr = p;
871 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
872 return IMAPX_TOK_ERROR;
873 p = is->priv->ptr;
874 e = is->priv->end;
878 protocol_error:
880 /* Protocol error, skip until next lf? */
881 if (c == '\n')
882 is->priv->ptr = p - 1;
883 else
884 is->priv->ptr = p;
886 g_set_error (error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED, "protocol error");
888 return IMAPX_TOK_ERROR;
891 void
892 camel_imapx_input_stream_ungettoken (CamelIMAPXInputStream *is,
893 camel_imapx_token_t tok,
894 guchar *token,
895 guint len)
897 g_return_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is));
899 is->priv->unget_tok = tok;
900 is->priv->unget_token = token;
901 is->priv->unget_len = len;
902 is->priv->unget++;
905 /* returns -1 on error, 0 if last lot of data, >0 if more remaining */
906 gint
907 camel_imapx_input_stream_gets (CamelIMAPXInputStream *is,
908 guchar **start,
909 guint *len,
910 GCancellable *cancellable,
911 GError **error)
913 gint max;
914 guchar *end;
916 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), -1);
917 g_return_val_if_fail (start != NULL, -1);
918 g_return_val_if_fail (len != NULL, -1);
920 *len = 0;
922 max = is->priv->end - is->priv->ptr;
923 if (max == 0) {
924 max = imapx_input_stream_fill (is, cancellable, error);
925 if (max <= 0)
926 return max;
929 *start = is->priv->ptr;
930 end = memchr (is->priv->ptr, '\n', max);
931 if (end)
932 max = (end - is->priv->ptr) + 1;
933 *start = is->priv->ptr;
934 *len = max;
935 is->priv->ptr += max;
937 return end == NULL ? 1 : 0;
940 void
941 camel_imapx_input_stream_set_literal (CamelIMAPXInputStream *is,
942 guint literal)
944 g_return_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is));
946 is->priv->literal = literal;
949 /* returns -1 on erorr, 0 if last data, >0 if more data left */
950 gint
951 camel_imapx_input_stream_getl (CamelIMAPXInputStream *is,
952 guchar **start,
953 guint *len,
954 GCancellable *cancellable,
955 GError **error)
957 gint max;
959 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), -1);
960 g_return_val_if_fail (start != NULL, -1);
961 g_return_val_if_fail (len != NULL, -1);
963 *len = 0;
965 if (is->priv->literal > 0) {
966 max = is->priv->end - is->priv->ptr;
967 if (max == 0) {
968 max = imapx_input_stream_fill (is, cancellable, error);
969 if (max <= 0)
970 return max;
973 max = MIN (max, is->priv->literal);
974 *start = is->priv->ptr;
975 *len = max;
976 is->priv->ptr += max;
977 is->priv->literal -= max;
980 if (is->priv->literal > 0)
981 return 1;
983 return 0;
986 /* skip the rest of the line of tokens */
987 gboolean
988 camel_imapx_input_stream_skip (CamelIMAPXInputStream *is,
989 GCancellable *cancellable,
990 GError **error)
992 camel_imapx_token_t tok;
993 guchar *token;
994 guint len;
996 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), FALSE);
998 do {
999 tok = camel_imapx_input_stream_token (
1000 is, &token, &len, cancellable, error);
1002 if (tok == IMAPX_TOK_ERROR)
1003 return FALSE;
1005 if (tok == IMAPX_TOK_LITERAL) {
1006 camel_imapx_input_stream_set_literal (is, len);
1007 do {
1008 tok = camel_imapx_input_stream_getl (
1009 is, &token, &len, cancellable, error);
1010 } while (tok > 0);
1012 } while (tok != '\n' && tok >= 0);
1014 return (tok != IMAPX_TOK_ERROR);
1017 gboolean
1018 camel_imapx_input_stream_skip_until (CamelIMAPXInputStream *is,
1019 const gchar *delimiters,
1020 GCancellable *cancellable,
1021 GError **error)
1023 register guchar c;
1024 guchar *p, *e;
1026 g_return_val_if_fail (CAMEL_IS_IMAPX_INPUT_STREAM (is), FALSE);
1028 if (is->priv->unget > 0) {
1029 is->priv->unget--;
1030 return TRUE;
1033 if (is->priv->literal > 0) {
1034 is->priv->literal--;
1035 return TRUE;
1038 p = is->priv->ptr;
1039 e = is->priv->end;
1041 do {
1042 while (p >= e ) {
1043 is->priv->ptr = p;
1044 if (imapx_input_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
1045 return FALSE;
1046 p = is->priv->ptr;
1047 e = is->priv->end;
1049 c = *p++;
1050 } while (c && c != ' ' && c != '\r' && c != '\n' && (!delimiters || !strchr (delimiters, c)));
1052 is->priv->ptr = p;
1054 return TRUE;