~ubuntu-branches/ubuntu/karmic/libtinymail/karmic

« back to all changes in this revision

Viewing changes to libtinymail-camel/camel-lite/camel/providers/pop3/camel-pop3-stream.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-10-12 11:21:12 UTC
  • Revision ID: james.westby@ubuntu.com-20071012112112-fod9fs7yrooxjr7i
Tags: upstream-0.0.2
ImportĀ upstreamĀ versionĀ 0.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 
20
 * USA
 
21
 */
 
22
 
 
23
/* This is *identical* to the camel-nntp-stream, so should probably
 
24
   work out a way to merge them */
 
25
 
 
26
 
 
27
#ifdef HAVE_CONFIG_H
 
28
#include <config.h>
 
29
#endif
 
30
 
 
31
#include <errno.h>
 
32
#include <stdio.h>
 
33
#include <string.h>
 
34
 
 
35
#include <glib.h>
 
36
 
 
37
#include "camel-pop3-stream.h"
 
38
 
 
39
extern int camel_verbose_debug;
 
40
#define dd(x) (camel_verbose_debug?(x):0)
 
41
 
 
42
static CamelObjectClass *parent_class = NULL;
 
43
 
 
44
/* Returns the class for a CamelStream */
 
45
#define CS_CLASS(so) CAMEL_POP3_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
 
46
 
 
47
#define CAMEL_POP3_STREAM_SIZE (4096)
 
48
#define CAMEL_POP3_STREAM_LINE (1024) /* maximum line size */
 
49
 
 
50
static int
 
51
stream_fill(CamelPOP3Stream *is)
 
52
{
 
53
        int left = 0;
 
54
 
 
55
        if (is->source) {
 
56
                left = is->end - is->ptr;
 
57
                memcpy(is->buf, is->ptr, left);
 
58
                is->end = is->buf + left;
 
59
                is->ptr = is->buf;
 
60
                left = camel_stream_read(is->source, (char*) is->end, CAMEL_POP3_STREAM_SIZE - (is->end - is->buf));
 
61
                if (left > 0) {
 
62
                        is->end += left;
 
63
                        is->end[0] = '\n';
 
64
                        return is->end - is->ptr;
 
65
                } else {
 
66
                        pop3_debug ("POP3_STREAM_FILL(ERROR): '%s'\n", strerror (errno));
 
67
                        return -1;
 
68
                }
 
69
        }
 
70
 
 
71
        return 0;
 
72
}
 
73
 
 
74
static ssize_t
 
75
stream_read(CamelStream *stream, char *buffer, size_t n)
 
76
{
 
77
        CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
 
78
        char *o, *oe;
 
79
        unsigned char *p, *e, c;
 
80
        int state;
 
81
 
 
82
        if (is->mode != CAMEL_POP3_STREAM_DATA || n == 0)
 
83
                return 0;
 
84
 
 
85
        o = buffer;
 
86
        oe = buffer + n;
 
87
        state = is->state;
 
88
 
 
89
        /* Need to copy/strip '.'s and whatnot */
 
90
        p = is->ptr;
 
91
        e = is->end;
 
92
 
 
93
        switch(state) {
 
94
        state_0:
 
95
        case 0:         /* start of line, always read at least 3 chars */
 
96
                while (e - p < 3) {
 
97
                        is->ptr = p;
 
98
                        if (stream_fill(is) == -1)
 
99
                                return -1;
 
100
                        p = is->ptr;
 
101
                        e = is->end;
 
102
                }
 
103
                if (p[0] == '.') {
 
104
                        if (p[1] == '\r' && p[2] == '\n') {
 
105
                                is->ptr = p+3;
 
106
                                is->mode = CAMEL_POP3_STREAM_EOD;
 
107
                                is->state = 0;
 
108
                                pop3_debug ("POP3_STREAM_READ %d bytes:\n", (int)(o-buffer));
 
109
                                return o-buffer;
 
110
                        }
 
111
                        p++;
 
112
                }
 
113
                state = 1;
 
114
                /* FALLS THROUGH */
 
115
        case 1:         /* looking for next sol */
 
116
                while (o < oe) {
 
117
                        c = *p++;
 
118
                        if (c == '\n') {
 
119
                                /* end of input sentinal check */
 
120
                                if (p > e) {
 
121
                                        is->ptr = e;
 
122
                                        if (stream_fill(is) == -1)
 
123
                                                return -1;
 
124
                                        p = is->ptr;
 
125
                                        e = is->end;
 
126
                                } else {
 
127
                                        *o++ = '\n';
 
128
                                        state = 0;
 
129
                                        goto state_0;
 
130
                                }
 
131
                        } else if (c != '\r') {
 
132
                                *o++ = c;
 
133
                        }
 
134
                }
 
135
                break;
 
136
        }
 
137
 
 
138
        is->ptr = p;
 
139
        is->state = state;
 
140
 
 
141
        pop3_debug ("POP3_STREAM_READ %d bytes\n", (int)(o-buffer));
 
142
 
 
143
        return o-buffer;
 
144
}
 
