Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-stream-mem.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-stream-mem.c: memory buffer based stream */
3
4 /*
5  * Authors: Bertrand Guiheneuf <bertrand@helixcode.com>
6  *          Michael Zucchi <notzed@ximian.com>
7  *
8  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU Lesser General Public
12  * License as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34
35 #include "camel-stream-mem.h"
36
37 #define CAMEL_STREAM_MEM_GET_PRIVATE(obj) \
38         (G_TYPE_INSTANCE_GET_PRIVATE \
39         ((obj), CAMEL_TYPE_STREAM_MEM, CamelStreamMemPrivate))
40
41 struct _CamelStreamMemPrivate {
42         guint owner  : 1;       /* do we own the buffer? */
43         guint secure : 1;       /* do we clear the buffer on finalize?
44                                    (only if we own it) */
45
46         GByteArray *buffer;
47         goffset position;
48 };
49
50 /* Forward Declarations */
51 static void camel_stream_mem_seekable_init (GSeekableIface *interface);
52
53 G_DEFINE_TYPE_WITH_CODE (
54         CamelStreamMem, camel_stream_mem, CAMEL_TYPE_STREAM,
55         G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, camel_stream_mem_seekable_init))
56
57 /* could probably be a util method */
58 static void
59 clear_mem (gpointer p,
60            gsize len)
61 {
62         gchar *s = p;
63
64         /* This also helps debug bad access memory errors */
65         while (len > 4) {
66                 *s++ = 0xAB;
67                 *s++ = 0xAD;
68                 *s++ = 0xF0;
69                 *s++ = 0x0D;
70                 len -= 4;
71         }
72
73         memset (s, 0xbf, len);
74 }
75
76 static void
77 stream_mem_finalize (GObject *object)
78 {
79         CamelStreamMemPrivate *priv;
80
81         priv = CAMEL_STREAM_MEM_GET_PRIVATE (object);
82
83         if (priv->buffer && priv->owner) {
84                 /* TODO: we need our own bytearray type since we don't know
85                  * the real size of the underlying buffer :-/ */
86                 if (priv->secure && priv->buffer->len)
87                         clear_mem (priv->buffer->data, priv->buffer->len);
88                 g_byte_array_free (priv->buffer, TRUE);
89         }
90
91         /* Chain up to parent's finalize() method. */
92         G_OBJECT_CLASS (camel_stream_mem_parent_class)->finalize (object);
93 }
94
95 static gssize
96 stream_mem_read (CamelStream *stream,
97                  gchar *buffer,
98                  gsize n,
99                  GCancellable *cancellable,
100                  GError **error)
101 {
102         CamelStreamMemPrivate *priv;
103         gssize nread;
104
105         priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
106
107         nread = MIN (n, priv->buffer->len - priv->position);
108         if (nread > 0) {
109                 memcpy (buffer, priv->buffer->data + priv->position, nread);
110                 priv->position += nread;
111         } else
112                 nread = 0;
113
114         return nread;
115 }
116
117 static gssize
118 stream_mem_write (CamelStream *stream,
119                   const gchar *buffer,
120                   gsize n,
121                   GCancellable *cancellable,
122                   GError **error)
123 {
124         CamelStreamMemPrivate *priv;
125         gssize nwrite = n;
126
127         priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
128
129         /* FIXME: we shouldn't use g_byte_arrays or g_malloc perhaps? */
130         if (priv->position == priv->buffer->len) {
131                 g_byte_array_append (priv->buffer, (const guint8 *) buffer, nwrite);
132         } else {
133                 g_byte_array_set_size (priv->buffer, nwrite + priv->buffer->len);
134                 memcpy (priv->buffer->data + priv->position, buffer, nwrite);
135         }
136         priv->position += nwrite;
137
138         return nwrite;
139 }
140
141 static gboolean
142 stream_mem_eos (CamelStream *stream)
143 {
144         CamelStreamMemPrivate *priv;
145
146         priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
147
148         return priv->buffer->len <= priv->position;
149 }
150
151 static goffset
152 stream_mem_tell (GSeekable *seekable)
153 {
154         CamelStreamMemPrivate *priv;
155
156         priv = CAMEL_STREAM_MEM_GET_PRIVATE (seekable);
157
158         return priv->position;
159 }
160
161 static gboolean
162 stream_mem_can_seek (GSeekable *seekable)
163 {
164         return TRUE;
165 }
166
167 static gboolean
168 stream_mem_seek (GSeekable *seekable,
169                  goffset offset,
170                  GSeekType type,
171                  GCancellable *cancellable,
172                  GError **error)
173 {
174         CamelStreamMemPrivate *priv;
175         goffset position;
176
177         priv = CAMEL_STREAM_MEM_GET_PRIVATE (seekable);
178
179         switch (type) {
180         case G_SEEK_SET:
181                 position = offset;
182                 break;
183         case G_SEEK_CUR:
184                 position = priv->position + offset;
185                 break;
186         case G_SEEK_END:
187                 position = (priv->buffer)->len + offset;
188                 break;
189         default:
190                 position = offset;
191                 break;
192         }
193
194         position = MAX (position, 0);
195
196         if (position > priv->buffer->len) {
197                 gint oldlen = priv->buffer->len;
198                 g_byte_array_set_size (priv->buffer, position);
199                 memset (priv->buffer->data + oldlen, 0, position - oldlen);
200         }
201
202         priv->position = position;
203
204         return TRUE;
205 }
206
207 static gboolean
208 stream_mem_can_truncate (GSeekable *seekable)
209 {
210         return FALSE;
211 }
212
213 static gboolean
214 stream_mem_truncate_fn (GSeekable *seekable,
215                         goffset offset,
216                         GCancellable *cancellable,
217                         GError **error)
218 {
219         /* XXX Don't bother translating this.  Camel never calls it. */
220         g_set_error (
221                 error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
222                 "Truncation is not supported");
223
224         return FALSE;
225 }
226
227 static void
228 camel_stream_mem_class_init (CamelStreamMemClass *class)
229 {
230         GObjectClass *object_class;
231         CamelStreamClass *stream_class;
232
233         g_type_class_add_private (class, sizeof (CamelStreamMemPrivate));
234
235         object_class = G_OBJECT_CLASS (class);
236         object_class->finalize = stream_mem_finalize;
237
238         stream_class = CAMEL_STREAM_CLASS (class);
239         stream_class->read = stream_mem_read;
240         stream_class->write = stream_mem_write;
241         stream_class->eos = stream_mem_eos;
242 }
243
244 static void
245 camel_stream_mem_seekable_init (GSeekableIface *interface)
246 {
247         interface->tell = stream_mem_tell;
248         interface->can_seek = stream_mem_can_seek;
249         interface->seek = stream_mem_seek;
250         interface->can_truncate = stream_mem_can_truncate;
251         interface->truncate_fn = stream_mem_truncate_fn;
252 }
253
254 static void
255 camel_stream_mem_init (CamelStreamMem *stream)
256 {
257         stream->priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
258 }
259
260 /**
261  * camel_stream_mem_new:
262  *
263  * Create a new #CamelStreamMem object.
264  *
265  * Returns: a new #CamelStreamMem
266  **/
267 CamelStream *
268 camel_stream_mem_new (void)
269 {
270         return camel_stream_mem_new_with_byte_array (g_byte_array_new ());
271 }
272
273 /**
274  * camel_stream_mem_new_with_buffer:
275  * @buffer: a memory buffer to use as the stream data
276  * @len: length of @buffer
277  *
278  * Create a new memory stream using @buffer as the stream data.
279  *
280  * Note: @buffer will be copied into an internal #GByteArray structure
281  * for use as the stream backing. This may have resource implications
282  * you may wish to consider.
283  *
284  * Returns: a new #CamelStreamMem
285  **/
286 CamelStream *
287 camel_stream_mem_new_with_buffer (const gchar *buffer,
288                                   gsize len)
289 {
290         GByteArray *ba;
291
292         g_return_val_if_fail (buffer != NULL, NULL);
293
294         ba = g_byte_array_new ();
295         g_byte_array_append (ba, (const guint8 *) buffer, len);
296
297         return camel_stream_mem_new_with_byte_array (ba);
298 }
299
300 /**
301  * camel_stream_mem_new_with_byte_array:
302  * @buffer: a #GByteArray to use as the stream data
303  *
304  * Create a new #CamelStreamMem using @buffer as the stream data.
305  *
306  * Note: The newly created #CamelStreamMem will destroy @buffer
307  * when destroyed.
308  *
309  * Returns: a new #CamelStreamMem
310  **/
311 CamelStream *
312 camel_stream_mem_new_with_byte_array (GByteArray *buffer)
313 {
314         CamelStream *stream;
315         CamelStreamMemPrivate *priv;
316
317         g_return_val_if_fail (buffer != NULL, NULL);
318
319         stream = g_object_new (CAMEL_TYPE_STREAM_MEM, NULL);
320         priv = CAMEL_STREAM_MEM_GET_PRIVATE (stream);
321
322         priv->buffer = buffer;
323         priv->owner = TRUE;
324
325         return stream;
326 }
327
328 /**
329  * camel_stream_mem_set_secure:
330  * @mem: a #CamelStreamMem object
331  *
332  * Mark the memory stream as secure.  At the very least this means the
333  * data in the buffer will be cleared when the buffer is finalized.
334  * This only applies to buffers owned by the stream.
335  **/
336 void
337 camel_stream_mem_set_secure (CamelStreamMem *mem)
338 {
339         g_return_if_fail (CAMEL_IS_STREAM_MEM (mem));
340
341         mem->priv->secure = 1;
342 }
343
344 /* note: with these functions the caller is the 'owner' of the buffer */
345
346 /**
347  * camel_stream_mem_get_byte_array:
348  * @mem: a #CamelStreamMem
349  *
350  * Since: 2.32
351  **/
352 GByteArray *
353 camel_stream_mem_get_byte_array (CamelStreamMem *mem)
354 {
355         g_return_val_if_fail (CAMEL_IS_STREAM_MEM (mem), NULL);
356
357         return mem->priv->buffer;
358 }
359
360 /**
361  * camel_stream_mem_set_byte_array:
362  * @mem: a #CamelStreamMem object
363  * @buffer: a #GByteArray
364  *
365  * Set @buffer to be the backing data to the existing #CamelStreamMem, @mem.
366  *
367  * Note: @mem will not take ownership of @buffer and so will need to
368  * be freed separately from @mem.
369  **/
370 void
371 camel_stream_mem_set_byte_array (CamelStreamMem *mem,
372                                  GByteArray *buffer)
373 {
374         g_return_if_fail (CAMEL_IS_STREAM_MEM (mem));
375         g_return_if_fail (buffer != NULL);
376
377         if (mem->priv->buffer && mem->priv->owner) {
378                 if (mem->priv->secure && mem->priv->buffer->len)
379                         clear_mem (
380                                 mem->priv->buffer->data,
381                                 mem->priv->buffer->len);
382                 g_byte_array_free (mem->priv->buffer, TRUE);
383         }
384         mem->priv->owner = FALSE;
385         mem->priv->buffer = buffer;
386 }
387
388 /**
389  * camel_stream_mem_set_buffer:
390  * @mem: a #CamelStreamMem object
391  * @buffer: a memory buffer
392  * @len: length of @buffer
393  *
394  * Set @buffer to be the backing data to the existing #CamelStreamMem, @mem.
395  *
396  * Note: @buffer will be copied into an internal #GByteArray structure
397  * and so may have resource implications to consider.
398  **/
399 void
400 camel_stream_mem_set_buffer (CamelStreamMem *mem,
401                              const gchar *buffer,
402                              gsize len)
403 {
404         GByteArray *ba;
405
406         g_return_if_fail (CAMEL_IS_STREAM_MEM (mem));
407         g_return_if_fail (buffer != NULL);
408
409         ba = g_byte_array_new ();
410         g_byte_array_append (ba, (const guint8 *) buffer, len);
411         camel_stream_mem_set_byte_array (mem, ba);
412         mem->priv->owner = TRUE;
413 }