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

« back to all changes in this revision

Viewing changes to libtinymail-camel/camel-lite/camel/camel-stream-buffer.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
/* camel-stream-buffer.c : Buffer any other other stream
 
4
 *
 
5
 * Authors: Michael Zucchi <notzed@ximian.com>
 
6
 *
 
7
 * Copyright 1999-2003 Ximian, Inc. (www.ximian.com)
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or
 
10
 * modify it under the terms of version 2 of the GNU Lesser General Public
 
11
 * License as published by the Free Software Foundation.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 * GNU Lesser General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU Lesser General Public License
 
19
 * along with this program; if not, write to the Free Software
 
20
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 
21
 * USA
 
22
 */
 
23
 
 
24
#ifdef HAVE_CONFIG_H
 
25
#include <config.h>
 
26
#endif
 
27
 
 
28
#include <errno.h>
 
29
#include <fcntl.h>
 
30
#include <string.h>
 
31
#include <sys/stat.h>
 
32
#include <sys/types.h>
 
33
 
 
34
#include "camel-stream-buffer.h"
 
35
#include "camel-tcp-stream.h"
 
36
 
 
37
static CamelStreamClass *parent_class = NULL;
 
38
 
 
39
enum {
 
40
        BUF_USER = 1<<0,        /* user-supplied buffer, do not free */
 
41
};
 
42
 
 
43
#define BUF_SIZE 1024
 
44
 
 
45
static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n);
 
46
static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
 
47
static int stream_flush (CamelStream *stream);
 
48
static int stream_close (CamelStream *stream);
 
49
static gboolean stream_eos (CamelStream *stream);
 
50
 
 
51
static void init_vbuf(CamelStreamBuffer *sbf, CamelStream *s, CamelStreamBufferMode mode, char *buf, guint32 size);
 
52
static void init(CamelStreamBuffer *sbuf, CamelStream *s, CamelStreamBufferMode mode);
 
53
 
 
54
static void
 
55
camel_stream_buffer_class_init (CamelStreamBufferClass *camel_stream_buffer_class)
 
56
{
 
57
        CamelStreamClass *camel_stream_class = CAMEL_STREAM_CLASS (camel_stream_buffer_class);
 
58
 
 
59
        parent_class = CAMEL_STREAM_CLASS (camel_type_get_global_classfuncs (camel_stream_get_type ()));
 
60
 
 
61
        /* virtual method definition */
 
62
        camel_stream_buffer_class->init = init;
 
63
        camel_stream_buffer_class->init_vbuf = init_vbuf;
 
64
 
 
65
        /* virtual method overload */
 
66
        camel_stream_class->read = stream_read;
 
67
        camel_stream_class->write = stream_write;
 
68
        camel_stream_class->flush = stream_flush;
 
69
        camel_stream_class->close = stream_close;
 
70
        camel_stream_class->eos = stream_eos;
 
71
}
 
72
 
 
73
static void
 
74
camel_stream_buffer_init (gpointer object, gpointer klass)
 
75
{
 
76
        CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (object);
 
77
 
 
78
        sbf->flags = 0;
 
79
        sbf->size = BUF_SIZE;
 
80
        sbf->buf = g_malloc(BUF_SIZE);
 
81
        sbf->ptr = sbf->buf;
 
82
        sbf->end = sbf->buf;
 
83
        sbf->mode = CAMEL_STREAM_BUFFER_READ | CAMEL_STREAM_BUFFER_BUFFER;
 
84
        sbf->stream = 0;
 
85
        sbf->linesize = 80;
 
86
        sbf->linebuf = g_malloc(sbf->linesize);
 
87
}
 
88
 
 
89
static void
 
90
camel_stream_buffer_finalize (CamelObject *object)
 