145
 
 
146
static ssize_t
 
147
stream_write(CamelStream *stream, const char *buffer, size_t n)
 
148
{
 
149
        CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
 
150
 
 
151
        if (strncmp (buffer, "PASS ", 5) != 0) {
 
152
                pop3_debug("POP3_STREAM_WRITE(%d):\n%.*s\n", (int)n, (int)n, buffer);
 
153
        } else {
 
154
                pop3_debug("POP3_STREAM_WRITE(%d):\nPASS xxxxxxxx\n", (int)n);
 
155
        }
 
156
 
 
157
        return camel_stream_write(is->source, buffer, n);
 
158
}
 
159
 
 
160
static int
 
161
stream_close(CamelStream *stream)
 
162
{
 
163
        /* nop? */
 
164
        return 0;
 
165
}
 
166
 
 
167
static int
 
168
stream_flush(CamelStream *stream)
 
169
{
 
170
        /* nop? */
 
171
        return 0;
 
172
}
 
173
 
 
174
static gboolean
 
175
stream_eos(CamelStream *stream)
 
176
{
 
177
        CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
 
178
 
 
179
        return is->mode != CAMEL_POP3_STREAM_DATA;
 
180
}
 
181
 
 
182
static int
 
183
stream_reset(CamelStream *stream)
 
184
{
 
185
        /* nop?  reset literal mode? */
 
186
        return 0;
 
187
}
 
188
 
 
189
static void
 
190
camel_pop3_stream_class_init (CamelStreamClass *camel_pop3_stream_class)
 
191
{
 
192
        CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_pop3_stream_class;
 
193
 
 
194
        parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
 
195
 
 
196
        /* virtual method definition */
 
197
        camel_stream_class->read = stream_read;
 
198
        camel_stream_class->write = stream_write;
 
199
        camel_stream_class->close = stream_close;
 
200
        camel_stream_class->flush = stream_flush;
 
201
        camel_stream_class->eos = stream_eos;
 
202
        camel_stream_class->reset = stream_reset;
 
203
}
 
204
 
 
205
static void
 
206
camel_pop3_stream_init(CamelPOP3Stream *is, CamelPOP3StreamClass *isclass)
 
207
{
 
208
        /* +1 is room for appending a 0 if we need to for a line */
 
209
        is->ptr = is->end = is->buf = g_malloc(CAMEL_POP3_STREAM_SIZE+1);
 
210
        is->lineptr = is->linebuf = g_malloc(CAMEL_POP3_STREAM_LINE+1);
 
211
        is->lineend = is->linebuf + CAMEL_POP3_STREAM_LINE;
 
212
 
 
213
        /* init sentinal */
 
214
        is->ptr[0] = '\n';
 
215
 
 
216
        is->state = 0;
 
217
        is->mode = CAMEL_POP3_STREAM_LINE;
 
218
}
 
219
 
 
220
static void
 
221
camel_pop3_stream_finalise(CamelPOP3Stream *is)
 
222
{
 
223
        g_free(is->buf);
 
224
        g_free(is->linebuf);
 
225
        if (is->source)
 
226
                camel_object_unref((CamelObject *)is->source);
 
227
}
 
228
 
 
229
CamelType
 
230
camel_pop3_stream_get_type (void)
 
231
{
 
232
        static CamelType camel_pop3_stream_type = CAMEL_INVALID_TYPE;
 
233
 
 
234
        if (camel_pop3_stream_type == CAMEL_INVALID_TYPE) {
 
235
                camel_pop3_stream_type = camel_type_register( camel_stream_get_type(),
 
236
                                                            "CamelPOP3Stream",
 
237
                                                            sizeof( CamelPOP3Stream ),
 
238
                                                            sizeof( CamelPOP3StreamClass ),
 
239
                                                            (CamelObjectClassInitFunc) camel_pop3_stream_class_init,
 
240
                                                            NULL,
 
241
                                                            (CamelObjectInitFunc) camel_pop3_stream_init,
 
242
                                                            (CamelObjectFinalizeFunc) camel_pop3_stream_finalise );
 
243
        }
 
244
 
 
245
        return camel_pop3_stream_type;
 
246
}
 
