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