91
{
 
92
        CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (object);
 
93
 
 
94
        if (!(sbf->flags & BUF_USER)) {
 
95
                g_free(sbf->buf);
 
96
        }
 
97
        if (sbf->stream)
 
98
                camel_object_unref (sbf->stream);
 
99
 
 
100
        g_free(sbf->linebuf);
 
101
}
 
102
 
 
103
 
 
104
CamelType
 
105
camel_stream_buffer_get_type (void)
 
106
{
 
107
        static CamelType camel_stream_buffer_type = CAMEL_INVALID_TYPE;
 
108
 
 
109
        if (camel_stream_buffer_type == CAMEL_INVALID_TYPE)     {
 
110
                camel_stream_buffer_type = camel_type_register (camel_stream_get_type (), "CamelStreamBuffer",
 
111
                                                                sizeof (CamelStreamBuffer),
 
112
                                                                sizeof (CamelStreamBufferClass),
 
113
                                                                (CamelObjectClassInitFunc) camel_stream_buffer_class_init,
 
114
                                                                NULL,
 
115
                                                                (CamelObjectInitFunc) camel_stream_buffer_init,
 
116
                                                                (CamelObjectFinalizeFunc) camel_stream_buffer_finalize);
 
117
        }
 
118
 
 
119
        return camel_stream_buffer_type;
 
120
}
 
121
 
 
122
 
 
123
static void
 
124
set_vbuf(CamelStreamBuffer *sbf, char *buf, CamelStreamBufferMode mode, int size)
 
125
{
 
126
        if (sbf->buf && !(sbf->flags & BUF_USER)) {
 
127
                g_free(sbf->buf);
 
128
        }
 
129
        if (buf) {
 
130
                sbf->buf = (unsigned char*)buf;
 
131
                sbf->flags |= BUF_USER;
 
132
        } else {
 
133
                sbf->buf = g_malloc(size);
 
134
                sbf->flags &= ~BUF_USER;
 
135
        }
 
136
        
 
137
        sbf->ptr = sbf->buf;
 
138
        sbf->end = sbf->buf;
 
139
        sbf->size = size;
 
140
        sbf->mode = mode;
 
141
}
 
142
 
 
143
static void
 
144
init_vbuf(CamelStreamBuffer *sbf, CamelStream *s, CamelStreamBufferMode mode, char *buf, guint32 size)
 
145
{
 
146
        set_vbuf(sbf, buf, mode, size);
 
147
        if (sbf->stream)
 
148
                camel_object_unref (sbf->stream);
 
149
        sbf->stream = s;
 
150
        camel_object_ref (sbf->stream);
 
151
}
 
152
 
 
153
static void
 
154
init(CamelStreamBuffer *sbuf, CamelStream *s, CamelStreamBufferMode mode)
 
155
{
 
156
        init_vbuf(sbuf, s, mode, NULL, BUF_SIZE);
 
157
}
 
158
 
 
159
 
 
160
/**
 
161
 * camel_stream_buffer_new:
 
162
 * @stream: a #CamelStream object to buffer
 
163
 * @mode: Operational mode of buffered stream.
 
164
 *
 
165
 * Create a new buffered stream of another stream.  A default
 
166
 * buffer size (1024 bytes), automatically managed will be used
 
167
 * for buffering.
 
168
 *
 
169
 * See #camel_stream_buffer_new_with_vbuf for details on the
 
170
 * @mode parameter.
 
171
 *
 
172
 * Returns a newly created buffered stream.
 
173
 **/
 
174
CamelStream *
 
175
camel_stream_buffer_new (CamelStream *stream, CamelStreamBufferMode mode)
 
176
{
 
177
        CamelStreamBuffer *sbf;
 
178
 
 
179
        sbf = CAMEL_STREAM_BUFFER (camel_object_new (camel_stream_buffer_get_type ()));
 
180
        CAMEL_STREAM_BUFFER_CLASS (CAMEL_OBJECT_GET_CLASS(sbf))->init (sbf, stream, mode);
 
181
 
 
182
        return CAMEL_STREAM (sbf);
 
183
}
 
