Fix FSF address (Tobias Mueller, #470445)
[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 1999, 2000 Ximian, Inc. (www.ximian.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 static CamelSeekableStreamClass *parent_class = NULL;
38
39 /* Returns the class for a CamelStreamMem */
40 #define CSM_CLASS(so) CAMEL_STREAM_MEM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
41
42 static ssize_t stream_read (CamelStream *stream, char *buffer, size_t n);
43 static ssize_t stream_write (CamelStream *stream, const char *buffer, size_t n);
44 static gboolean stream_eos (CamelStream *stream);
45 static off_t stream_seek (CamelSeekableStream *stream, off_t offset,
46                           CamelStreamSeekPolicy policy);
47
48 static void camel_stream_mem_finalize (CamelObject *object);
49
50 static void
51 camel_stream_mem_class_init (CamelStreamMemClass *camel_stream_mem_class)
52 {
53         CamelSeekableStreamClass *camel_seekable_stream_class =
54                 CAMEL_SEEKABLE_STREAM_CLASS (camel_stream_mem_class);
55         CamelStreamClass *camel_stream_class =
56                 CAMEL_STREAM_CLASS (camel_stream_mem_class);
57
58         parent_class = CAMEL_SEEKABLE_STREAM_CLASS( camel_type_get_global_classfuncs( CAMEL_SEEKABLE_STREAM_TYPE ) );
59
60         /* virtual method overload */
61         camel_stream_class->read = stream_read;
62         camel_stream_class->write = stream_write;
63         camel_stream_class->eos = stream_eos;
64
65         camel_seekable_stream_class->seek = stream_seek;
66 }
67
68 static void
69 camel_stream_mem_init (CamelObject *object)
70 {
71         CamelStreamMem *stream_mem = CAMEL_STREAM_MEM (object);
72
73         stream_mem->owner = FALSE;
74         stream_mem->buffer = 0;
75 }
76
77 /* could probably be a util method */
78 static void clear_mem(void *p, size_t len)
79 {
80         char *s = p;
81
82         /* This also helps debug bad access memory errors */
83         while (len > 4) {
84                 *s++ = 0xAB;
85                 *s++ = 0xAD;
86                 *s++ = 0xF0;
87                 *s++ = 0x0D;
88                 len -= 4;
89         }
90
91         memset(s, 0xbf, len);
92 }
93
94 CamelType
95 camel_stream_mem_get_type (void)
96 {
97         static CamelType camel_stream_mem_type = CAMEL_INVALID_TYPE;
98
99         if (camel_stream_mem_type == CAMEL_INVALID_TYPE) {
100                 camel_stream_mem_type = camel_type_register( CAMEL_SEEKABLE_STREAM_TYPE,
101                                                              "CamelStreamMem",
102                                                              sizeof( CamelStreamMem ),
103                                                              sizeof( CamelStreamMemClass ),
104                                                              (CamelObjectClassInitFunc) camel_stream_mem_class_init,
105                                                              NULL,
106                                                              (CamelObjectInitFunc) camel_stream_mem_init,
107                                                              (CamelObjectFinalizeFunc) camel_stream_mem_finalize );
108         }
109
110         return camel_stream_mem_type;
111 }
112
113
114 /**
115  * camel_stream_mem_new:
116  *
117  * Create a new #CamelStreamMem object.
118  *
119  * Returns a new #CamelStreamMem
120  **/
121 CamelStream *
122 camel_stream_mem_new (void)
123 {
124         return camel_stream_mem_new_with_byte_array (g_byte_array_new ());
125 }
126
127
128 /**
129  * camel_stream_mem_new_with_buffer:
130  * @buffer: a memory buffer to use as the stream data
131  * @len: length of @buffer
132  *
133  * Create a new memory stream using @buffer as the stream data.
134  *
135  * Note: @buffer will be copied into an internal #GByteArray structure
136  * for use as the stream backing. This may have resource implications
137  * you may wish to consider.
138  *
139  * Returns a new #CamelStreamMem
140  **/
141 CamelStream *
142 camel_stream_mem_new_with_buffer (const char *buffer, size_t len)
143 {
144         GByteArray *ba;
145
146         ba = g_byte_array_new ();
147         g_byte_array_append (ba, (const guint8 *)buffer, len);
148         return camel_stream_mem_new_with_byte_array (ba);
149 }
150
151
152 /**
153  * camel_stream_mem_new_with_byte_array:
154  * @buffer: a #GByteArray to use as the stream data
155  *
156  * Create a new #CamelStreamMem using @buffer as the stream data.
157  *
158  * Note: The newly created #CamelStreamMem will destroy @buffer
159  * when destroyed.
160  *
161  * Returns a new #CamelStreamMem
162  **/
163 CamelStream *
164 camel_stream_mem_new_with_byte_array (GByteArray *buffer)
165 {
166         CamelStreamMem *stream_mem;
167
168         stream_mem = CAMEL_STREAM_MEM (camel_object_new (CAMEL_STREAM_MEM_TYPE));
169         stream_mem->buffer = buffer;
170         stream_mem->owner = TRUE;
171
172         return CAMEL_STREAM (stream_mem);
173 }
174
175
176 /**
177  * camel_stream_mem_set_secure:
178  * @mem: a #CamelStreamMem object
179  * 
180  * Mark the memory stream as secure.  At the very least this means the
181  * data in the buffer will be cleared when the buffer is finalised.
182  * This only applies to buffers owned by the stream.
183  **/
184 void
185 camel_stream_mem_set_secure(CamelStreamMem *mem)
186 {
187         mem->secure = 1;
188         /* setup a mem-locked buffer etc?  blah blah, well not yet anyway */
189 }
190
191
192 /* note: with these functions the caller is the 'owner' of the buffer */
193
194 /**
195  * camel_stream_mem_set_byte_array:
196  * @mem: a #CamelStreamMem object
197  * @buffer: a #GByteArray
198  *
199  * Set @buffer to be the backing data to the existing #CamelStreamMem, @mem.
200  *
201  * Note: @mem will not take ownership of @buffer and so will need to
202  * be freed separately from @mem.
203  **/
204 void
205 camel_stream_mem_set_byte_array (CamelStreamMem *mem, GByteArray *buffer)
206 {
207         if (mem->buffer && mem->owner) {
208                 if (mem->secure && mem->buffer->len)
209                         clear_mem (mem->buffer->data, mem->buffer->len);
210                 g_byte_array_free (mem->buffer, TRUE);
211         }
212         mem->owner = FALSE;
213         mem->buffer = buffer;
214 }
215
216
217 /**
218  * camel_stream_mem_set_buffer:
219  * @mem: a #CamelStreamMem object
220  * @buffer: a memory buffer
221  * @len: length of @buffer
222  *
223  * Set @buffer to be the backing data to the existing #CamelStreamMem, @mem.
224  *
225  * Note: @buffer will be copied into an internal #GByteArray structure
226  * and so may have resource implications to consider.
227  **/
228 void
229 camel_stream_mem_set_buffer (CamelStreamMem *mem, const char *buffer, size_t len)
230 {
231         GByteArray *ba;
232
233         ba = g_byte_array_new ();
234         g_byte_array_append(ba, (const guint8 *)buffer, len);
235         camel_stream_mem_set_byte_array(mem, ba);
236         mem->owner = TRUE;
237 }
238
239
240 static void
241 camel_stream_mem_finalize (CamelObject *object)
242 {
243         CamelStreamMem *s = CAMEL_STREAM_MEM (object);
244
245         if (s->buffer && s->owner) {
246                 /* TODO: we need our own bytearray type since we don't know
247                    the real size of the underlying buffer :-/ */
248                 if (s->secure && s->buffer->len)
249                         clear_mem(s->buffer->data, s->buffer->len);
250                 g_byte_array_free(s->buffer, TRUE);
251         }
252 }
253
254 static ssize_t
255 stream_read (CamelStream *stream, char *buffer, size_t n)
256 {
257         CamelStreamMem *camel_stream_mem = CAMEL_STREAM_MEM (stream);
258         CamelSeekableStream *seekable = CAMEL_SEEKABLE_STREAM (stream);
259         ssize_t nread;
260
261         if (seekable->bound_end != CAMEL_STREAM_UNBOUND)
262                 n = MIN(seekable->bound_end - seekable->position, n);
263
264         nread = MIN (n, camel_stream_mem->buffer->len - seekable->position);
265         if (nread > 0) {
266                 memcpy (buffer, camel_stream_mem->buffer->data + seekable->position, nread);
267                 seekable->position += nread;
268         } else
269                 nread = 0;
270
271         return nread;
272 }
273
274 static ssize_t
275 stream_write (CamelStream *stream, const char *buffer, size_t n)
276 {
277         CamelStreamMem *stream_mem = CAMEL_STREAM_MEM (stream);
278         CamelSeekableStream *seekable = CAMEL_SEEKABLE_STREAM (stream);
279         ssize_t nwrite = n;
280         
281         if (seekable->bound_end != CAMEL_STREAM_UNBOUND)
282                 nwrite = MIN(seekable->bound_end - seekable->position, n);
283
284         /* FIXME: we shouldn't use g_byte_arrays or g_malloc perhaps? */
285         if (seekable->position == stream_mem->buffer->len) {
286                 g_byte_array_append(stream_mem->buffer, (const guint8 *)buffer, nwrite);
287         } else {
288                 g_byte_array_set_size(stream_mem->buffer, nwrite + stream_mem->buffer->len);
289                 memcpy(stream_mem->buffer->data + seekable->position, buffer, nwrite);
290         }
291         seekable->position += nwrite;
292
293         return nwrite;
294 }
295
296 static gboolean
297 stream_eos (CamelStream *stream)
298 {
299         CamelStreamMem *stream_mem = CAMEL_STREAM_MEM (stream);
300         CamelSeekableStream *seekable_stream = CAMEL_SEEKABLE_STREAM (stream);
301
302         return stream_mem->buffer->len <= seekable_stream->position;
303 }
304
305 static off_t
306 stream_seek (CamelSeekableStream *stream, off_t offset,
307              CamelStreamSeekPolicy policy)
308 {
309         off_t position;
310         CamelStreamMem *stream_mem = CAMEL_STREAM_MEM (stream);
311
312         switch  (policy) {
313         case CAMEL_STREAM_SET:
314                 position = offset;
315                 break;
316         case CAMEL_STREAM_CUR:
317                 position = stream->position + offset;
318                 break;
319         case CAMEL_STREAM_END:
320                 position = (stream_mem->buffer)->len + offset;
321                 break;
322         default:
323                 position = offset;
324                 break;
325         }
326
327         if (stream->bound_end != CAMEL_STREAM_UNBOUND)
328                 position = MIN (position, stream->bound_end);
329         if (stream->bound_start != CAMEL_STREAM_UNBOUND)
330                 position = MAX (position, 0);
331         else
332                 position = MAX (position, stream->bound_start);
333
334         if (position > stream_mem->buffer->len) {
335                 int oldlen = stream_mem->buffer->len;
336                 g_byte_array_set_size (stream_mem->buffer, position);
337                 memset (stream_mem->buffer->data + oldlen, 0,
338                         position - oldlen);
339         }
340
341         stream->position = position;
342
343         return position;
344 }