Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-imapx-stream.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
2  *
3  * Author:
4  *  Michael Zucchi <notzed@ximian.com>
5  *
6  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of version 2 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <errno.h>
31
32 #include <glib/gi18n-lib.h>
33
34 #include <camel/camel-stream-mem.h>
35
36 #include "camel-imapx-utils.h"
37 #include "camel-imapx-stream.h"
38
39 #define CAMEL_IMAPX_STREAM_GET_PRIVATE(obj) \
40         (G_TYPE_INSTANCE_GET_PRIVATE \
41         ((obj), CAMEL_TYPE_IMAPX_STREAM, CamelIMAPXStreamPrivate))
42
43 #define t(...) camel_imapx_debug(token, __VA_ARGS__)
44 #define io(...) camel_imapx_debug(io, __VA_ARGS__)
45
46 struct _CamelIMAPXStreamPrivate {
47         CamelStream *source;
48
49         guchar *buf, *ptr, *end;
50         guint literal;
51
52         guint unget;
53         camel_imapx_token_t unget_tok;
54         guchar *unget_token;
55         guint unget_len;
56
57         guchar *tokenbuf;
58         guint bufsize;
59 };
60
61 enum {
62         PROP_0,
63         PROP_SOURCE
64 };
65
66 G_DEFINE_TYPE (CamelIMAPXStream, camel_imapx_stream, CAMEL_TYPE_STREAM)
67
68 static gint
69 imapx_stream_fill (CamelIMAPXStream *is,
70                    GCancellable *cancellable,
71                    GError **error)
72 {
73         gint left = 0;
74
75         if (is->priv->source != NULL) {
76                 left = is->priv->end - is->priv->ptr;
77                 memcpy (is->priv->buf, is->priv->ptr, left);
78                 is->priv->end = is->priv->buf + left;
79                 is->priv->ptr = is->priv->buf;
80                 left = camel_stream_read (
81                         is->priv->source,
82                         (gchar *) is->priv->end,
83                         is->priv->bufsize - (is->priv->end - is->priv->buf),
84                         cancellable, error);
85                 if (left > 0) {
86                         is->priv->end += left;
87                         io (is->tagprefix, "camel_imapx_read: buffer is '%.*s'\n", (gint)(is->priv->end - is->priv->ptr), is->priv->ptr);
88                         return is->priv->end - is->priv->ptr;
89                 } else {
90                         io (is->tagprefix, "camel_imapx_read: -1\n");
91                         /* If returning zero, camel_stream_read() doesn't consider
92                          * that to be an error. But we do -- we should only be here
93                          * if we *know* there are data to receive. So set the error
94                          * accordingly */
95                         if (!left)
96                                 g_set_error (
97                                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
98                                         _("Source stream returned no data"));
99                         return -1;
100                 }
101         }
102
103         io (is->tagprefix, "camel_imapx_read: -1\n");
104
105         g_set_error (
106                 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
107                 _("Source stream unavailable"));
108
109         return -1;
110 }
111
112 static void
113 imapx_stream_set_source (CamelIMAPXStream *stream,
114                          CamelStream *source)
115 {
116         g_return_if_fail (CAMEL_IS_STREAM (source));
117         g_return_if_fail (stream->priv->source == NULL);
118
119         stream->priv->source = g_object_ref (source);
120 }
121
122 static void
123 imapx_stream_set_property (GObject *object,
124                            guint property_id,
125                            const GValue *value,
126                            GParamSpec *pspec)
127 {
128         switch (property_id) {
129                 case PROP_SOURCE:
130                         imapx_stream_set_source (
131                                 CAMEL_IMAPX_STREAM (object),
132                                 g_value_get_object (value));
133                         return;
134         }
135
136         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
137 }
138
139 static void
140 imapx_stream_get_property (GObject *object,
141                            guint property_id,
142                            GValue *value,
143                            GParamSpec *pspec)
144 {
145         switch (property_id) {
146                 case PROP_SOURCE:
147                         g_value_take_object (
148                                 value,
149                                 camel_imapx_stream_ref_source (
150                                 CAMEL_IMAPX_STREAM (object)));
151                         return;
152         }
153
154         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
155 }
156
157 static void
158 imapx_stream_dispose (GObject *object)
159 {
160         CamelIMAPXStream *stream = CAMEL_IMAPX_STREAM (object);
161
162         if (stream->priv->source != NULL) {
163                 g_object_unref (stream->priv->source);
164                 stream->priv->source = NULL;
165         }
166
167         /* Chain up to parent's dispose() method. */
168         G_OBJECT_CLASS (camel_imapx_stream_parent_class)->dispose (object);
169 }
170
171 static void
172 imapx_stream_finalize (GObject *object)
173 {
174         CamelIMAPXStream *stream = CAMEL_IMAPX_STREAM (object);
175
176         g_free (stream->priv->buf);
177         g_free (stream->priv->tokenbuf);
178
179         /* Chain up to parent's finalize() method. */
180         G_OBJECT_CLASS (camel_imapx_stream_parent_class)->finalize (object);
181 }
182
183 static gssize
184 imapx_stream_read (CamelStream *stream,
185                    gchar *buffer,
186                    gsize n,
187                    GCancellable *cancellable,
188                    GError **error)
189 {
190         CamelIMAPXStream *is = (CamelIMAPXStream *) stream;
191         gssize max;
192
193         if (is->priv->literal == 0 || n == 0)
194                 return 0;
195
196         max = is->priv->end - is->priv->ptr;
197         if (max > 0) {
198                 max = MIN (max, is->priv->literal);
199                 max = MIN (max, n);
200                 memcpy (buffer, is->priv->ptr, max);
201                 is->priv->ptr += max;
202         } else {
203                 max = MIN (is->priv->literal, n);
204                 max = camel_stream_read (
205                         is->priv->source,
206                         buffer, max, cancellable, error);
207                 if (max <= 0)
208                         return max;
209         }
210
211         io (is->tagprefix, "camel_imapx_read(literal): '%.*s'\n", (gint) max, buffer);
212
213         is->priv->literal -= max;
214
215         return max;
216 }
217
218 static gssize
219 imapx_stream_write (CamelStream *stream,
220                     const gchar *buffer,
221                     gsize n,
222                     GCancellable *cancellable,
223                     GError **error)
224 {
225         CamelIMAPXStream *is = (CamelIMAPXStream *) stream;
226
227         if (g_strstr_len (buffer, n, "LOGIN")) {
228                 io (is->tagprefix, "camel_imapx_write: 'LOGIN...'\n");
229         } else {
230                 io (is->tagprefix, "camel_imapx_write: '%.*s'\n", (gint) n, buffer);
231         }
232
233         return camel_stream_write (
234                 is->priv->source,
235                 buffer, n, cancellable, error);
236 }
237
238 static gint
239 imapx_stream_close (CamelStream *stream,
240                     GCancellable *cancellable,
241                     GError **error)
242 {
243         CamelIMAPXStream *is = (CamelIMAPXStream *) stream;
244
245         return camel_stream_close (is->priv->source, cancellable, error);
246 }
247
248 static gint
249 imapx_stream_flush (CamelStream *stream,
250                     GCancellable *cancellable,
251                     GError **error)
252 {
253         /* nop? */
254         return 0;
255 }
256
257 static gboolean
258 imapx_stream_eos (CamelStream *stream)
259 {
260         CamelIMAPXStream *is = (CamelIMAPXStream *) stream;
261
262         return is->priv->literal == 0;
263 }
264
265 static void
266 camel_imapx_stream_class_init (CamelIMAPXStreamClass *class)
267 {
268         GObjectClass *object_class;
269         CamelStreamClass *stream_class;
270
271         g_type_class_add_private (class, sizeof (CamelIMAPXStreamPrivate));
272
273         object_class = G_OBJECT_CLASS (class);
274         object_class->set_property = imapx_stream_set_property;
275         object_class->get_property = imapx_stream_get_property;
276         object_class->dispose = imapx_stream_dispose;
277         object_class->finalize = imapx_stream_finalize;
278
279         stream_class = CAMEL_STREAM_CLASS (class);
280         stream_class->read = imapx_stream_read;
281         stream_class->write = imapx_stream_write;
282         stream_class->close = imapx_stream_close;
283         stream_class->flush = imapx_stream_flush;
284         stream_class->eos = imapx_stream_eos;
285
286         g_object_class_install_property (
287                 object_class,
288                 PROP_SOURCE,
289                 g_param_spec_object (
290                         "source",
291                         "Source",
292                         "Source stream",
293                         CAMEL_TYPE_STREAM,
294                         G_PARAM_READWRITE |
295                         G_PARAM_CONSTRUCT_ONLY |
296                         G_PARAM_STATIC_STRINGS));
297 }
298
299 static void
300 camel_imapx_stream_init (CamelIMAPXStream *is)
301 {
302         is->priv = CAMEL_IMAPX_STREAM_GET_PRIVATE (is);
303
304         /* +1 is room for appending a 0 if we need to for a token */
305         is->priv->bufsize = 4096;
306         is->priv->buf = g_malloc (is->priv->bufsize + 1);
307         is->priv->ptr = is->priv->end = is->priv->buf;
308         is->priv->tokenbuf = g_malloc (is->priv->bufsize + 1);
309 }
310
311 static void
312 camel_imapx_stream_grow (CamelIMAPXStream *is,
313                          guint len,
314                          guchar **bufptr,
315                          guchar **tokptr)
316 {
317         guchar *oldtok = is->priv->tokenbuf;
318         guchar *oldbuf = is->priv->buf;
319
320         do {
321                 is->priv->bufsize <<= 1;
322         } while (is->priv->bufsize <= len);
323
324         io (is->tagprefix, "Grow imapx buffers to %d bytes\n", is->priv->bufsize);
325
326         is->priv->tokenbuf = g_realloc (
327                 is->priv->tokenbuf,
328                 is->priv->bufsize + 1);
329         if (tokptr)
330                 *tokptr = is->priv->tokenbuf + (*tokptr - oldtok);
331         if (is->priv->unget)
332                 is->priv->unget_token =
333                         is->priv->tokenbuf +
334                         (is->priv->unget_token - oldtok);
335
336         is->priv->buf = g_realloc (is->priv->buf, is->priv->bufsize + 1);
337         is->priv->ptr = is->priv->buf + (is->priv->ptr - oldbuf);
338         is->priv->end = is->priv->buf + (is->priv->end - oldbuf);
339         if (bufptr)
340                 *bufptr = is->priv->buf + (*bufptr - oldbuf);
341 }
342
343 GQuark
344 camel_imapx_error_quark (void)
345 {
346         static GQuark quark = 0;
347
348         if (G_UNLIKELY (quark == 0)) {
349                 const gchar *string = "camel-imapx-error-quark";
350                 quark = g_quark_from_static_string (string);
351         }
352
353         return quark;
354 }
355
356 /**
357  * camel_imapx_stream_new:
358  *
359  * Returns a NULL stream.  A null stream is always at eof, and
360  * always returns success for all reads and writes.
361  *
362  * Returns: the stream
363  **/
364 CamelStream *
365 camel_imapx_stream_new (CamelStream *source)
366 {
367         g_return_val_if_fail (CAMEL_IS_STREAM (source), NULL);
368
369         return g_object_new (
370                 CAMEL_TYPE_IMAPX_STREAM,
371                 "source", source, NULL);
372 }
373
374 CamelStream *
375 camel_imapx_stream_ref_source (CamelIMAPXStream *is)
376 {
377         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), NULL);
378
379         return g_object_ref (is->priv->source);
380 }
381
382 /* Returns if there is any data buffered that is ready for processing */
383 gint
384 camel_imapx_stream_buffered (CamelIMAPXStream *is)
385 {
386         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), 0);
387
388         return is->priv->end - is->priv->ptr;
389 }
390
391 /* FIXME: these should probably handle it themselves,
392  * and get rid of the token interface? */
393 gint
394 camel_imapx_stream_atom (CamelIMAPXStream *is,
395                          guchar **data,
396                          guint *lenp,
397                          GCancellable *cancellable,
398                          GError **error)
399 {
400         guchar *p, c;
401         GError *local_error = NULL;
402
403         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), IMAPX_TOK_ERROR);
404         g_return_val_if_fail (data != NULL, IMAPX_TOK_ERROR);
405         g_return_val_if_fail (lenp != NULL, IMAPX_TOK_ERROR);
406
407         /* this is only 'approximate' atom */
408         switch (camel_imapx_stream_token (is, data, lenp, cancellable, &local_error)) {
409         case IMAPX_TOK_TOKEN:
410                 p = *data;
411                 while ((c = *p))
412                         *p++ = toupper(c);
413         case IMAPX_TOK_INT:
414                 return 0;
415         case IMAPX_TOK_ERROR:
416                 if (local_error != NULL)
417                         g_propagate_error (error, local_error);
418                 return IMAPX_TOK_ERROR;
419         default:
420                 if (local_error == NULL)
421                         g_set_error (error, CAMEL_IMAPX_ERROR, 1, "expecting atom");
422                 else
423                         g_propagate_error (error, local_error);
424                 io (is->tagprefix, "expecting atom!\n");
425                 return IMAPX_TOK_PROTOCOL;
426         }
427 }
428
429 /* gets an atom, a quoted_string, or a literal */
430 gint
431 camel_imapx_stream_astring (CamelIMAPXStream *is,
432                             guchar **data,
433                             GCancellable *cancellable,
434                             GError **error)
435 {
436         guchar *p, *start;
437         guint len, inlen;
438         gint ret;
439         GError *local_error = NULL;
440
441         g_return_val_if_fail (CAMEL_IMAPX_STREAM (is), IMAPX_TOK_ERROR);
442         g_return_val_if_fail (data != NULL, IMAPX_TOK_ERROR);
443
444         switch (camel_imapx_stream_token (is, data, &len, cancellable, &local_error)) {
445         case IMAPX_TOK_TOKEN:
446         case IMAPX_TOK_INT:
447         case IMAPX_TOK_STRING:
448                 return 0;
449         case IMAPX_TOK_LITERAL:
450                 if (len >= is->priv->bufsize)
451                         camel_imapx_stream_grow (is, len, NULL, NULL);
452                 p = is->priv->tokenbuf;
453                 camel_imapx_stream_set_literal (is, len);
454                 do {
455                         ret = camel_imapx_stream_getl (is, &start, &inlen, cancellable, error);
456                         if (ret < 0)
457                                 return ret;
458                         memcpy (p, start, inlen);
459                         p += inlen;
460                 } while (ret > 0);
461                 *p = 0;
462                 *data = is->priv->tokenbuf;
463                 return 0;
464         case IMAPX_TOK_ERROR:
465                 /* wont get unless no exception hanlder*/
466                 if (local_error != NULL)
467                         g_propagate_error (error, local_error);
468                 return IMAPX_TOK_ERROR;
469         default:
470                 if (local_error == NULL)
471                         g_set_error (error, CAMEL_IMAPX_ERROR, 1, "expecting astring");
472                 else
473                         g_propagate_error (error, local_error);
474                 io (is->tagprefix, "expecting astring!\n");
475                 return IMAPX_TOK_PROTOCOL;
476         }
477 }
478
479 /* check for NIL or (small) quoted_string or literal */
480 gint
481 camel_imapx_stream_nstring (CamelIMAPXStream *is,
482                             guchar **data,
483                             GCancellable *cancellable,
484                             GError **error)
485 {
486         guchar *p, *start;
487         guint len, inlen;
488         gint ret;
489         GError *local_error = NULL;
490
491         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), IMAPX_TOK_ERROR);
492         g_return_val_if_fail (data != NULL, IMAPX_TOK_ERROR);
493
494         switch (camel_imapx_stream_token (is, data, &len, cancellable, &local_error)) {
495         case IMAPX_TOK_STRING:
496                 return 0;
497         case IMAPX_TOK_LITERAL:
498                 if (len >= is->priv->bufsize)
499                         camel_imapx_stream_grow (is, len, NULL, NULL);
500                 p = is->priv->tokenbuf;
501                 camel_imapx_stream_set_literal (is, len);
502                 do {
503                         ret = camel_imapx_stream_getl (is, &start, &inlen, cancellable, error);
504                         if (ret < 0)
505                                 return ret;
506                         memcpy (p, start, inlen);
507                         p += inlen;
508                 } while (ret > 0);
509                 *p = 0;
510                 *data = is->priv->tokenbuf;
511                 return 0;
512         case IMAPX_TOK_TOKEN:
513                 p = *data;
514                 if (toupper (p[0]) == 'N' && toupper (p[1]) == 'I' && toupper (p[2]) == 'L' && p[3] == 0) {
515                         *data = NULL;
516                         return 0;
517                 }
518         default:
519                 if (local_error == NULL)
520                         g_set_error (error, CAMEL_IMAPX_ERROR, 1, "expecting nstring");
521                 else
522                         g_propagate_error (error, local_error);
523                 return IMAPX_TOK_PROTOCOL;
524         case IMAPX_TOK_ERROR:
525                 /* we'll never get this unless there are no exception  handlers anyway */
526                 if (local_error != NULL)
527                         g_propagate_error (error, local_error);
528                 return IMAPX_TOK_ERROR;
529
530         }
531 }
532
533 /* parse an nstring as a stream */
534 gint
535 camel_imapx_stream_nstring_stream (CamelIMAPXStream *is,
536                                    CamelStream **stream,
537                                    GCancellable *cancellable,
538                                    GError **error)
539 {
540         guchar *token;
541         guint len;
542         gint ret = 0;
543         CamelStream * mem = NULL;
544         GError *local_error = NULL;
545
546         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), -1);
547         g_return_val_if_fail (stream != NULL, -1);
548
549         *stream = NULL;
550
551         switch (camel_imapx_stream_token (is, &token, &len, cancellable, &local_error)) {
552                 case IMAPX_TOK_STRING:
553                         mem = camel_stream_mem_new_with_buffer ((gchar *) token, len);
554                         *stream = mem;
555                         break;
556                 case IMAPX_TOK_LITERAL:
557                         /* if len is big, we could automatically use a file backing */
558                         camel_imapx_stream_set_literal (is, len);
559                         mem = camel_stream_mem_new ();
560                         if (camel_stream_write_to_stream ((CamelStream *) is, mem, cancellable, error) == -1) {
561                                 g_object_unref (mem);
562                                 ret = -1;
563                                 break;
564                         }
565
566                         g_seekable_seek (
567                                 G_SEEKABLE (mem), 0,
568                                 G_SEEK_SET, NULL, NULL);
569
570                         *stream = mem;
571                         break;
572                 case IMAPX_TOK_TOKEN:
573                         if (toupper (token[0]) == 'N' && toupper (token[1]) == 'I' && toupper (token[2]) == 'L' && token[3] == 0) {
574                                 *stream = NULL;
575                                 break;
576                         }
577                 default:
578                         ret = -1;
579                         if (local_error == NULL)
580                                 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "nstring: token not string");
581                         else
582                                 g_propagate_error (error, local_error);
583         }
584
585         return ret;
586 }
587
588 guint64
589 camel_imapx_stream_number (CamelIMAPXStream *is,
590                            GCancellable *cancellable,
591                            GError **error)
592 {
593         guchar *token;
594         guint len;
595         GError *local_error = NULL;
596
597         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), 0);
598
599         if (camel_imapx_stream_token (is, &token, &len, cancellable, &local_error) != IMAPX_TOK_INT) {
600                 if (local_error == NULL)
601                         g_set_error (error, CAMEL_IMAPX_ERROR, 1, "expecting number");
602                 else
603                         g_propagate_error (error, local_error);
604                 return 0;
605         }
606
607         return strtoull ((gchar *) token, 0, 10);
608 }
609
610 gint
611 camel_imapx_stream_text (CamelIMAPXStream *is,
612                          guchar **text,
613                          GCancellable *cancellable,
614                          GError **error)
615 {
616         GByteArray *build = g_byte_array_new ();
617         guchar *token;
618         guint len;
619         gint tok;
620
621         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), -1);
622         g_return_val_if_fail (text != NULL, -1);
623
624         while (is->priv->unget > 0) {
625                 switch (is->priv->unget_tok) {
626                         case IMAPX_TOK_TOKEN:
627                         case IMAPX_TOK_STRING:
628                         case IMAPX_TOK_INT:
629                                 g_byte_array_append (
630                                         build, (guint8 *)
631                                         is->priv->unget_token,
632                                         is->priv->unget_len);
633                                 g_byte_array_append (
634                                         build, (guint8 *) " ", 1);
635                         default: /* invalid, but we'll ignore */
636                                 break;
637                 }
638                 is->priv->unget--;
639         }
640
641         do {
642                 tok = camel_imapx_stream_gets (is, &token, &len, cancellable, error);
643                 if (tok < 0) {
644                         *text = NULL;
645                         g_byte_array_free (build, TRUE);
646                         return -1;
647                 }
648                 if (len)
649                         g_byte_array_append (build, token, len);
650         } while (tok > 0);
651
652         g_byte_array_append (build, (guint8 *) "", 1);
653         *text = build->data;
654         g_byte_array_free (build, FALSE);
655
656         return 0;
657 }
658
659 /* Get one token from the imap stream */
660 camel_imapx_token_t
661 camel_imapx_stream_token (CamelIMAPXStream *is,
662                           guchar **data,
663                           guint *len,
664                           GCancellable *cancellable,
665                           GError **error)
666 {
667         register guchar c, *oe;
668         guchar *o, *p, *e;
669         guint literal;
670         gint digits;
671
672         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), IMAPX_TOK_ERROR);
673         g_return_val_if_fail (data != NULL, IMAPX_TOK_ERROR);
674         g_return_val_if_fail (len != NULL, IMAPX_TOK_ERROR);
675
676         if (is->priv->unget > 0) {
677                 is->priv->unget--;
678                 *data = is->priv->unget_token;
679                 *len = is->priv->unget_len;
680                 return is->priv->unget_tok;
681         }
682
683         if (is->priv->literal > 0)
684                 g_warning (
685                         "stream_token called with literal %d",
686                         is->priv->literal);
687
688         p = is->priv->ptr;
689         e = is->priv->end;
690
691         /* skip whitespace/prefill buffer */
692         do {
693                 while (p >= e ) {
694                         is->priv->ptr = p;
695                         if (imapx_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
696                                 return IMAPX_TOK_ERROR;
697                         p = is->priv->ptr;
698                         e = is->priv->end;
699                 }
700                 c = *p++;
701         } while (c == ' ' || c == '\r');
702
703         /*strchr("\n*()[]+", c)*/
704         if (imapx_is_token_char (c)) {
705                 is->priv->ptr = p;
706                 t (is->tagprefix, "token '%c'\n", c);
707                 return c;
708         } else if (c == '{') {
709                 literal = 0;
710                 *data = p;
711                 while (1) {
712                         while (p < e) {
713                                 c = *p++;
714                                 if (isdigit (c) && literal < (UINT_MAX / 10)) {
715                                         literal = literal * 10 + (c - '0');
716                                 } else if (c == '}') {
717                                         while (1) {
718                                                 while (p < e) {
719                                                         c = *p++;
720                                                         if (c == '\n') {
721                                                                 *len = literal;
722                                                                 is->priv->ptr = p;
723                                                                 is->priv->literal = literal;
724                                                                 t (is->tagprefix, "token LITERAL %d\n", literal);
725                                                                 return IMAPX_TOK_LITERAL;
726                                                         }
727                                                 }
728                                                 is->priv->ptr = p;
729                                                 if (imapx_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
730                                                         return IMAPX_TOK_ERROR;
731                                                 p = is->priv->ptr;
732                                                 e = is->priv->end;
733                                         }
734                                 } else {
735                                         if (isdigit (c)) {
736                                                 io (is->tagprefix, "Protocol error: literal too big\n");
737                                         } else {
738                                                 io (is->tagprefix, "Protocol error: literal contains invalid gchar %02x '%c'\n", c, isprint (c) ? c : c);
739                                         }
740                                         goto protocol_error;
741                                 }
742                         }
743                         is->priv->ptr = p;
744                         if (imapx_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
745                                 return IMAPX_TOK_ERROR;
746                         p = is->priv->ptr;
747                         e = is->priv->end;
748                 }
749         } else if (c == '"') {
750                 o = is->priv->tokenbuf;
751                 oe = is->priv->tokenbuf + is->priv->bufsize - 1;
752                 while (1) {
753                         while (p < e) {
754                                 c = *p++;
755                                 if (c == '\\') {
756                                         while (p >= e) {
757                                                 is->priv->ptr = p;
758                                                 if (imapx_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
759                                                         return IMAPX_TOK_ERROR;
760                                                 p = is->priv->ptr;
761                                                 e = is->priv->end;
762                                         }
763                                         c = *p++;
764                                 } else if (c == '\"') {
765                                         is->priv->ptr = p;
766                                         *o = 0;
767                                         *data = is->priv->tokenbuf;
768                                         *len = o - is->priv->tokenbuf;
769                                         t (is->tagprefix, "token STRING '%s'\n", is->priv->tokenbuf);
770                                         return IMAPX_TOK_STRING;
771                                 }
772                                 if (c == '\n' || c == '\r') {
773                                         io (is->tagprefix, "Protocol error: truncated string\n");
774                                         goto protocol_error;
775                                 }
776                                 if (o >= oe) {
777                                         camel_imapx_stream_grow (is, 0, &p, &o);
778                                         oe = is->priv->tokenbuf + is->priv->bufsize - 1;
779                                         e = is->priv->end;
780                                 }
781                                 *o++ = c;
782                         }
783                         is->priv->ptr = p;
784                         if (imapx_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
785                                 return IMAPX_TOK_ERROR;
786                         p = is->priv->ptr;
787                         e = is->priv->end;
788                 }
789         } else {
790                 o = is->priv->tokenbuf;
791                 oe = is->priv->tokenbuf + is->priv->bufsize - 1;
792                 digits = isdigit (c);
793                 *o++ = c;
794                 while (1) {
795                         while (p < e) {
796                                 c = *p++;
797                                 /*if (strchr(" \r\n*()[]+", c) != NULL) {*/
798                                 if (imapx_is_notid_char (c)) {
799                                         if (c == ' ' || c == '\r')
800                                                 is->priv->ptr = p;
801                                         else
802                                                 is->priv->ptr = p - 1;
803                                         *o = 0;
804                                         *data = is->priv->tokenbuf;
805                                         *len = o - is->priv->tokenbuf;
806                                         t (is->tagprefix, "token TOKEN '%s'\n", is->priv->tokenbuf);
807                                         return digits ? IMAPX_TOK_INT : IMAPX_TOK_TOKEN;
808                                 }
809
810                                 if (o >= oe) {
811                                         camel_imapx_stream_grow (is, 0, &p, &o);
812                                         oe = is->priv->tokenbuf + is->priv->bufsize - 1;
813                                         e = is->priv->end;
814                                 }
815                                 digits &= isdigit (c);
816                                 *o++ = c;
817                         }
818                         is->priv->ptr = p;
819                         if (imapx_stream_fill (is, cancellable, error) == IMAPX_TOK_ERROR)
820                                 return IMAPX_TOK_ERROR;
821                         p = is->priv->ptr;
822                         e = is->priv->end;
823                 }
824         }
825
826         /* Protocol error, skip until next lf? */
827 protocol_error:
828         io (is->tagprefix, "Got protocol error\n");
829
830         if (c == '\n')
831                 is->priv->ptr = p - 1;
832         else
833                 is->priv->ptr = p;
834
835         g_set_error (error, CAMEL_IMAPX_ERROR, 1, "protocol error");
836         return IMAPX_TOK_PROTOCOL;
837 }
838
839 void
840 camel_imapx_stream_ungettoken (CamelIMAPXStream *is,
841                                camel_imapx_token_t tok,
842                                guchar *token,
843                                guint len)
844 {
845         g_return_if_fail (CAMEL_IS_IMAPX_STREAM (is));
846
847         is->priv->unget_tok = tok;
848         is->priv->unget_token = token;
849         is->priv->unget_len = len;
850         is->priv->unget++;
851 }
852
853 /* returns -1 on error, 0 if last lot of data, >0 if more remaining */
854 gint
855 camel_imapx_stream_gets (CamelIMAPXStream *is,
856                          guchar **start,
857                          guint *len,
858                          GCancellable *cancellable,
859                          GError **error)
860 {
861         gint max;
862         guchar *end;
863
864         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), -1);
865         g_return_val_if_fail (start != NULL, -1);
866         g_return_val_if_fail (len != NULL, -1);
867
868         *len = 0;
869
870         max = is->priv->end - is->priv->ptr;
871         if (max == 0) {
872                 max = imapx_stream_fill (is, cancellable, error);
873                 if (max <= 0)
874                         return max;
875         }
876
877         *start = is->priv->ptr;
878         end = memchr (is->priv->ptr, '\n', max);
879         if (end)
880                 max = (end - is->priv->ptr) + 1;
881         *start = is->priv->ptr;
882         *len = max;
883         is->priv->ptr += max;
884
885         return end == NULL ? 1 : 0;
886 }
887
888 void
889 camel_imapx_stream_set_literal (CamelIMAPXStream *is,
890                                 guint literal)
891 {
892         g_return_if_fail (CAMEL_IS_IMAPX_STREAM (is));
893
894         is->priv->literal = literal;
895 }
896
897 /* returns -1 on erorr, 0 if last data, >0 if more data left */
898 gint
899 camel_imapx_stream_getl (CamelIMAPXStream *is,
900                          guchar **start,
901                          guint *len,
902                          GCancellable *cancellable,
903                          GError **error)
904 {
905         gint max;
906
907         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), -1);
908         g_return_val_if_fail (start != NULL, -1);
909         g_return_val_if_fail (len != NULL, -1);
910
911         *len = 0;
912
913         if (is->priv->literal > 0) {
914                 max = is->priv->end - is->priv->ptr;
915                 if (max == 0) {
916                         max = imapx_stream_fill (is, cancellable, error);
917                         if (max <= 0)
918                                 return max;
919                 }
920
921                 max = MIN (max, is->priv->literal);
922                 *start = is->priv->ptr;
923                 *len = max;
924                 is->priv->ptr += max;
925                 is->priv->literal -= max;
926         }
927
928         if (is->priv->literal > 0)
929                 return 1;
930
931         return 0;
932 }
933
934 /* skip the rest of the line of tokens */
935 gint
936 camel_imapx_stream_skip (CamelIMAPXStream *is,
937                          GCancellable *cancellable,
938                          GError **error)
939 {
940         gint tok;
941         guchar *token;
942         guint len;
943
944         g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), -1);
945
946         do {
947                 tok = camel_imapx_stream_token (is, &token, &len, cancellable, error);
948                 if (tok == IMAPX_TOK_LITERAL) {
949                         camel_imapx_stream_set_literal (is, len);
950                         while ((tok = camel_imapx_stream_getl (is, &token, &len, cancellable, error)) > 0) {
951                                 io (is->tagprefix, "Skip literal data '%.*s'\n", (gint) len, token);
952                         }
953                 }
954         } while (tok != '\n' && tok >= 0);
955
956         if (tok < 0)
957                 return -1;
958
959         return 0;
960 }
961