184
 
 
185
/**
 
186
 * camel_stream_buffer_new_with_vbuf:
 
187
 * @stream: An existing stream to buffer.
 
188
 * @mode: Mode to buffer in.
 
189
 * @buf: Memory to use for buffering.
 
190
 * @size: Size of buffer to use.
 
191
 *
 
192
 * Create a new stream which buffers another stream, @stream.
 
193
 *
 
194
 * The following values are available for @mode:
 
195
 *
 
196
 * #CAMEL_STREAM_BUFFER_BUFFER, Buffer the input/output in blocks.
 
197
 * #CAMEL_STREAM_BUFFER_NEWLINE, Buffer on newlines (for output).
 
198
 * #CAMEL_STREAM_BUFFER_NONE, Perform no buffering.
 
199
 *
 
200
 * Note that currently this is ignored and #CAMEL_STREAM_BUFFER_BUFFER
 
201
 * is always used.
 
202
 *
 
203
 * In addition, one of the following mode options should be or'd
 
204
 * together with the buffering mode:
 
205
 *
 
206
 * #CAMEL_STREAM_BUFFER_WRITE, Buffer in write mode.
 
207
 * #CAMEL_STREAM_BUFFER_READ, Buffer in read mode.
 
208
 *
 
209
 * Buffering can only be done in one direction for any
 
210
 * buffer instance.
 
211
 *
 
212
 * If @buf is non-NULL, then use the memory pointed to
 
213
 * (for upto @size bytes) as the buffer for all buffering
 
214
 * operations.  It is upto the application to free this buffer.
 
215
 * If @buf is NULL, then allocate and manage @size bytes
 
216
 * for all buffering.
 
217
 *
 
218
 * Return value: A new stream with buffering applied.
 
219
 **/
 
220
CamelStream *
 
221
camel_stream_buffer_new_with_vbuf (CamelStream *stream, CamelStreamBufferMode mode, char *buf, guint32 size)
 
222
{
 
223
        CamelStreamBuffer *sbf;
 
224
        sbf = CAMEL_STREAM_BUFFER (camel_object_new (camel_stream_buffer_get_type ()));
 
225
        CAMEL_STREAM_BUFFER_CLASS (CAMEL_OBJECT_GET_CLASS(sbf))->init_vbuf (sbf, stream, mode, buf, size);
 
226
 
 
227
        return CAMEL_STREAM (sbf);
 
228
}
 
229
 
 
230
static ssize_t
 
231
stream_read (CamelStream *stream, char *buffer, size_t n)
 
