Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-stream-buffer.c
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 (C) 1999-2008 Novell, Inc. (www.novell.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
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
36 #define CAMEL_STREAM_BUFFER_GET_PRIVATE(obj) \
37         (G_TYPE_INSTANCE_GET_PRIVATE \
38         ((obj), CAMEL_TYPE_STREAM_BUFFER, CamelStreamBufferPrivate))
39
40 struct _CamelStreamBufferPrivate {
41
42         CamelStream *stream;
43
44         guchar *buf, *ptr, *end;
45         gint size;
46
47         guchar *linebuf;        /* for reading lines at a time */
48         gint linesize;
49
50         CamelStreamBufferMode mode;
51         guint flags;
52 };
53
54 G_DEFINE_TYPE (CamelStreamBuffer, camel_stream_buffer, CAMEL_TYPE_STREAM)
55
56 enum {
57         BUF_USER = 1 << 0       /* user-supplied buffer, do not free */
58 };
59
60 #define BUF_SIZE 1024
61
62 /* only returns the number passed in, or -1 on an error */
63 static gssize
64 stream_write_all (CamelStream *stream,
65                   const gchar *buffer,
66                   gsize n,
67                   GCancellable *cancellable,
68                   GError **error)
69 {
70         gsize left = n, w;
71
72         while (left > 0) {
73                 w = camel_stream_write (
74                         stream, buffer, left, cancellable, error);
75                 if (w == -1)
76                         return -1;
77                 left -= w;
78                 buffer += w;
79         }
80
81         return n;
82 }
83
84 static void
85 set_vbuf (CamelStreamBuffer *stream,
86           gchar *buf,
87           CamelStreamBufferMode mode,
88           gint size)
89 {
90         CamelStreamBufferPrivate *priv;
91
92         priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
93
94         if (priv->buf && !(priv->flags & BUF_USER))
95                 g_free (priv->buf);
96
97         if (buf) {
98                 priv->buf = (guchar *) buf;
99                 priv->flags |= BUF_USER;
100         } else {
101                 priv->buf = g_malloc (size);
102                 priv->flags &= ~BUF_USER;
103         }
104
105         priv->ptr = priv->buf;
106         priv->end = priv->buf;
107         priv->size = size;
108         priv->mode = mode;
109 }
110
111 static void
112 stream_buffer_dispose (GObject *object)
113 {
114         CamelStreamBufferPrivate *priv;
115
116         priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (object);
117
118         if (priv->stream != NULL) {
119                 g_object_unref (priv->stream);
120                 priv->stream = NULL;
121         }
122
123         /* Chain up to parent's dispose() method. */
124         G_OBJECT_CLASS (camel_stream_buffer_parent_class)->dispose (object);
125 }
126
127 static void
128 stream_buffer_finalize (GObject *object)
129 {
130         CamelStreamBufferPrivate *priv;
131
132         priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (object);
133
134         if (!(priv->flags & BUF_USER))
135                 g_free (priv->buf);
136
137         g_free (priv->linebuf);
138
139         /* Chain up to parent's finalize() method. */
140         G_OBJECT_CLASS (camel_stream_buffer_parent_class)->finalize (object);
141 }
142
143 static gssize
144 stream_buffer_read (CamelStream *stream,
145                     gchar *buffer,
146                     gsize n,
147                     GCancellable *cancellable,
148                     GError **error)
149 {
150         CamelStreamBufferPrivate *priv;
151         gssize bytes_read = 1;
152         gssize bytes_left;
153         gchar *bptr = buffer;
154         GError *local_error = NULL;
155
156         priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
157
158         g_return_val_if_fail (
159                 (priv->mode & CAMEL_STREAM_BUFFER_MODE) ==
160                 CAMEL_STREAM_BUFFER_READ, 0);
161
162         while (n && bytes_read > 0) {
163                 bytes_left = priv->end - priv->ptr;
164                 if (bytes_left < n) {
165                         if (bytes_left > 0) {
166                                 memcpy (bptr, priv->ptr, bytes_left);
167                                 n -= bytes_left;
168                                 bptr += bytes_left;
169                                 priv->ptr += bytes_left;
170                         }
171                         /* if we are reading a lot, then read directly to the destination buffer */
172                         if (n >= priv->size / 3) {
173                                 bytes_read = camel_stream_read (
174                                         priv->stream, bptr, n,
175                                         cancellable, &local_error);
176                                 if (bytes_read > 0) {
177                                         n -= bytes_read;
178                                         bptr += bytes_read;
179                                 }
180                         } else {
181                                 bytes_read = camel_stream_read (
182                                         priv->stream, (gchar *) priv->buf,
183                                         priv->size, cancellable, &local_error);
184                                 if (bytes_read > 0) {
185                                         gsize bytes_used = bytes_read > n ? n : bytes_read;
186                                         priv->ptr = priv->buf;
187                                         priv->end = priv->buf + bytes_read;
188                                         memcpy (bptr, priv->ptr, bytes_used);
189                                         priv->ptr += bytes_used;
190                                         bptr += bytes_used;
191                                         n -= bytes_used;
192                                 }
193                         }
194                 } else {
195                         memcpy (bptr, priv->ptr, n);
196                         priv->ptr += n;
197                         bptr += n;
198                         n = 0;
199                 }
200         }
201
202         /* If camel_stream_read() failed but we managed to read some data
203          * before the failure, discard the error and return the number of
204          * bytes read.  If we didn't read any data, propagate the error. */
205         if (local_error != NULL) {
206                 if (bptr > buffer)
207                         g_clear_error (&local_error);
208                 else {
209                         g_propagate_error (error, local_error);
210                         return -1;
211                 }
212         }
213
214         return (gssize)(bptr - buffer);
215 }
216
217 static gssize
218 stream_buffer_write (CamelStream *stream,
219                      const gchar *buffer,
220                      gsize n,
221                      GCancellable *cancellable,
222                      GError **error)
223 {
224         CamelStreamBufferPrivate *priv;
225         gssize total = n;
226         gssize left, todo;
227
228         priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
229
230         g_return_val_if_fail (
231                 (priv->mode & CAMEL_STREAM_BUFFER_MODE) ==
232                 CAMEL_STREAM_BUFFER_WRITE, 0);
233
234         /* first, copy as much as we can */
235         left = priv->size - (priv->ptr - priv->buf);
236         todo = MIN (left, n);
237
238         memcpy (priv->ptr, buffer, todo);
239         n -= todo;
240         buffer += todo;
241         priv->ptr += todo;
242
243         /* if we've filled the buffer, write it out, reset buffer */
244         if (left == todo) {
245                 if (stream_write_all (
246                         priv->stream, (gchar *) priv->buf,
247                         priv->size, cancellable, error) == -1)
248                         return -1;
249
250                 priv->ptr = priv->buf;
251         }
252
253         /* if we still have more, write directly, or copy to buffer */
254         if (n > 0) {
255                 if (n >= priv->size / 3) {
256                         if (stream_write_all (
257                                 priv->stream, buffer, n,
258                                 cancellable, error) == -1)
259                                 return -1;
260                 } else {
261                         memcpy (priv->ptr, buffer, n);
262                         priv->ptr += n;
263                 }
264         }
265
266         return total;
267 }
268
269 static gint
270 stream_buffer_flush (CamelStream *stream,
271                      GCancellable *cancellable,
272                      GError **error)
273 {
274         CamelStreamBufferPrivate *priv;
275
276         priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
277
278         if ((priv->mode & CAMEL_STREAM_BUFFER_MODE) == CAMEL_STREAM_BUFFER_WRITE) {
279                 gsize len = priv->ptr - priv->buf;
280
281                 if (camel_stream_write (
282                         priv->stream, (gchar *) priv->buf,
283                         len, cancellable, error) == -1)
284                         return -1;
285
286                 priv->ptr = priv->buf;
287         } else {
288                 /* nothing to do for read mode 'flush' */
289         }
290
291         return camel_stream_flush (priv->stream, cancellable, error);
292 }
293
294 static gint
295 stream_buffer_close (CamelStream *stream,
296                      GCancellable *cancellable,
297                      GError **error)
298 {
299         CamelStreamBufferPrivate *priv;
300
301         priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
302
303         if (stream_buffer_flush (stream, cancellable, error) == -1)
304                 return -1;
305
306         return camel_stream_close (priv->stream, cancellable, error);
307 }
308
309 static gboolean
310 stream_buffer_eos (CamelStream *stream)
311 {
312         CamelStreamBufferPrivate *priv;
313
314         priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
315
316         return camel_stream_eos (priv->stream) && priv->ptr == priv->end;
317 }
318
319 static void
320 stream_buffer_init_vbuf (CamelStreamBuffer *stream,
321                          CamelStream *other_stream,
322                          CamelStreamBufferMode mode,
323                          gchar *buf,
324                          guint32 size)
325 {
326         CamelStreamBufferPrivate *priv;
327
328         priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
329
330         set_vbuf (stream, buf, mode, size);
331
332         if (priv->stream != NULL)
333                 g_object_unref (priv->stream);
334
335         priv->stream = g_object_ref (other_stream);
336 }
337
338 static void
339 stream_buffer_init_method (CamelStreamBuffer *stream,
340                            CamelStream *other_stream,
341                            CamelStreamBufferMode mode)
342 {
343         stream_buffer_init_vbuf (stream, other_stream, mode, NULL, BUF_SIZE);
344 }
345
346 static void
347 camel_stream_buffer_class_init (CamelStreamBufferClass *class)
348 {
349         GObjectClass *object_class;
350         CamelStreamClass *stream_class;
351
352         g_type_class_add_private (class, sizeof (CamelStreamBufferPrivate));
353
354         object_class = G_OBJECT_CLASS (class);
355         object_class->dispose = stream_buffer_dispose;
356         object_class->finalize = stream_buffer_finalize;
357
358         stream_class = CAMEL_STREAM_CLASS (class);
359         stream_class->read = stream_buffer_read;
360         stream_class->write = stream_buffer_write;
361         stream_class->flush = stream_buffer_flush;
362         stream_class->close = stream_buffer_close;
363         stream_class->eos = stream_buffer_eos;
364
365         class->init = stream_buffer_init_method;
366         class->init_vbuf = stream_buffer_init_vbuf;
367 }
368
369 static void
370 camel_stream_buffer_init (CamelStreamBuffer *stream)
371 {
372         stream->priv = CAMEL_STREAM_BUFFER_GET_PRIVATE (stream);
373         stream->priv->flags = 0;
374         stream->priv->size = BUF_SIZE;
375         stream->priv->buf = g_malloc (BUF_SIZE);
376         stream->priv->ptr = stream->priv->buf;
377         stream->priv->end = stream->priv->buf;
378         stream->priv->mode =
379                 CAMEL_STREAM_BUFFER_READ |
380                 CAMEL_STREAM_BUFFER_BUFFER;
381         stream->priv->stream = NULL;
382         stream->priv->linesize = 80;
383         stream->priv->linebuf = g_malloc (stream->priv->linesize);
384 }
385
386 /**
387  * camel_stream_buffer_new:
388  * @stream: a #CamelStream object to buffer
389  * @mode: Operational mode of buffered stream.
390  *
391  * Create a new buffered stream of another stream.  A default
392  * buffer size (1024 bytes), automatically managed will be used
393  * for buffering.
394  *
395  * See camel_stream_buffer_new_with_vbuf() for details on the
396  * @mode parameter.
397  *
398  * Returns: a newly created buffered stream.
399  **/
400 CamelStream *
401 camel_stream_buffer_new (CamelStream *stream,
402                          CamelStreamBufferMode mode)
403 {
404         CamelStreamBuffer *sbf;
405         CamelStreamBufferClass *class;
406
407         g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL);
408
409         sbf = g_object_new (CAMEL_TYPE_STREAM_BUFFER, NULL);
410
411         class = CAMEL_STREAM_BUFFER_GET_CLASS (sbf);
412         g_return_val_if_fail (class->init != NULL, NULL);
413
414         class->init (sbf, stream, mode);
415
416         return CAMEL_STREAM (sbf);
417 }
418
419 /**
420  * camel_stream_buffer_new_with_vbuf:
421  * @stream: An existing stream to buffer.
422  * @mode: Mode to buffer in.
423  * @buf: Memory to use for buffering.
424  * @size: Size of buffer to use.
425  *
426  * Create a new stream which buffers another stream, @stream.
427  *
428  * The following values are available for @mode:
429  *
430  * #CAMEL_STREAM_BUFFER_BUFFER, Buffer the input/output in blocks.
431  * #CAMEL_STREAM_BUFFER_NEWLINE, Buffer on newlines (for output).
432  * #CAMEL_STREAM_BUFFER_NONE, Perform no buffering.
433  *
434  * Note that currently this is ignored and #CAMEL_STREAM_BUFFER_BUFFER
435  * is always used.
436  *
437  * In addition, one of the following mode options should be or'd
438  * together with the buffering mode:
439  *
440  * #CAMEL_STREAM_BUFFER_WRITE, Buffer in write mode.
441  * #CAMEL_STREAM_BUFFER_READ, Buffer in read mode.
442  *
443  * Buffering can only be done in one direction for any
444  * buffer instance.
445  *
446  * If @buf is non-NULL, then use the memory pointed to
447  * (for upto @size bytes) as the buffer for all buffering
448  * operations.  It is upto the application to free this buffer.
449  * If @buf is NULL, then allocate and manage @size bytes
450  * for all buffering.
451  *
452  * Returns: A new stream with buffering applied.
453  **/
454 CamelStream *
455 camel_stream_buffer_new_with_vbuf (CamelStream *stream,
456                                    CamelStreamBufferMode mode,
457                                    gchar *buf,
458                                    guint32 size)
459 {
460         CamelStreamBuffer *sbf;
461         CamelStreamBufferClass *class;
462
463         g_return_val_if_fail (CAMEL_IS_STREAM (stream), NULL);
464         g_return_val_if_fail (buf != NULL, NULL);
465
466         sbf = g_object_new (CAMEL_TYPE_STREAM_BUFFER, NULL);
467
468         class = CAMEL_STREAM_BUFFER_GET_CLASS (sbf);
469         g_return_val_if_fail (class->init_vbuf != NULL, NULL);
470
471         class->init_vbuf (sbf, stream, mode, buf, size);
472
473         return CAMEL_STREAM (sbf);
474 }
475
476 /**
477  * camel_stream_buffer_gets:
478  * @sbf: a #CamelStreamBuffer object
479  * @buf: Memory to write the string to.
480  * @max: Maxmimum number of characters to store.
481  * @cancellable: optional #GCancellable object, or %NULL
482  * @error: return location for a #GError, or %NULL
483  *
484  * Read a line of characters up to the next newline character or
485  * @max-1 characters.
486  *
487  * If the newline character is encountered, then it will be
488  * included in the buffer @buf.  The buffer will be %NULL terminated.
489  *
490  * Returns: the number of characters read, or %0 for end of file,
491  * and %-1 on error.
492  **/
493 gint
494 camel_stream_buffer_gets (CamelStreamBuffer *sbf,
495                           gchar *buf,
496                           guint max,
497                           GCancellable *cancellable,
498                           GError **error)
499 {
500         register gchar *outptr, *inptr, *inend, c, *outend;
501         gint bytes_read;
502         GError *local_error = NULL;
503
504         outptr = buf;
505         inptr = (gchar *) sbf->priv->ptr;
506         inend = (gchar *) sbf->priv->end;
507         outend = buf + max-1;   /* room for NUL */
508
509         do {
510                 while (inptr < inend && outptr < outend) {
511                         c = *inptr++;
512                         *outptr++ = c;
513                         if (c == '\n') {
514                                 *outptr = 0;
515                                 sbf->priv->ptr = (guchar *) inptr;
516                                 return outptr - buf;
517                         }
518                 }
519                 if (outptr == outend)
520                         break;
521
522                 bytes_read = camel_stream_read (
523                         sbf->priv->stream, (gchar *) sbf->priv->buf,
524                         sbf->priv->size, cancellable, &local_error);
525                 if (bytes_read == -1) {
526                         if (buf == outptr) {
527                                 if (local_error)
528                                         g_propagate_error (error, local_error);
529                                 return -1;
530                         } else
531                                 bytes_read = 0;
532                 }
533                 sbf->priv->ptr = sbf->priv->buf;
534                 sbf->priv->end = sbf->priv->buf + bytes_read;
535                 inptr = (gchar *) sbf->priv->ptr;
536                 inend = (gchar *) sbf->priv->end;
537         } while (bytes_read > 0);
538
539         sbf->priv->ptr = (guchar *) inptr;
540         *outptr = 0;
541
542         g_clear_error (&local_error);
543
544         return (gint)(outptr - buf);
545 }
546
547 /**
548  * camel_stream_buffer_read_line:
549  * @sbf: a #CamelStreamBuffer object
550  * @cancellable: optional #GCancellable object, or %NULL
551  * @error: return location for a @GError, or %NULL
552  *
553  * This function reads a complete newline-terminated line from the stream
554  * and returns it in allocated memory. The trailing newline (and carriage
555  * return if any) are not included in the returned string.
556  *
557  * Returns: the line read, which the caller must free when done with,
558  * or %NULL on eof. If an error occurs, @error will be set.
559  **/
560 gchar *
561 camel_stream_buffer_read_line (CamelStreamBuffer *sbf,
562                                GCancellable *cancellable,
563                                GError **error)
564 {
565         guchar *p;
566         gint nread;
567         GError *local_error = NULL;
568
569         p = sbf->priv->linebuf;
570
571         while (1) {
572                 nread = camel_stream_buffer_gets (
573                         sbf, (gchar *) p, sbf->priv->linesize -
574                         (p - sbf->priv->linebuf), cancellable, &local_error);
575                 if (nread <=0) {
576                         if (p > sbf->priv->linebuf)
577                                 break;
578                         if (local_error)
579                                 g_propagate_error (error, local_error);
580                         return NULL;
581                 }
582
583                 p += nread;
584                 if (p[-1] == '\n')
585                         break;
586
587                 nread = p - sbf->priv->linebuf;
588                 sbf->priv->linesize *= 2;
589                 sbf->priv->linebuf = g_realloc (
590                         sbf->priv->linebuf, sbf->priv->linesize);
591                 p = sbf->priv->linebuf + nread;
592         }
593
594         p--;
595         if (p > sbf->priv->linebuf && p[-1] == '\r')
596                 p--;
597         p[0] = 0;
598
599         g_clear_error (&local_error);
600
601         return g_strdup ((gchar *) sbf->priv->linebuf);
602 }