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