Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / imapp / camel-imapp-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 1999, 2000 Ximian, Inc. (www.ximian.com)
7  *
8  * This program 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
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20  * USA
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <ctype.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #include <glib.h>
33
34 #include "camel-stream-mem.h"
35
36 #include "camel-imapp-exception.h"
37 #include "camel-imapp-stream.h"
38
39 #define t(x) 
40 #define io(x) x
41
42 static void setup_table(void);
43
44 static CamelObjectClass *parent_class = NULL;
45
46 /* Returns the class for a CamelStream */
47 #define CS_CLASS(so) CAMEL_IMAPP_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
48
49 #define CAMEL_IMAPP_STREAM_SIZE (4096)
50 #define CAMEL_IMAPP_STREAM_TOKEN (4096) /* maximum token size */
51
52 static int
53 stream_fill(CamelIMAPPStream *is)
54 {
55         int left = 0;
56
57         if (is->source) {
58                 left = is->end - is->ptr;
59                 memcpy(is->buf, is->ptr, left);
60                 is->end = is->buf + left;
61                 is->ptr = is->buf;
62                 left = camel_stream_read(is->source, is->end, CAMEL_IMAPP_STREAM_SIZE - (is->end - is->buf));
63                 if (left > 0) {
64                         is->end += left;
65                         io(printf("camel_imapp_read: buffer is '%.*s'\n", is->end - is->ptr, is->ptr));
66                         return is->end - is->ptr;
67                 } else {
68                         io(printf("camel_imapp_read: -1\n"));
69                         return -1;
70                 }
71         }
72
73         printf("camel_imapp_read: -1\n");
74
75         return -1;
76 }
77
78 static ssize_t
79 stream_read(CamelStream *stream, char *buffer, size_t n)
80 {
81         CamelIMAPPStream *is = (CamelIMAPPStream *)stream;
82         ssize_t max;
83
84         if (is->literal == 0 || n == 0)
85                 return 0;
86
87         max = is->end - is->ptr;
88         if (max > 0) {
89                 max = MIN(max, is->literal);
90                 max = MIN(max, n);
91                 memcpy(buffer, is->ptr, max);
92                 is->ptr += max;
93         } else {
94                 max = MIN(is->literal, n);
95                 max = camel_stream_read(is->source, buffer, max);
96                 if (max <= 0)
97                         return max;
98         }
99
100         is->literal -= max;
101         
102         return max;
103 }
104
105 static ssize_t
106 stream_write(CamelStream *stream, const char *buffer, size_t n)
107 {
108         CamelIMAPPStream *is = (CamelIMAPPStream *)stream;
109
110         return camel_stream_write(is->source, buffer, n);
111 }
112
113 static int
114 stream_close(CamelStream *stream)
115 {
116         /* nop? */
117         return 0;
118 }
119
120 static int
121 stream_flush(CamelStream *stream)
122 {
123         /* nop? */
124         return 0;
125 }
126
127 static gboolean
128 stream_eos(CamelStream *stream)
129 {
130         CamelIMAPPStream *is = (CamelIMAPPStream *)stream;
131
132         return is->literal == 0;
133 }
134
135 static int
136 stream_reset(CamelStream *stream)
137 {
138         /* nop?  reset literal mode? */
139         return 0;
140 }
141
142 static void
143 camel_imapp_stream_class_init (CamelStreamClass *camel_imapp_stream_class)
144 {
145         CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_imapp_stream_class;
146
147         parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
148
149         /* virtual method definition */
150         camel_stream_class->read = stream_read;
151         camel_stream_class->write = stream_write;
152         camel_stream_class->close = stream_close;
153         camel_stream_class->flush = stream_flush;
154         camel_stream_class->eos = stream_eos;
155         camel_stream_class->reset = stream_reset;
156 }
157
158 static void
159 camel_imapp_stream_init(CamelIMAPPStream *is, CamelIMAPPStreamClass *isclass)
160 {
161         /* +1 is room for appending a 0 if we need to for a token */
162         is->ptr = is->end = is->buf = g_malloc(CAMEL_IMAPP_STREAM_SIZE+1);
163         is->tokenptr = is->tokenbuf = g_malloc(CAMEL_IMAPP_STREAM_SIZE+1);
164         is->tokenend = is->tokenbuf + CAMEL_IMAPP_STREAM_SIZE;
165 }
166
167 static void
168 camel_imapp_stream_finalise(CamelIMAPPStream *is)
169 {
170         g_free(is->buf);
171         if (is->source)
172                 camel_object_unref((CamelObject *)is->source);
173 }
174
175 CamelType
176 camel_imapp_stream_get_type (void)
177 {
178         static CamelType camel_imapp_stream_type = CAMEL_INVALID_TYPE;
179
180         if (camel_imapp_stream_type == CAMEL_INVALID_TYPE) {
181                 setup_table();
182                 camel_imapp_stream_type = camel_type_register( camel_stream_get_type(),
183                                                             "CamelIMAPPStream",
184                                                             sizeof( CamelIMAPPStream ),
185                                                             sizeof( CamelIMAPPStreamClass ),
186                                                             (CamelObjectClassInitFunc) camel_imapp_stream_class_init,
187                                                             NULL,
188                                                             (CamelObjectInitFunc) camel_imapp_stream_init,
189                                                             (CamelObjectFinalizeFunc) camel_imapp_stream_finalise );
190         }
191
192         return camel_imapp_stream_type;
193 }
194
195 /**
196  * camel_imapp_stream_new:
197  *
198  * Returns a NULL stream.  A null stream is always at eof, and
199  * always returns success for all reads and writes.
200  *
201  * Return value: the stream
202  **/
203 CamelStream *
204 camel_imapp_stream_new(CamelStream *source)
205 {
206         CamelIMAPPStream *is;
207
208         is = (CamelIMAPPStream *)camel_object_new(camel_imapp_stream_get_type ());
209         camel_object_ref((CamelObject *)source);
210         is->source = source;
211
212         return (CamelStream *)is;
213 }
214
215
216 /*
217  From rfc2060
218
219 ATOM_CHAR       ::= <any CHAR except atom_specials>
220
221 atom_specials   ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards /
222                     quoted_specials
223
224 CHAR            ::= <any 7-bit US-ASCII character except NUL,
225                      0x01 - 0x7f>
226
227 CTL             ::= <any ASCII control character and DEL,
228                         0x00 - 0x1f, 0x7f>
229
230 SPACE           ::= <ASCII SP, space, 0x20>
231
232 list_wildcards  ::= "%" / "*"
233
234 quoted_specials ::= <"> / "\"
235 */
236
237 static unsigned char imap_specials[256] = {
238 /* 00 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
239 /* 10 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
240 /* 20 */0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
241 /* 30 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
242 /* 40 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
243 /* 50 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
244 /* 60 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
245 /* 70 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
246         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
247         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
248         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
249         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
250         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
251         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
252         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
253         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
254 };
255
256 #define imap_is_atom(c) ((imap_specials[(c)&0xff] & 0x01) != 0)
257 #define imap_is_simple(c) ((imap_specials[(c)&0xff] & 0x02) != 0)
258 #define imap_not_id(c) ((imap_specials[(c)&0xff] & 0x04) != 0)
259
260 /* could be pregenerated, but this is cheap */
261 static struct {
262         unsigned char *chars;
263         unsigned char mask;
264 } is_masks[] = {
265         { "\n*()[]+", 2 },
266         { " \r\n()[]+", 4 },
267 };
268
269 static void setup_table(void)
270 {
271         int i;
272         unsigned char *p, c;
273
274         for (i=0;i<(int)(sizeof(is_masks)/sizeof(is_masks[0]));i++) {
275                 p = is_masks[i].chars;
276                 while ((c = *p++))
277                         imap_specials[c] |= is_masks[i].mask;
278         }
279 }
280
281 #if 0
282
283 static int
284 skip_ws(CamelIMAPPStream *is, unsigned char *pp, unsigned char *pe)
285 {
286         register unsigned char c, *p;
287         unsigned char *e;
288
289         p = is->ptr;
290         e = is->end;
291
292         do {
293                 while (p >= e ) {
294                         is->ptr = p;
295                         if (stream_fill(is) == IMAP_TOK_ERROR)
296                                 return IMAP_TOK_ERROR;
297                         p = is->ptr;
298                         e = is->end;
299                 }
300                 c = *p++;
301         } while (c == ' ' || c == '\r');
302
303         is->ptr = p;
304         is->end = e;
305
306         return c;
307 }
308 #endif
309
310 /* FIXME: these should probably handle it themselves,
311    and get rid of the token interface? */
312 int
313 camel_imapp_stream_atom(CamelIMAPPStream *is, unsigned char **data, unsigned int *lenp)
314 {
315         unsigned char *p, c;
316
317         /* this is only 'approximate' atom */
318         switch(camel_imapp_stream_token(is, data, lenp)) {
319         case IMAP_TOK_TOKEN:
320                 p = *data;
321                 while ((c = *p))
322                         *p++ = toupper(c);
323         case IMAP_TOK_INT:
324                 return 0;
325         case IMAP_TOK_ERROR:
326                 return IMAP_TOK_ERROR;
327         default:
328                 camel_exception_throw(1, "expecting atom");
329                 printf("expecting atom!\n");
330                 return IMAP_TOK_PROTOCOL;
331         }
332 }
333
334 /* gets an atom, a quoted_string, or a literal */
335 int
336 camel_imapp_stream_astring(CamelIMAPPStream *is, unsigned char **data)
337 {
338         unsigned char *p, *start;
339         unsigned int len, inlen;
340
341         switch(camel_imapp_stream_token(is, data, &len)) {
342         case IMAP_TOK_TOKEN:
343         case IMAP_TOK_INT:
344         case IMAP_TOK_STRING:
345                 return 0;
346         case IMAP_TOK_LITERAL:
347                 /* FIXME: just grow buffer */
348                 if (len >= CAMEL_IMAPP_STREAM_TOKEN) {
349                         camel_exception_throw(1, "astring: literal too long");
350                         printf("astring too long\n");
351                         return IMAP_TOK_PROTOCOL;
352                 }
353                 p = is->tokenptr;
354                 camel_imapp_stream_set_literal(is, len);
355                 do {
356                         len = camel_imapp_stream_getl(is, &start, &inlen);
357                         if (len < 0)
358                                 return len;
359                         memcpy(p, start, inlen);
360                         p += inlen;
361                 } while (len > 0);
362                 *data = is->tokenptr;
363                 return 0;
364         case IMAP_TOK_ERROR:
365                 /* wont get unless no exception hanlder*/
366                 return IMAP_TOK_ERROR;
367         default:
368                 camel_exception_throw(1, "expecting astring");
369                 printf("expecting astring!\n");
370                 return IMAP_TOK_PROTOCOL;
371         }
372 }
373
374 /* check for NIL or (small) quoted_string or literal */
375 int
376 camel_imapp_stream_nstring(CamelIMAPPStream *is, unsigned char **data)
377 {
378         unsigned char *p, *start;
379         unsigned int len, inlen;
380
381         switch(camel_imapp_stream_token(is, data, &len)) {
382         case IMAP_TOK_STRING:
383                 return 0;
384         case IMAP_TOK_LITERAL:
385                 /* FIXME: just grow buffer */
386                 if (len >= CAMEL_IMAPP_STREAM_TOKEN) {
387                         camel_exception_throw(1, "nstring: literal too long");
388                         return IMAP_TOK_PROTOCOL;
389                 }
390                 p = is->tokenptr;
391                 camel_imapp_stream_set_literal(is, len);
392                 do {
393                         len = camel_imapp_stream_getl(is, &start, &inlen);
394                         if (len < 0)
395                                 return len;
396                         memcpy(p, start, inlen);
397                         p += inlen;
398                 } while (len > 0);
399                 *data = is->tokenptr;
400                 return 0;
401         case IMAP_TOK_TOKEN:
402                 p = *data;
403                 if (toupper(p[0]) == 'N' && toupper(p[1]) == 'I' && toupper(p[2]) == 'L' && p[3] == 0) {
404                         *data = NULL;
405                         return 0;
406                 }
407         default:
408                 camel_exception_throw(1, "expecting nstring");
409                 return IMAP_TOK_PROTOCOL;
410         case IMAP_TOK_ERROR:
411                 /* we'll never get this unless there are no exception  handlers anyway */
412                 return IMAP_TOK_ERROR;
413
414         }
415 }
416
417 /* parse an nstring as a stream */
418 int
419 camel_imapp_stream_nstring_stream(CamelIMAPPStream *is, CamelStream **stream)
420 /* throws IO,PARSE exception */
421 {
422         unsigned char *token;
423         unsigned int len;
424         int ret = 0;
425         CamelStream * volatile mem = NULL;
426
427         *stream = NULL;
428
429         CAMEL_TRY {
430                 switch(camel_imapp_stream_token(is, &token, &len)) {
431                 case IMAP_TOK_STRING:
432                         mem = camel_stream_mem_new_with_buffer(token, len);
433                         *stream = mem;
434                         break;
435                 case IMAP_TOK_LITERAL:
436                         /* if len is big, we could automatically use a file backing */
437                         camel_imapp_stream_set_literal(is, len);
438                         mem = camel_stream_mem_new();
439                         if (camel_stream_write_to_stream((CamelStream *)is, mem) == -1)
440                                 camel_exception_throw(1, "nstring: io error: %s", strerror(errno));
441                         camel_stream_reset(mem);
442                         *stream = mem;
443                         break;
444                 case IMAP_TOK_TOKEN:
445                         if (toupper(token[0]) == 'N' && toupper(token[1]) == 'I' && toupper(token[2]) == 'L' && token[3] == 0) {
446                                 *stream = NULL;
447                                 break;
448                         }
449                 default:
450                         ret = -1;
451                         camel_exception_throw(1, "nstring: token not string");
452                 }
453         } CAMEL_CATCH(ex) {
454                 if (mem)
455                         camel_object_unref((CamelObject *)mem);
456                 camel_exception_throw_ex(ex);
457         } CAMEL_DONE;
458
459         /* never reaches here anyway */
460         return ret;
461 }
462
463 guint32
464 camel_imapp_stream_number(CamelIMAPPStream *is)
465 {
466         unsigned char *token;
467         unsigned int len;
468
469         if (camel_imapp_stream_token(is, &token, &len) != IMAP_TOK_INT) {
470                 camel_exception_throw(1, "expecting number");
471                 return 0;
472         }
473
474         return strtoul(token, 0, 10);
475 }
476
477 int
478 camel_imapp_stream_text(CamelIMAPPStream *is, unsigned char **text)
479 {
480         GByteArray *build = g_byte_array_new();
481         unsigned char *token;
482         unsigned int len;
483         int tok;
484
485         CAMEL_TRY {
486                 while (is->unget > 0) {
487                         switch (is->unget_tok) {
488                         case IMAP_TOK_TOKEN:
489                         case IMAP_TOK_STRING:
490                         case IMAP_TOK_INT:
491                                 g_byte_array_append(build, is->unget_token, is->unget_len);
492                                 g_byte_array_append(build, " ", 1);
493                         default: /* invalid, but we'll ignore */
494                                 break;
495                         }
496                         is->unget--;
497                 }
498
499                 do {
500                         tok = camel_imapp_stream_gets(is, &token, &len);
501                         if (tok < 0)
502                                 camel_exception_throw(1, "io error: %s", strerror(errno));
503                         if (len)
504                                 g_byte_array_append(build, token, len);
505                 } while (tok > 0);
506         } CAMEL_CATCH(ex) {
507                 *text = NULL;
508                 g_byte_array_free(build, TRUE);
509                 camel_exception_throw_ex(ex);
510         } CAMEL_DONE;
511
512         g_byte_array_append(build, "", 1);
513         *text = build->data;
514         g_byte_array_free(build, FALSE);
515
516         return 0;
517 }
518
519 /* Get one token from the imap stream */
520 camel_imapp_token_t
521 /* throws IO,PARSE exception */
522 camel_imapp_stream_token(CamelIMAPPStream *is, unsigned char **data, unsigned int *len)
523 {
524         register unsigned char c, *p, *o, *oe;
525         unsigned char *e;
526         unsigned int literal;
527         int digits;
528
529         if (is->unget > 0) {
530                 is->unget--;
531                 *data = is->unget_token;
532                 *len = is->unget_len;
533                 /*printf("token UNGET '%c' %s\n", is->unget_tok, is->unget_token);*/
534                 return is->unget_tok;
535         }
536
537         if (is->literal > 0)
538                 g_warning("stream_token called with literal %d", is->literal);
539
540         p = is->ptr;
541         e = is->end;
542
543         /* skip whitespace/prefill buffer */
544         do {
545                 while (p >= e ) {
546                         is->ptr = p;
547                         if (stream_fill(is) == IMAP_TOK_ERROR)
548                                 goto io_error;
549                         p = is->ptr;
550                         e = is->end;
551                 }
552                 c = *p++;
553         } while (c == ' ' || c == '\r');
554
555         /*strchr("\n*()[]+", c)*/
556         if (imap_is_simple(c)) {
557                 is->ptr = p;
558                 t(printf("token '%c'\n", c));
559                 return c;
560         } else if (c == '{') {
561                 literal = 0;
562                 *data = p;
563                 while (1) {
564                         while (p < e) {
565                                 c = *p++;
566                                 if (isdigit(c) && literal < (UINT_MAX/10)) {
567                                         literal = literal * 10 + (c - '0');
568                                 } else if (c == '}') {
569                                         while (1) {
570                                                 while (p < e) {
571                                                         c = *p++;
572                                                         if (c == '\n') {
573                                                                 *len = literal;
574                                                                 is->ptr = p;
575                                                                 is->literal = literal;
576                                                                 t(printf("token LITERAL %d\n", literal));
577                                                                 return IMAP_TOK_LITERAL;
578                                                         }
579                                                 }
580                                                 is->ptr = p;
581                                                 if (stream_fill(is) == IMAP_TOK_ERROR)
582                                                         goto io_error;
583                                                 p = is->ptr;
584                                                 e = is->end;
585                                         }
586                                 } else {
587                                         if (isdigit(c))
588                                                 printf("Protocol error: literal too big\n");
589                                         else
590                                                 printf("Protocol error: literal contains invalid char %02x '%c'\n", c, isprint(c)?c:c);
591                                         goto protocol_error;
592                                 }
593                         }
594                         is->ptr = p;
595                         if (stream_fill(is) == IMAP_TOK_ERROR)
596                                 goto io_error;
597                         p = is->ptr;
598                         e = is->end;
599                 }
600         } else if (c == '"') {
601                 o = is->tokenptr;
602                 oe = is->tokenptr + CAMEL_IMAPP_STREAM_TOKEN - 1;
603                 while (1) {
604                         while (p < e) {
605                                 c = *p++;
606                                 if (c == '\\') {
607                                         while (p >= e) {
608                                                 is->ptr = p;
609                                                 if (stream_fill(is) == IMAP_TOK_ERROR)
610                                                         goto io_error;
611                                                 p = is->ptr;
612                                                 e = is->end;
613                                         }
614                                         c = *p++;
615                                 } else if (c == '\"') {
616                                         is->ptr = p;
617                                         *o = 0;
618                                         *data = is->tokenbuf;
619                                         *len = o - is->tokenbuf;
620                                         t(printf("token STRING '%s'\n", is->tokenbuf));
621                                         return IMAP_TOK_STRING;
622                                 }
623
624                                 if (c == '\n' || c == '\r' || o>=oe) {
625                                         if (o >= oe)
626                                                 printf("Protocol error: string too long\n");
627                                         else
628                                                 printf("Protocol error: truncated string\n");
629                                         goto protocol_error;
630                                 } else {
631                                         *o++ = c;
632                                 }
633                         }
634                         is->ptr = p;
635                         if (stream_fill(is) == IMAP_TOK_ERROR)
636                                 goto io_error;
637                         p = is->ptr;
638                         e = is->end;
639                 }
640         } else {
641                 o = is->tokenptr;
642                 oe = is->tokenptr + CAMEL_IMAPP_STREAM_TOKEN - 1;
643                 digits = isdigit(c);
644                 *o++ = c;
645                 while (1) {
646                         while (p < e) {
647                                 c = *p++;
648                                 /*if (strchr(" \r\n*()[]+", c) != NULL) {*/
649                                 if (imap_not_id(c)) {
650                                         if (c == ' ' || c == '\r')
651                                                 is->ptr = p;
652                                         else
653                                                 is->ptr = p-1;
654                                         *o = 0;
655                                         *data = is->tokenbuf;
656                                         *len = o - is->tokenbuf;
657                                         t(printf("token TOKEN '%s'\n", is->tokenbuf));
658                                         return digits?IMAP_TOK_INT:IMAP_TOK_TOKEN;
659                                 } else if (o < oe) {
660                                         digits &= isdigit(c);
661                                         *o++ = c;
662                                 } else {
663                                         printf("Protocol error: token too long\n");
664                                         goto protocol_error;
665                                 }
666                         }
667                         is->ptr = p;
668                         if (stream_fill(is) == IMAP_TOK_ERROR)
669                                 goto io_error;
670                         p = is->ptr;
671                         e = is->end;
672                 }
673         }
674
675         /* Had an i/o erorr */
676 io_error:
677         printf("Got io error\n");
678         camel_exception_throw(1, "io error");
679         return IMAP_TOK_ERROR;
680
681         /* Protocol error, skip until next lf? */
682 protocol_error:
683         printf("Got protocol error\n");
684
685         if (c == '\n')
686                 is->ptr = p-1;
687         else
688                 is->ptr = p;
689
690         camel_exception_throw(1, "protocol error");
691         return IMAP_TOK_PROTOCOL;
692 }
693
694 void
695 camel_imapp_stream_ungettoken(CamelIMAPPStream *is, camel_imapp_token_t tok, unsigned char *token, unsigned int len)
696 {
697         /*printf("ungettoken: '%c' '%s'\n", tok, token);*/
698         is->unget_tok = tok;
699         is->unget_token = token;
700         is->unget_len = len;
701         is->unget++;
702 }
703
704 /* returns -1 on error, 0 if last lot of data, >0 if more remaining */
705 int camel_imapp_stream_gets(CamelIMAPPStream *is, unsigned char **start, unsigned int *len)
706 {
707         int max;
708         unsigned char *end;
709
710         *len = 0;
711
712         max = is->end - is->ptr;
713         if (max == 0) {
714                 max = stream_fill(is);
715                 if (max <= 0)
716                         return max;
717         }
718
719         *start = is->ptr;
720         end = memchr(is->ptr, '\n', max);
721         if (end)
722                 max = (end - is->ptr) + 1;
723         *start = is->ptr;
724         *len = max;
725         is->ptr += max;
726
727         return end == NULL?1:0;
728 }
729
730 void camel_imapp_stream_set_literal(CamelIMAPPStream *is, unsigned int literal)
731 {
732         is->literal = literal;
733 }
734
735 /* returns -1 on erorr, 0 if last data, >0 if more data left */
736 int camel_imapp_stream_getl(CamelIMAPPStream *is, unsigned char **start, unsigned int *len)
737 {
738         int max;
739
740         *len = 0;
741
742         if (is->literal > 0) {
743                 max = is->end - is->ptr;
744                 if (max == 0) {
745                         max = stream_fill(is);
746                         if (max <= 0)
747                                 return max;
748                 }
749
750                 max = MIN(max, is->literal);
751                 *start = is->ptr;
752                 *len = max;
753                 is->ptr += max;
754                 is->literal -= max;
755         }
756
757         if (is->literal > 0)
758                 return 1;
759
760         return 0;
761 }