Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-stream-file.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib.h>
27
28 #ifdef G_OS_WIN32
29 #include <fcntl.h>
30 #include <io.h>
31 #endif
32 #include <errno.h>
33
34 #include "gmime-stream-file.h"
35
36
37 /**
38  * SECTION: gmime-stream-file
39  * @title: GMimeStreamFile
40  * @short_description: A Standard-C FILE-based stream
41  * @see_also: #GMimeStream
42  *
43  * A simple #GMimeStream implementation that sits on top of the
44  * Standard C FILE pointer based I/O layer. Unlike #GMimeStreamFs, a
45  * #GMimeStreamFile will typically buffer read and write operations at
46  * the FILE level and so it may be wasteful to wrap one in a
47  * #GMimeStreamBuffer stream.
48  **/
49
50
51 static void g_mime_stream_file_class_init (GMimeStreamFileClass *klass);
52 static void g_mime_stream_file_init (GMimeStreamFile *stream, GMimeStreamFileClass *klass);
53 static void g_mime_stream_file_finalize (GObject *object);
54
55 static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
56 static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
57 static int stream_flush (GMimeStream *stream);
58 static int stream_close (GMimeStream *stream);
59 static gboolean stream_eos (GMimeStream *stream);
60 static int stream_reset (GMimeStream *stream);
61 static gint64 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence);
62 static gint64 stream_tell (GMimeStream *stream);
63 static gint64 stream_length (GMimeStream *stream);
64 static GMimeStream *stream_substream (GMimeStream *stream, gint64 start, gint64 end);
65
66
67 static GMimeStreamClass *parent_class = NULL;
68
69
70 GType
71 g_mime_stream_file_get_type (void)
72 {
73         static GType type = 0;
74         
75         if (!type) {
76                 static const GTypeInfo info = {
77                         sizeof (GMimeStreamFileClass),
78                         NULL, /* base_class_init */
79                         NULL, /* base_class_finalize */
80                         (GClassInitFunc) g_mime_stream_file_class_init,
81                         NULL, /* class_finalize */
82                         NULL, /* class_data */
83                         sizeof (GMimeStreamFile),
84                         0,    /* n_preallocs */
85                         (GInstanceInitFunc) g_mime_stream_file_init,
86                 };
87                 
88                 type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamFile", &info, 0);
89         }
90         
91         return type;
92 }
93
94
95 static void
96 g_mime_stream_file_class_init (GMimeStreamFileClass *klass)
97 {
98         GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
99         GObjectClass *object_class = G_OBJECT_CLASS (klass);
100         
101         parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
102         
103         object_class->finalize = g_mime_stream_file_finalize;
104         
105         stream_class->read = stream_read;
106         stream_class->write = stream_write;
107         stream_class->flush = stream_flush;
108         stream_class->close = stream_close;
109         stream_class->eos = stream_eos;
110         stream_class->reset = stream_reset;
111         stream_class->seek = stream_seek;
112         stream_class->tell = stream_tell;
113         stream_class->length = stream_length;
114         stream_class->substream = stream_substream;
115 }
116
117 static void
118 g_mime_stream_file_init (GMimeStreamFile *stream, GMimeStreamFileClass *klass)
119 {
120         stream->owner = TRUE;
121         stream->fp = NULL;
122 }
123
124 static void
125 g_mime_stream_file_finalize (GObject *object)
126 {
127         GMimeStreamFile *stream = (GMimeStreamFile *) object;
128         
129         if (stream->owner && stream->fp)
130                 fclose (stream->fp);
131         
132         G_OBJECT_CLASS (parent_class)->finalize (object);
133 }
134
135
136 static ssize_t
137 stream_read (GMimeStream *stream, char *buf, size_t len)
138 {
139         GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
140         size_t nread;
141         
142         if (fstream->fp == NULL) {
143                 errno = EBADF;
144                 return -1;
145         }
146         
147         if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
148                 errno = EINVAL;
149                 return -1;
150         }
151         
152         if (stream->bound_end != -1)
153                 len = (size_t) MIN (stream->bound_end - stream->position, (gint64) len);
154         
155         /* make sure we are at the right position */
156         fseek (fstream->fp, (long) stream->position, SEEK_SET);
157         
158         if ((nread = fread (buf, 1, len, fstream->fp)) > 0)
159                 stream->position += nread;
160         
161         return (ssize_t) nread;
162 }
163
164 static ssize_t
165 stream_write (GMimeStream *stream, const char *buf, size_t len)
166 {
167         GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
168         size_t nwritten;
169         
170         if (fstream->fp == NULL) {
171                 errno = EBADF;
172                 return -1;
173         }
174         
175         if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
176                 errno = EINVAL;
177                 return -1;
178         }
179         
180         if (stream->bound_end != -1)
181                 len = (size_t) MIN (stream->bound_end - stream->position, (gint64) len);
182         
183         /* make sure we are at the right position */
184         fseek (fstream->fp, (long) stream->position, SEEK_SET);
185
186         if ((nwritten = fwrite (buf, 1, len, fstream->fp)) > 0)
187                 stream->position += nwritten;
188         
189         return (ssize_t) nwritten;
190 }
191
192 static int
193 stream_flush (GMimeStream *stream)
194 {
195         GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
196         
197         if (fstream->fp == NULL) {
198                 errno = EBADF;
199                 return -1;
200         }
201         
202         return fflush (fstream->fp);
203 }
204
205 static int
206 stream_close (GMimeStream *stream)
207 {
208         GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
209         int rv;
210         
211         if (fstream->fp == NULL)
212                 return 0;
213         
214         if ((rv = fclose (fstream->fp)) != 0)
215                 fstream->fp = NULL;
216         
217         return rv;
218 }
219
220 static gboolean
221 stream_eos (GMimeStream *stream)
222 {
223         GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
224         
225         if (fstream->fp == NULL)
226                 return TRUE;
227         
228         if (stream->bound_end == -1)
229                 return feof (fstream->fp) ? TRUE : FALSE;
230         else
231                 return stream->position >= stream->bound_end;
232 }
233
234 static int
235 stream_reset (GMimeStream *stream)
236 {
237         GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
238         
239         if (fstream->fp == NULL) {
240                 errno = EBADF;
241                 return -1;
242         }
243         
244         if (stream->position == stream->bound_start)
245                 return 0;
246         
247         if (fseek (fstream->fp, (long) stream->bound_start, SEEK_SET) == -1)
248                 return -1;
249         
250         return 0;
251 }
252
253 static gint64
254 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
255 {
256         GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
257         gint64 real = stream->position;
258         FILE *fp = fstream->fp;
259         
260         if (fstream->fp == NULL) {
261                 errno = EBADF;
262                 return -1;
263         }
264         
265         switch (whence) {
266         case GMIME_STREAM_SEEK_SET:
267                 real = offset;
268                 break;
269         case GMIME_STREAM_SEEK_CUR:
270                 real = stream->position + offset;
271                 break;
272         case GMIME_STREAM_SEEK_END:
273                 if (offset > 0 || (stream->bound_end == -1 && !feof (fp))) {
274                         /* need to do an actual lseek() here because
275                          * we either don't know the offset of the end
276                          * of the stream and/or don't know if we can
277                          * seek past the end */
278                         if (fseek (fp, (long) offset, SEEK_END) == -1 || (real = ftell (fp)) == -1)
279                                 return -1;
280                 } else if (feof (fp) && stream->bound_end == -1) {
281                         /* seeking backwards from eos (which happens
282                          * to be our current position) */
283                         real = stream->position + offset;
284                 } else {
285                         /* seeking backwards from a known position */
286                         real = stream->bound_end + offset;
287                 }
288                 break;
289         }
290         
291         /* sanity check the resultant offset */
292         if (real < stream->bound_start) {
293                 errno = EINVAL;
294                 return -1;
295         }
296         
297         if (stream->bound_end != -1 && real > stream->bound_end) {
298                 errno = EINVAL;
299                 return -1;
300         }
301         
302         if (fseek (fp, (long) real, SEEK_SET) == -1 || (real = ftell (fp)) == -1)
303                 return -1;
304         
305         stream->position = real;
306         
307         return real;
308 }
309
310 static gint64
311 stream_tell (GMimeStream *stream)
312 {
313         return stream->position;
314 }
315
316 static gint64
317 stream_length (GMimeStream *stream)
318 {
319         GMimeStreamFile *fstream = (GMimeStreamFile *) stream;
320         gint64 bound_end;
321         
322         if (fstream->fp == NULL) {
323                 errno = EBADF;
324                 return -1;
325         }
326         
327         if (stream->bound_start != -1 && stream->bound_end != -1)
328                 return stream->bound_end - stream->bound_start;
329         
330         fseek (fstream->fp, (long) 0, SEEK_END);
331         bound_end = ftell (fstream->fp);
332         fseek (fstream->fp, (long) stream->position, SEEK_SET);
333         
334         if (bound_end < stream->bound_start) {
335                 errno = EINVAL;
336                 return -1;
337         }
338         
339         return bound_end - stream->bound_start;
340 }
341
342 static GMimeStream *
343 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
344 {
345         GMimeStreamFile *fstream;
346         
347         fstream = g_object_newv (GMIME_TYPE_STREAM_FILE, 0, NULL);
348         g_mime_stream_construct (GMIME_STREAM (fstream), start, end);
349         fstream->owner = FALSE;
350         fstream->fp = GMIME_STREAM_FILE (stream)->fp;
351         
352         return GMIME_STREAM (fstream);
353 }
354
355
356 /**
357  * g_mime_stream_file_new:
358  * @fp: a FILE pointer
359  *
360  * Creates a new #GMimeStreamFile object around @fp.
361  *
362  * Note: The created #GMimeStreamFile object will own the FILE pointer
363  * passed in.
364  *
365  * Returns: a stream using @fp.
366  **/
367 GMimeStream *
368 g_mime_stream_file_new (FILE *fp)
369 {
370         GMimeStreamFile *fstream;
371         gint64 start;
372         
373 #ifdef G_OS_WIN32
374         _setmode (_fileno (fp), O_BINARY);
375 #endif
376         
377         if ((start = ftell (fp)) == -1)
378                 start = 0;
379         
380         fstream = g_object_newv (GMIME_TYPE_STREAM_FILE, 0, NULL);
381         g_mime_stream_construct (GMIME_STREAM (fstream), start, -1);
382         fstream->owner = TRUE;
383         fstream->fp = fp;
384         
385         return GMIME_STREAM (fstream);
386 }
387
388
389 /**
390  * g_mime_stream_file_new_with_bounds:
391  * @fp: a FILE pointer
392  * @start: start boundary
393  * @end: end boundary
394  *
395  * Creates a new #GMimeStreamFile object around @fp with bounds @start
396  * and @end.
397  *
398  * Note: The created #GMimeStreamFile object will own the FILE pointer
399  * passed in.
400  *
401  * Returns: a stream using @fp with bounds @start and @end.
402  **/
403 GMimeStream *
404 g_mime_stream_file_new_with_bounds (FILE *fp, gint64 start, gint64 end)
405 {
406         GMimeStreamFile *fstream;
407         
408 #ifdef G_OS_WIN32
409         _setmode (_fileno (fp), O_BINARY);
410 #endif
411         
412         fstream = g_object_newv (GMIME_TYPE_STREAM_FILE, 0, NULL);
413         g_mime_stream_construct (GMIME_STREAM (fstream), start, end);
414         fstream->owner = TRUE;
415         fstream->fp = fp;
416         
417         return GMIME_STREAM (fstream);
418 }
419
420
421 /**
422  * g_mime_stream_file_get_owner:
423  * @stream: a #GMimeStreamFile
424  *
425  * Gets whether or not @stream owns the backend FILE pointer.
426  *
427  * Returns: %TRUE if @stream owns the backend FILE pointer or %FALSE
428  * otherwise.
429  **/
430 gboolean
431 g_mime_stream_file_get_owner (GMimeStreamFile *stream)
432 {
433         g_return_val_if_fail (GMIME_IS_STREAM_FILE (stream), FALSE);
434         
435         return stream->owner;
436 }
437
438
439 /**
440  * g_mime_stream_file_set_owner:
441  * @stream: a #GMimeStreamFile
442  * @owner: %TRUE if this stream should own the FILE pointer or %FALSE otherwise
443  *
444  * Sets whether or not @stream owns the backend FILE pointer.
445  *
446  * Note: @owner should be %TRUE if the stream should fclose() the
447  * backend FILE pointer when destroyed or %FALSE otherwise.
448  **/
449 void
450 g_mime_stream_file_set_owner (GMimeStreamFile *stream, gboolean owner)
451 {
452         g_return_if_fail (GMIME_IS_STREAM_FILE (stream));
453         
454         stream->owner = owner;
455 }