232
{
 
233
        CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
 
234
        ssize_t bytes_read = 1;
 
235
        ssize_t bytes_left;
 
236
        char *bptr = buffer;
 
237
 
 
238
        g_return_val_if_fail( (sbf->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_READ, 0);
 
239
 
 
240
        while (n && bytes_read > 0) {
 
241
                bytes_left = sbf->end - sbf->ptr;
 
242
                if (bytes_left < n) {
 
243
                        if (bytes_left > 0) {
 
244
                                memcpy(bptr, sbf->ptr, bytes_left);
 
245
                                n -= bytes_left;
 
246
                                bptr += bytes_left;
 
247
                                sbf->ptr += bytes_left;
 
248
                        }
 
249
                        /* if we are reading a lot, then read directly to the destination buffer */
 
250
                        if (n >= sbf->size/3) {
 
251
                                bytes_read = camel_stream_read(sbf->stream, bptr, n);
 
252
                                if (bytes_read>0) {
 
253
                                        n -= bytes_read;
 
254
                                        bptr += bytes_read;
 
255
                                }
 
256
                        } else {
 
257
                                bytes_read = camel_stream_read(sbf->stream, (char *)sbf->buf, sbf->size);
 
258
                                if (bytes_read>0) {
 
259
                                        size_t bytes_used = bytes_read > n ? n : bytes_read;
 
260
                                        sbf->ptr = sbf->buf;
 
261
                                        sbf->end = sbf->buf+bytes_read;
 
262
                                        memcpy(bptr, sbf->ptr, bytes_used);
 
263
                                        sbf->ptr += bytes_used;
 
264
                                        bptr += bytes_used;
 
265
                                        n -= bytes_used;
 
266
                                }
 
267
                        }
 
268
                } else {
 
269
                        memcpy(bptr, sbf->ptr, n);
 
270
                        sbf->ptr += n;
 
271
                        bptr += n;
 
272
                        n = 0;
 
273
                }
 
274
        }
 
275
 
 
276
        return (ssize_t)(bptr - buffer);
 
277
}
 
278
 
 
279
/* only returns the number passed in, or -1 on an error */
 
280
static ssize_t
 
281
stream_write_all(CamelStream *stream, const char *buffer, size_t n)
 
282
{
 
283
        size_t left = n, w;
 
284
 
 
285
        while (left > 0) {
 
286
                w = camel_stream_write(stream, buffer, left);
 
287
                if (w == -1)
 
288
                        return -1;
 
289
                left -= w;
 
290
                buffer += w;
 
291
        }
 
292
 
 
293
        return n;
 
294
}
 
295
 
 
296
static ssize_t
 
297
stream_write (CamelStream *stream, const char *buffer, size_t n)
 
298
{
 
299
        CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
 
300
        ssize_t total = n;
 
301
        ssize_t left, todo;
 
302
 
 
303
        g_return_val_if_fail( (sbf->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_WRITE, 0);
 
304
 
 
305
        /* first, copy as much as we can */
 
306
        left = sbf->size - (sbf->ptr-sbf->buf);
 
307
        todo = MIN(left, n);
 
308
 
 
309
        memcpy(sbf->ptr, buffer, todo);
 
310
        n -= todo;
 
311
        buffer += todo;
 
312
        sbf->ptr += todo;
 
313
 
 
314
        /* if we've filled the buffer, write it out, reset buffer */
 
315
        if (left == todo) {
 
316
                if (stream_write_all(sbf->stream, (const char *)sbf->buf, sbf->size) == -1)
 
317
                        return -1;
 
318
 
 
319
                sbf->ptr = sbf->buf;
 
320
        }
 
321
 
 
322
        /* if we still have more, write directly, or copy to buffer */
 
323
        if (n > 0) {
 
324
                if (n >= sbf->size/3) {
 
325
                        if (stream_write_all(sbf->stream, buffer, n) == -1)
 
326
                                return -1;
 
327
                } else {
 
328
                        memcpy(sbf->ptr, buffer, n);
 
329
                        sbf->ptr += n;
 
330
                }
 
331
        }
 
332
 
 
333
        return total;
 
334
}
 
335
 
 
336
static int
 
337
stream_flush (CamelStream *stream)
 
338
{
 
339
        CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
 
340
 
 
341
        if ((sbf->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_WRITE) {
 
342
                size_t len = sbf->ptr - sbf->buf;
 
343
                
 
344
                if (camel_stream_write (sbf->stream, (const char *)sbf->buf, len) == -1)
 
345
                        return -1;
 
346
                
 
347
                sbf->ptr = sbf->buf;
 
348
        } else {
 
349
                /* nothing to do for read mode 'flush' */
 
350
        }
 
351
 
 
352
        return camel_stream_flush(sbf->stream);
 
353
}
 
354
 
 
355
static int
 
356
stream_close (CamelStream *stream)
 
357
{
 
358
        CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
 
359
 
 
360
        if (stream_flush(stream) == -1)
 
361
                return -1;
 
362
        return camel_stream_close(sbf->stream);
 
363
}
 
364
 
 
365
static gboolean
 
366
stream_eos (CamelStream *stream)
 
367
{
 
368
        CamelStreamBuffer *sbf = CAMEL_STREAM_BUFFER (stream);
 
369
 
 
370
        return camel_stream_eos(sbf->stream) && sbf->ptr == sbf->end;
 
371
}
 
372
 
 
373
/**
 
374
 * camel_stream_buffer_gets:
 
375
 * @sbf: a #CamelStreamBuffer object
 
376
 * @buf: Memory to write the string to.
 
377
 * @max: Maxmimum number of characters to store.
 
378
 *
 
379
 * Read a line of characters up to the next newline character or
 
380
 * @max-1 characters.
 
381
 *
 
382
 * If the newline character is encountered, then it will be
 
383
 * included in the buffer @buf.  The buffer will be #NUL terminated.
 
384
 *
 
385
 * Returns the number of characters read, or %0 for end of file,
 
386
 * and %-1 on error.
 
387
 **/
 
388
int
 
389
camel_stream_buffer_gets(CamelStreamBuffer *sbf, char *buf, unsigned int max)
 
390
{
 
391
        register char *outptr, *inptr, *inend, c, *outend;
 
392
        int bytes_read;
 
393
 
 
394
        outptr = buf;
 
395
        inptr = (char*)sbf->ptr;
 
396
        inend = (char*)sbf->end;
 
397
        outend = buf+max-1;     /* room for NUL */
 
398
 
 
399
        do {
 
400
                while (inptr<inend && outptr<outend) {
 
401
                        c = *inptr++;
 
402
                        *outptr++ = c;
 
403
                        if (c == '\n') {
 
404
                                *outptr = 0;
 
405
                                sbf->ptr = (unsigned char*) inptr;
 
406
                                return outptr-buf;
 
407
                        }
 
408
                }
 
409
                if (outptr == outend)
 
410
                        break;
 
411
 
 
412
                bytes_read = camel_stream_read (sbf->stream, (char*)sbf->buf, sbf->size);
 
413
                if (bytes_read == -1) {
 
414
                        if (buf == outptr)
 
415
                                return -1;
 
416
                        else
 
417
                                bytes_read = 0;
 
418
                }
 
419
                sbf->ptr = sbf->buf;
 
420
                sbf->end = sbf->buf + bytes_read;
 
421
                inptr = (char*)sbf->ptr;
 
422
                inend = (char*)sbf->end;
 
423
        } while (bytes_read>0);
 
424
 
 
425
        sbf->ptr = (unsigned char*)inptr;
 
426
        *outptr = 0;
 
427
 
 
428
        return (int)(outptr - buf);
 
429
}
 
430
 
 
431
 
 
432
 
 
433
int
 
434
camel_stream_buffer_gets_idle (CamelStreamBuffer *sbf, char *buf, unsigned int max)
 
435
{
 
436
        register char *outptr, *inptr, *inend, c, *outend;
 
437
        int bytes_read;
 
438
 
 
439
        outptr = buf;
 
440
        inptr = (char*)sbf->ptr;
 
441
        inend = (char*)sbf->end;
 
442
        outend = buf+max-1;     /* room for NUL */
 
443
 
 
444
        do {
 
445
                while (inptr<inend && outptr<outend) {
 
446
                        c = *inptr++;
 
447
                        *outptr++ = c;
 
448
                        if (c == '\n') {
 
449
                                *outptr = 0;
 
450
                                sbf->ptr = (unsigned char*) inptr;
 
451
                                return outptr-buf;
 
452
                        }
 
453
                }
 
454
                if (outptr == outend)
 
455
                        break;
 
456
 
 
457
                bytes_read = camel_stream_read_idle (sbf->stream, (char*)sbf->buf, sbf->size);
 
458
                if (bytes_read == -1) {
 
459
                        if (buf == outptr)
 
460
                                return -1;
 
461
                        else
 
462
                                bytes_read = 0;
 
463
                }
 
464
                sbf->ptr = sbf->buf;
 
465
                sbf->end = sbf->buf + bytes_read;
 
466
                inptr = (char*)sbf->ptr;
 
467
                inend = (char*)sbf->end;
 
468
        } while (bytes_read>0);
 
469
 
 
470
        sbf->ptr = (unsigned char*)inptr;
 
471
        *outptr = 0;
 
472
 
 
473
        return (int)(outptr - buf);
 
474
}
 
475
 
 
476
int
 
477
camel_tcp_stream_buffer_gets_nb (CamelStreamBuffer *sbf, char *buf, unsigned int max)
 
478
{
 
479
        register char *outptr, *inptr, *inend, c, *outend;
 
480
        int bytes_read;
 
481
 
 
482
        outptr = buf;
 
483
        inptr = (char*)sbf->ptr;
 
484
        inend = (char*)sbf->end;
 
485
        outend = buf+max-1;     /* room for NUL */
 
486
 
 
487
        do {
 
488
                while (inptr<inend && outptr<outend) {
 
489
                        c = *inptr++;
 
490
                        *outptr++ = c;
 
491
                        if (c == '\n') {
 
492
                                *outptr = 0;
 
493
                                sbf->ptr = (unsigned char*) inptr;
 
494
                                return outptr-buf;
 
495
                        }
 
496
                }
 
497
                if (outptr == outend)
 
498
                        break;
 
499
 
 
500
                bytes_read = camel_tcp_stream_read_nb ((CamelTcpStream *)sbf->stream, (char*)sbf->buf, sbf->size);
 
501
                if (bytes_read == -1) {
 
502
                        if (buf == outptr)
 
503
                                return -1;
 
504
                        else
 
505
                                bytes_read = 0;
 
506
                }
 
507
                sbf->ptr = sbf->buf;
 
508
                sbf->end = sbf->buf + bytes_read;
 
509
                inptr = (char*)sbf->ptr;
 
510
                inend = (char*)sbf->end;
 
511
        } while (bytes_read>0);
 
512
 
 
513
        sbf->ptr = (unsigned char*)inptr;
 
514
        *outptr = 0;
 
515
 
 
516
        return (int)(outptr - buf);
 
517
}
 
518
 
 
519
/**
 
520
 * camel_stream_buffer_read_line: read a complete line from the stream
 
521
 * @sbf: a #CamelStreamBuffer object
 
522
 *
 
523
 * This function reads a complete newline-terminated line from the stream
 
524
 * and returns it in allocated memory. The trailing newline (and carriage
 
525
 * return if any) are not included in the returned string.
 
526
 *
 
527
 * Returns the line read, which the caller must free when done with,
 
528
 * or %NULL on eof. If an error occurs, @ex will be set.
 
529
 **/
 
530
char *
 
531
camel_stream_buffer_read_line (CamelStreamBuffer *sbf)
 
532
{
 
533
        unsigned char *p;
 
534
        int nread;
 
535
 
 
536
        p = sbf->linebuf;
 
537
 
 
538
        while (1) {
 
539
                nread = camel_stream_buffer_gets (sbf, (char*)p, sbf->linesize - (p - sbf->linebuf));
 
540
                if (nread <=0) {
 
541
                        if (p > sbf->linebuf)
 
542
                                break;
 
543
                        return NULL;
 
544
                }
 
545
 
 
546
                p += nread;
 
547
                if (p[-1] == '\n')
 
548
                        break;
 
549
 
 
550
                nread = p - sbf->linebuf;
 
551
                sbf->linesize *= 2;
 
552
                sbf->linebuf = g_realloc (sbf->linebuf, sbf->linesize);
 
553
                p = sbf->linebuf + nread;
 
554
        }
 
555
 
 
556
        p--;
 
557
        if (p > sbf->linebuf && p[-1] == '\r')
 
558
                p--;
 
559
        p[0] = 0;
 
560
 
 
561
        return g_strdup((gchar*)sbf->linebuf);
 
562
}