247
 
 
248
/**
 
249
 * camel_pop3_stream_new:
 
250
 *
 
251
 * Returns a NULL stream.  A null stream is always at eof, and
 
252
 * always returns success for all reads and writes.
 
253
 *
 
254
 * Return value: the stream
 
255
 **/
 
256
CamelStream *
 
257
camel_pop3_stream_new(CamelStream *source)
 
258
{
 
259
        CamelPOP3Stream *is;
 
260
 
 
261
        is = (CamelPOP3Stream *)camel_object_new(camel_pop3_stream_get_type ());
 
262
        camel_object_ref((CamelObject *)source);
 
263
        is->source = source;
 
264
 
 
265
        return (CamelStream *)is;
 
266
}
 
267
 
 
268
/* Get one line from the pop3 stream */
 
269
int
 
270
camel_pop3_stream_line(CamelPOP3Stream *is, unsigned char **data, unsigned int *len)
 
271
{
 
272
        register unsigned char c, *p, *o, *oe;
 
273
        int newlen, oldlen;
 
274
        unsigned char *e;
 
275
 
 
276
        if (is->mode == CAMEL_POP3_STREAM_EOD) {
 
277
                *data = is->linebuf;
 
278
                *len = 0;
 
279
                return 0;
 
280
        }
 
281
 
 
282
        o = is->linebuf;
 
283
        oe = is->lineend - 1;
 
284
        p = is->ptr;
 
285
        e = is->end;
 
286
 
 
287
        /* Data mode, convert leading '..' to '.', and stop when we reach a solitary '.' */
 
288
        if (is->mode == CAMEL_POP3_STREAM_DATA) {
 
289
                /* need at least 3 chars in buffer */
 
290
                while (e-p < 3) {
 
291
                        is->ptr = p;
 
292
                        if (stream_fill(is) == -1)
 
293
                                return -1;
 
294
                        p = is->ptr;
 
295
                        e = is->end;
 
296
                }
 
297
 
 
298
                /* check for isolated '.\r\n' or begging of line '.' */
 
299
                if (p[0] == '.') {
 
300
                        if (p[1] == '\r' && p[2] == '\n') {
 
301
                                is->ptr = p+3;
 
302
                                is->mode = CAMEL_POP3_STREAM_EOD;
 
303
                                *data = is->linebuf;
 
304
                                *len = 0;
 
305
                                is->linebuf[0] = 0;
 
306
 
 
307
                                dd(printf("POP3_STREAM_LINE(END)\n"));
 
308
 
 
309
                                return 0;
 
310
                        }
 
311
                        p++;
 
312
                }
 
313
        }
 
314
 
 
315
        while (1) {
 
316
                while (o < oe) {
 
317
                        c = *p++;
 
318
                        if (c == '\n') {
 
319
                                /* sentinal? */
 
320
                                if (p> e) {
 
321
                                        is->ptr = e;
 
322
                                        if (stream_fill(is) == -1)
 
323
                                                return -1;
 
324
                                        p = is->ptr;
 
325
                                        e = is->end;
 
326
                                } else {
 
327
                                        is->ptr = p;
 
328
                                        *data = is->linebuf;
 
329
                                        *len = o - is->linebuf;
 
330
                                        *o = 0;
 
331
 
 
332
                                        dd(printf("POP3_STREAM_LINE(%d): '%s'\n", *len, *data));
 
333
 
 
334
                                        return 1;
 
335
                                }
 
336
                        } else if (c != '\r') {
 
337
                                *o++ = c;
 
338
                        }
 
339
                }
 
340
 
 
341
                /* limit this for bad server data? */
 
342
                oldlen = o - is->linebuf;
 
343
                newlen = (is->lineend - is->linebuf) * 3 / 2;
 
344
                is->lineptr = is->linebuf = g_realloc(is->linebuf, newlen);
 
345
                is->lineend = is->linebuf + newlen;
 
346
                oe = is->lineend - 1;
 
347
                o = is->linebuf + oldlen;
 
348
        }
 
349
 
 
350
        return -1;
 
351
}
 
