1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
4 * Michael Zucchi <notzed@ximian.com>
6 * Copyright 1999, 2000 Ximian, Inc. (www.ximian.com)
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.
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.
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
34 #include "camel-stream-mem.h"
36 #include "camel-imapp-exception.h"
37 #include "camel-imapp-stream.h"
42 static void setup_table(void);
44 static CamelObjectClass *parent_class = NULL;
46 /* Returns the class for a CamelStream */
47 #define CS_CLASS(so) CAMEL_IMAPP_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
49 #define CAMEL_IMAPP_STREAM_SIZE (4096)
50 #define CAMEL_IMAPP_STREAM_TOKEN (4096) /* maximum token size */
53 stream_fill(CamelIMAPPStream *is)
58 left = is->end - is->ptr;
59 memcpy(is->buf, is->ptr, left);
60 is->end = is->buf + left;
62 left = camel_stream_read(is->source, is->end, CAMEL_IMAPP_STREAM_SIZE - (is->end - is->buf));
65 io(printf("camel_imapp_read: buffer is '%.*s'\n", is->end - is->ptr, is->ptr));
66 return is->end - is->ptr;
68 io(printf("camel_imapp_read: -1\n"));
73 printf("camel_imapp_read: -1\n");
79 stream_read(CamelStream *stream, char *buffer, size_t n)
81 CamelIMAPPStream *is = (CamelIMAPPStream *)stream;
84 if (is->literal == 0 || n == 0)
87 max = is->end - is->ptr;
89 max = MIN(max, is->literal);
91 memcpy(buffer, is->ptr, max);
94 max = MIN(is->literal, n);
95 max = camel_stream_read(is->source, buffer, max);
106 stream_write(CamelStream *stream, const char *buffer, size_t n)
108 CamelIMAPPStream *is = (CamelIMAPPStream *)stream;
110 return camel_stream_write(is->source, buffer, n);
114 stream_close(CamelStream *stream)
121 stream_flush(CamelStream *stream)
128 stream_eos(CamelStream *stream)
130 CamelIMAPPStream *is = (CamelIMAPPStream *)stream;
132 return is->literal == 0;
136 stream_reset(CamelStream *stream)
138 /* nop? reset literal mode? */
143 camel_imapp_stream_class_init (CamelStreamClass *camel_imapp_stream_class)
145 CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_imapp_stream_class;
147 parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
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;
159 camel_imapp_stream_init(CamelIMAPPStream *is, CamelIMAPPStreamClass *isclass)
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;
168 camel_imapp_stream_finalise(CamelIMAPPStream *is)
172 camel_object_unref((CamelObject *)is->source);
176 camel_imapp_stream_get_type (void)
178 static CamelType camel_imapp_stream_type = CAMEL_INVALID_TYPE;
180 if (camel_imapp_stream_type == CAMEL_INVALID_TYPE) {
182 camel_imapp_stream_type = camel_type_register( camel_stream_get_type(),
184 sizeof( CamelIMAPPStream ),
185 sizeof( CamelIMAPPStreamClass ),
186 (CamelObjectClassInitFunc) camel_imapp_stream_class_init,
188 (CamelObjectInitFunc) camel_imapp_stream_init,
189 (CamelObjectFinalizeFunc) camel_imapp_stream_finalise );
192 return camel_imapp_stream_type;
196 * camel_imapp_stream_new:
198 * Returns a NULL stream. A null stream is always at eof, and
199 * always returns success for all reads and writes.
201 * Return value: the stream
204 camel_imapp_stream_new(CamelStream *source)
206 CamelIMAPPStream *is;
208 is = (CamelIMAPPStream *)camel_object_new(camel_imapp_stream_get_type ());
209 camel_object_ref((CamelObject *)source);
212 return (CamelStream *)is;
219 ATOM_CHAR ::= <any CHAR except atom_specials>
221 atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards /
224 CHAR ::= <any 7-bit US-ASCII character except NUL,
227 CTL ::= <any ASCII control character and DEL,
230 SPACE ::= <ASCII SP, space, 0x20>
232 list_wildcards ::= "%" / "*"
234 quoted_specials ::= <"> / "\"
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,
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)
260 /* could be pregenerated, but this is cheap */
262 unsigned char *chars;
269 static void setup_table(void)
274 for (i=0;i<(int)(sizeof(is_masks)/sizeof(is_masks[0]));i++) {
275 p = is_masks[i].chars;
277 imap_specials[c] |= is_masks[i].mask;
284 skip_ws(CamelIMAPPStream *is, unsigned char *pp, unsigned char *pe)
286 register unsigned char c, *p;
295 if (stream_fill(is) == IMAP_TOK_ERROR)
296 return IMAP_TOK_ERROR;
301 } while (c == ' ' || c == '\r');
310 /* FIXME: these should probably handle it themselves,
311 and get rid of the token interface? */
313 camel_imapp_stream_atom(CamelIMAPPStream *is, unsigned char **data, unsigned int *lenp)
317 /* this is only 'approximate' atom */
318 switch(camel_imapp_stream_token(is, data, lenp)) {
326 return IMAP_TOK_ERROR;
328 camel_exception_throw(1, "expecting atom");
329 printf("expecting atom!\n");
330 return IMAP_TOK_PROTOCOL;
334 /* gets an atom, a quoted_string, or a literal */
336 camel_imapp_stream_astring(CamelIMAPPStream *is, unsigned char **data)
338 unsigned char *p, *start;
339 unsigned int len, inlen;
341 switch(camel_imapp_stream_token(is, data, &len)) {
344 case IMAP_TOK_STRING:
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;
354 camel_imapp_stream_set_literal(is, len);
356 len = camel_imapp_stream_getl(is, &start, &inlen);
359 memcpy(p, start, inlen);
362 *data = is->tokenptr;
365 /* wont get unless no exception hanlder*/
366 return IMAP_TOK_ERROR;
368 camel_exception_throw(1, "expecting astring");
369 printf("expecting astring!\n");
370 return IMAP_TOK_PROTOCOL;
374 /* check for NIL or (small) quoted_string or literal */
376 camel_imapp_stream_nstring(CamelIMAPPStream *is, unsigned char **data)
378 unsigned char *p, *start;
379 unsigned int len, inlen;
381 switch(camel_imapp_stream_token(is, data, &len)) {
382 case IMAP_TOK_STRING:
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;
391 camel_imapp_stream_set_literal(is, len);
393 len = camel_imapp_stream_getl(is, &start, &inlen);
396 memcpy(p, start, inlen);
399 *data = is->tokenptr;
403 if (toupper(p[0]) == 'N' && toupper(p[1]) == 'I' && toupper(p[2]) == 'L' && p[3] == 0) {
408 camel_exception_throw(1, "expecting nstring");
409 return IMAP_TOK_PROTOCOL;
411 /* we'll never get this unless there are no exception handlers anyway */
412 return IMAP_TOK_ERROR;
417 /* parse an nstring as a stream */
419 camel_imapp_stream_nstring_stream(CamelIMAPPStream *is, CamelStream **stream)
420 /* throws IO,PARSE exception */
422 unsigned char *token;
425 CamelStream * volatile mem = NULL;
430 switch(camel_imapp_stream_token(is, &token, &len)) {
431 case IMAP_TOK_STRING:
432 mem = camel_stream_mem_new_with_buffer(token, len);
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);
445 if (toupper(token[0]) == 'N' && toupper(token[1]) == 'I' && toupper(token[2]) == 'L' && token[3] == 0) {
451 camel_exception_throw(1, "nstring: token not string");
455 camel_object_unref((CamelObject *)mem);
456 camel_exception_throw_ex(ex);
459 /* never reaches here anyway */
464 camel_imapp_stream_number(CamelIMAPPStream *is)
466 unsigned char *token;
469 if (camel_imapp_stream_token(is, &token, &len) != IMAP_TOK_INT) {
470 camel_exception_throw(1, "expecting number");
474 return strtoul(token, 0, 10);
478 camel_imapp_stream_text(CamelIMAPPStream *is, unsigned char **text)
480 GByteArray *build = g_byte_array_new();
481 unsigned char *token;
486 while (is->unget > 0) {
487 switch (is->unget_tok) {
489 case IMAP_TOK_STRING:
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 */
500 tok = camel_imapp_stream_gets(is, &token, &len);
502 camel_exception_throw(1, "io error: %s", strerror(errno));
504 g_byte_array_append(build, token, len);
508 g_byte_array_free(build, TRUE);
509 camel_exception_throw_ex(ex);
512 g_byte_array_append(build, "", 1);
514 g_byte_array_free(build, FALSE);
519 /* Get one token from the imap stream */
521 /* throws IO,PARSE exception */
522 camel_imapp_stream_token(CamelIMAPPStream *is, unsigned char **data, unsigned int *len)
524 register unsigned char c, *p, *o, *oe;
526 unsigned int literal;
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;
538 g_warning("stream_token called with literal %d", is->literal);
543 /* skip whitespace/prefill buffer */
547 if (stream_fill(is) == IMAP_TOK_ERROR)
553 } while (c == ' ' || c == '\r');
555 /*strchr("\n*()[]+", c)*/
556 if (imap_is_simple(c)) {
558 t(printf("token '%c'\n", c));
560 } else if (c == '{') {
566 if (isdigit(c) && literal < (UINT_MAX/10)) {
567 literal = literal * 10 + (c - '0');
568 } else if (c == '}') {
575 is->literal = literal;
576 t(printf("token LITERAL %d\n", literal));
577 return IMAP_TOK_LITERAL;
581 if (stream_fill(is) == IMAP_TOK_ERROR)
588 printf("Protocol error: literal too big\n");
590 printf("Protocol error: literal contains invalid char %02x '%c'\n", c, isprint(c)?c:c);
595 if (stream_fill(is) == IMAP_TOK_ERROR)
600 } else if (c == '"') {
602 oe = is->tokenptr + CAMEL_IMAPP_STREAM_TOKEN - 1;
609 if (stream_fill(is) == IMAP_TOK_ERROR)
615 } else if (c == '\"') {
618 *data = is->tokenbuf;
619 *len = o - is->tokenbuf;
620 t(printf("token STRING '%s'\n", is->tokenbuf));
621 return IMAP_TOK_STRING;
624 if (c == '\n' || c == '\r' || o>=oe) {
626 printf("Protocol error: string too long\n");
628 printf("Protocol error: truncated string\n");
635 if (stream_fill(is) == IMAP_TOK_ERROR)
642 oe = is->tokenptr + CAMEL_IMAPP_STREAM_TOKEN - 1;
648 /*if (strchr(" \r\n*()[]+", c) != NULL) {*/
649 if (imap_not_id(c)) {
650 if (c == ' ' || c == '\r')
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;
660 digits &= isdigit(c);
663 printf("Protocol error: token too long\n");
668 if (stream_fill(is) == IMAP_TOK_ERROR)
675 /* Had an i/o erorr */
677 printf("Got io error\n");
678 camel_exception_throw(1, "io error");
679 return IMAP_TOK_ERROR;
681 /* Protocol error, skip until next lf? */
683 printf("Got protocol error\n");
690 camel_exception_throw(1, "protocol error");
691 return IMAP_TOK_PROTOCOL;
695 camel_imapp_stream_ungettoken(CamelIMAPPStream *is, camel_imapp_token_t tok, unsigned char *token, unsigned int len)
697 /*printf("ungettoken: '%c' '%s'\n", tok, token);*/
699 is->unget_token = token;
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)
712 max = is->end - is->ptr;
714 max = stream_fill(is);
720 end = memchr(is->ptr, '\n', max);
722 max = (end - is->ptr) + 1;
727 return end == NULL?1:0;
730 void camel_imapp_stream_set_literal(CamelIMAPPStream *is, unsigned int literal)
732 is->literal = literal;
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)
742 if (is->literal > 0) {
743 max = is->end - is->ptr;
745 max = stream_fill(is);
750 max = MIN(max, is->literal);