352
 
 
353
/* returns -1 on error, 0 if last lot of data, >0 if more remaining */
 
354
int camel_pop3_stream_gets(CamelPOP3Stream *is, unsigned char **start, unsigned int *len)
 
355
{
 
356
        int max;
 
357
        unsigned char *end;
 
358
 
 
359
        *len = 0;
 
360
 
 
361
        max = is->end - is->ptr;
 
362
        if (max == 0) {
 
363
                max = stream_fill(is);
 
364
                if (max <= 0)
 
365
                        return max;
 
366
        }
 
367
 
 
368
        *start = is->ptr;
 
369
        end = memchr(is->ptr, '\n', max);
 
370
        if (end)
 
371
                max = (end - is->ptr) + 1;
 
372
        *start = is->ptr;
 
373
        *len = max;
 
374
        is->ptr += max;
 
375
 
 
376
        dd(printf("POP3_STREAM_GETS(%s,%d): '%.*s'\n", end==NULL?"more":"last", *len, (int)*len, *start));
 
377
 
 
378
        return end == NULL?1:0;
 
379
}
 
380
 
 
381
void camel_pop3_stream_set_mode(CamelPOP3Stream *is, camel_pop3_stream_mode_t mode)
 
382
{
 
383
        is->mode = mode;
 
384
}
 
385
 
 
386
/* returns -1 on erorr, 0 if last data, >0 if more data left */
 
387
int camel_pop3_stream_getd(CamelPOP3Stream *is, unsigned char **start, unsigned int *len)
 
388
{
 
389
        unsigned char *p, *e, *s;
 
390
        int state;
 
391
 
 
392
        *len = 0;
 
393
 
 
394
        if (is->mode == CAMEL_POP3_STREAM_EOD)
 
395
                return 0;
 
396
 
 
397
        if (is->mode == CAMEL_POP3_STREAM_LINE) {
 
398
                g_warning("pop3_stream reading data in line mode\n");
 
399
                return 0;
 
400
        }
 
401
 
 
402
        state = is->state;
 
403
        p = is->ptr;
 
404
        e = is->end;
 
405
 
 
406
        while (e - p < 3) {
 
407
                is->ptr = p;
 
408
                if (stream_fill(is) == -1)
 
409
                        return -1;
 
410
                p = is->ptr;
 
411
                e = is->end;
 
412
        }
 
413
 
 
414
        s = p;
 
415
 
 
416
        do {
 
417
                switch(state) {
 
418
                case 0:
 
419
                        /* check leading '.', ... */
 
420
                        if (p[0] == '.') {
 
421
                                if (p[1] == '\r' && p[2] == '\n') {
 
422
                                        is->ptr = p+3;
 
423
                                        *len = p-s;
 
424
                                        *start = s;
 
425
                                        is->mode = CAMEL_POP3_STREAM_EOD;
 
426
                                        is->state = 0;
 
427
 
 
428
                                        dd(printf("POP3_STREAM_GETD(%s,%d): '%.*s'\n", "last", *len, (int)*len, *start));
 
429
 
 
430
                                        return 0;
 
431
                                }
 
432
 
 
433
                                /* If at start, just skip '.', else return data upto '.' but skip it */
 
434
                                if (p == s) {
 
435
                                        s++;
 
436
                                        p++;
 
437
                                } else {
 
438
                                        is->ptr = p+1;
 
439
                                        *len = p-s;
 
440
                                        *start = s;
 
441
                                        is->state = 1;
 
442
 
 
443
                                        dd(printf("POP3_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
 
444
 
 
445
                                        return 1;
 
446
                                }
 
447
                        }
 
448
                        state = 1;
 
449
                case 1:
 
450
                        /* Scan for sentinal */
 
451
                        while ((*p++)!='\n')
 
452
                                ;
 
453
 
 
454
                        if (p > e) {
 
455
                                p = e;
 
456
                        } else {
 
457
                                state = 0;
 
458
                        }
 
459
                        break;
 
460
                }
 
461
        } while ((e-p) >= 3);
 
462
 
 
463
        is->state = state;
 
464
        is->ptr = p;
 
465
        *len = p-s;
 
466
        *start = s;
 
467
 
 
468
        dd(printf("POP3_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
 
469
 
 
470
        return 1;
 
471
}