Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-stream-fs.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 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33
34 #include "gmime-stream-fs.h"
35
36 #ifndef HAVE_FSYNC
37 #ifdef G_OS_WIN32
38 /* _commit() is the equivalent of fsync() on Windows, but it aborts the
39  * program if the fd is a tty, so we'll just no-op for now... */
40 static int fsync (int fd) { return 0; }
41 #else
42 static int fsync (int fd) { return 0; }
43 #endif
44 #endif
45
46
47 /**
48  * SECTION: gmime-stream-fs
49  * @title: GMimeStreamFs
50  * @short_description: A low-level FileSystem stream
51  * @see_also: #GMimeStream
52  *
53  * A simple #GMimeStream implementation that sits on top of the
54  * low-level UNIX file descriptor based I/O layer.
55  **/
56
57
58 static void g_mime_stream_fs_class_init (GMimeStreamFsClass *klass);
59 static void g_mime_stream_fs_init (GMimeStreamFs *stream, GMimeStreamFsClass *klass);
60 static void g_mime_stream_fs_finalize (GObject *object);
61
62 static ssize_t stream_read (GMimeStream *stream, char *buf, size_t len);
63 static ssize_t stream_write (GMimeStream *stream, const char *buf, size_t len);
64 static int stream_flush (GMimeStream *stream);
65 static int stream_close (GMimeStream *stream);
66 static gboolean stream_eos (GMimeStream *stream);
67 static int stream_reset (GMimeStream *stream);
68 static gint64 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence);
69 static gint64 stream_tell (GMimeStream *stream);
70 static gint64 stream_length (GMimeStream *stream);
71 static GMimeStream *stream_substream (GMimeStream *stream, gint64 start, gint64 end);
72
73
74 static GMimeStreamClass *parent_class = NULL;
75
76
77 GType
78 g_mime_stream_fs_get_type (void)
79 {
80         static GType type = 0;
81         
82         if (!type) {
83                 static const GTypeInfo info = {
84                         sizeof (GMimeStreamFsClass),
85                         NULL, /* base_class_init */
86                         NULL, /* base_class_finalize */
87                         (GClassInitFunc) g_mime_stream_fs_class_init,
88                         NULL, /* class_finalize */
89                         NULL, /* class_data */
90                         sizeof (GMimeStreamFs),
91                         0,    /* n_preallocs */
92                         (GInstanceInitFunc) g_mime_stream_fs_init,
93                 };
94                 
95                 type = g_type_register_static (GMIME_TYPE_STREAM, "GMimeStreamFs", &info, 0);
96         }
97         
98         return type;
99 }
100
101
102 static void
103 g_mime_stream_fs_class_init (GMimeStreamFsClass *klass)
104 {
105         GMimeStreamClass *stream_class = GMIME_STREAM_CLASS (klass);
106         GObjectClass *object_class = G_OBJECT_CLASS (klass);
107         
108         parent_class = g_type_class_ref (GMIME_TYPE_STREAM);
109         
110         object_class->finalize = g_mime_stream_fs_finalize;
111         
112         stream_class->read = stream_read;
113         stream_class->write = stream_write;
114         stream_class->flush = stream_flush;
115         stream_class->close = stream_close;
116         stream_class->eos = stream_eos;
117         stream_class->reset = stream_reset;
118         stream_class->seek = stream_seek;
119         stream_class->tell = stream_tell;
120         stream_class->length = stream_length;
121         stream_class->substream = stream_substream;
122 }
123
124 static void
125 g_mime_stream_fs_init (GMimeStreamFs *stream, GMimeStreamFsClass *klass)
126 {
127         stream->owner = TRUE;
128         stream->eos = FALSE;
129         stream->fd = -1;
130 }
131
132 static void
133 g_mime_stream_fs_finalize (GObject *object)
134 {
135         GMimeStreamFs *stream = (GMimeStreamFs *) object;
136         
137         if (stream->owner && stream->fd != -1)
138                 close (stream->fd);
139         
140         G_OBJECT_CLASS (parent_class)->finalize (object);
141 }
142
143 static ssize_t
144 stream_read (GMimeStream *stream, char *buf, size_t len)
145 {
146         GMimeStreamFs *fs = (GMimeStreamFs *) stream;
147         ssize_t nread;
148         
149         if (fs->fd == -1) {
150                 errno = EBADF;
151                 return -1;
152         }
153         
154         if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
155                 errno = EINVAL;
156                 return -1;
157         }
158         
159         if (stream->bound_end != -1)
160                 len = (size_t) MIN (stream->bound_end - stream->position, (gint64) len);
161         
162         /* make sure we are at the right position */
163         lseek (fs->fd, (off_t) stream->position, SEEK_SET);
164         
165         do {
166                 nread = read (fs->fd, buf, len);
167         } while (nread == -1 && errno == EINTR);
168         
169         if (nread > 0) {
170                 stream->position += nread;
171         } else if (nread == 0) {
172                 fs->eos = TRUE;
173         }
174         
175         return nread;
176 }
177
178 static ssize_t
179 stream_write (GMimeStream *stream, const char *buf, size_t len)
180 {
181         GMimeStreamFs *fs = (GMimeStreamFs *) stream;
182         size_t nwritten = 0;
183         ssize_t n;
184         
185         if (fs->fd == -1) {
186                 errno = EBADF;
187                 return -1;
188         }
189         
190         if (stream->bound_end != -1 && stream->position >= stream->bound_end) {
191                 errno = EINVAL;
192                 return -1;
193         }
194         
195         if (stream->bound_end != -1)
196                 len = (size_t) MIN (stream->bound_end - stream->position, (gint64) len);
197         
198         /* make sure we are at the right position */
199         lseek (fs->fd, (off_t) stream->position, SEEK_SET);
200         
201         do {
202                 do {
203                         n = write (fs->fd, buf + nwritten, len - nwritten);
204                 } while (n == -1 && (errno == EINTR || errno == EAGAIN));
205                 
206                 if (n > 0)
207                         nwritten += n;
208         } while (n != -1 && nwritten < len);
209         
210         if (n == -1 && (errno == EFBIG || errno == ENOSPC))
211                 fs->eos = TRUE;
212         
213         if (nwritten > 0) {
214                 stream->position += nwritten;
215         } else if (n == -1) {
216                 /* error and nothing written */
217                 return -1;
218         }
219         
220         return nwritten;
221 }
222
223 static int
224 stream_flush (GMimeStream *stream)
225 {
226         GMimeStreamFs *fs = (GMimeStreamFs *) stream;
227         
228         if (fs->fd == -1) {
229                 errno = EBADF;
230                 return -1;
231         }
232         
233         return fsync (fs->fd);
234 }
235
236 static int
237 stream_close (GMimeStream *stream)
238 {
239         GMimeStreamFs *fs = (GMimeStreamFs *) stream;
240         int rv;
241         
242         if (fs->fd == -1)
243                 return 0;
244         
245         do {
246                 if ((rv = close (fs->fd)) == 0)
247                         fs->fd = -1;
248         } while (rv == -1 && errno == EINTR);
249         
250         return rv;
251 }
252
253 static gboolean
254 stream_eos (GMimeStream *stream)
255 {
256         GMimeStreamFs *fs = (GMimeStreamFs *) stream;
257         
258         if (fs->fd == -1)
259                 return TRUE;
260         
261         return fs->eos;
262 }
263
264 static int
265 stream_reset (GMimeStream *stream)
266 {
267         GMimeStreamFs *fs = (GMimeStreamFs *) stream;
268         
269         if (fs->fd == -1) {
270                 errno = EBADF;
271                 return -1;
272         }
273         
274         if (stream->position == stream->bound_start) {
275                 fs->eos = FALSE;
276                 return 0;
277         }
278         
279         /* FIXME: if stream_read/write is always going to lseek to
280          * make sure fd's seek position matches our own, we could just
281          * set stream->position = stream->bound_start and be done. */
282         if (lseek (fs->fd, (off_t) stream->bound_start, SEEK_SET) == -1)
283                 return -1;
284         
285         fs->eos = FALSE;
286         
287         return 0;
288 }
289
290 static gint64
291 stream_seek (GMimeStream *stream, gint64 offset, GMimeSeekWhence whence)
292 {
293         GMimeStreamFs *fs = (GMimeStreamFs *) stream;
294         gint64 real;
295         
296         if (fs->fd == -1) {
297                 errno = EBADF;
298                 return -1;
299         }
300         
301         switch (whence) {
302         case GMIME_STREAM_SEEK_SET:
303                 real = offset;
304                 break;
305         case GMIME_STREAM_SEEK_CUR:
306                 real = stream->position + offset;
307                 break;
308         case GMIME_STREAM_SEEK_END:
309                 if (offset > 0 || (stream->bound_end == -1 && !fs->eos)) {
310                         /* need to do an actual lseek() here because
311                          * we either don't know the offset of the end
312                          * of the stream and/or don't know if we can
313                          * seek past the end */
314                         if ((real = lseek (fs->fd, (off_t) offset, SEEK_END)) == -1)
315                                 return -1;
316                 } else if (fs->eos && stream->bound_end == -1) {
317                         /* seeking backwards from eos (which happens
318                          * to be our current position) */
319                         real = stream->position + offset;
320                 } else {
321                         /* seeking backwards from a known position */
322                         real = stream->bound_end + offset;
323                 }
324                 
325                 break;
326         default:
327                 g_assert_not_reached ();
328                 return -1;
329         }
330         
331         /* sanity check the resultant offset */
332         if (real < stream->bound_start) {
333                 errno = EINVAL;
334                 return -1;
335         }
336         
337         /* short-cut if we are seeking to our current position */
338         if (real == stream->position)
339                 return real;
340         
341         if (stream->bound_end != -1 && real > stream->bound_end) {
342                 errno = EINVAL;
343                 return -1;
344         }
345         
346         if ((real = lseek (fs->fd, (off_t) real, SEEK_SET)) == -1)
347                 return -1;
348         
349         /* reset eos if appropriate */
350         if ((stream->bound_end != -1 && real < stream->bound_end) ||
351             (fs->eos && real < stream->position))
352                 fs->eos = FALSE;
353         
354         stream->position = real;
355         
356         return real;
357 }
358
359 static gint64
360 stream_tell (GMimeStream *stream)
361 {
362         return stream->position;
363 }
364
365 static gint64
366 stream_length (GMimeStream *stream)
367 {
368         GMimeStreamFs *fs = (GMimeStreamFs *) stream;
369         gint64 bound_end;
370         
371         if (fs->fd == -1) {
372                 errno = EBADF;
373                 return -1;
374         }
375         
376         if (stream->bound_end != -1)
377                 return stream->bound_end - stream->bound_start;
378         
379         bound_end = lseek (fs->fd, (off_t) 0, SEEK_END);
380         lseek (fs->fd, (off_t) stream->position, SEEK_SET);
381         
382         if (bound_end < stream->bound_start) {
383                 errno = EINVAL;
384                 return -1;
385         }
386         
387         return bound_end - stream->bound_start;
388 }
389
390 static GMimeStream *
391 stream_substream (GMimeStream *stream, gint64 start, gint64 end)
392 {
393         GMimeStreamFs *fs;
394         
395         fs = g_object_newv (GMIME_TYPE_STREAM_FS, 0, NULL);
396         g_mime_stream_construct (GMIME_STREAM (fs), start, end);
397         fs->fd = GMIME_STREAM_FS (stream)->fd;
398         fs->owner = FALSE;
399         fs->eos = FALSE;
400         
401         return (GMimeStream *) fs;
402 }
403
404
405 /**
406  * g_mime_stream_fs_new:
407  * @fd: a file descriptor
408  *
409  * Creates a new #GMimeStreamFs object around @fd.
410  *
411  * Returns: a stream using @fd.
412  **/
413 GMimeStream *
414 g_mime_stream_fs_new (int fd)
415 {
416         GMimeStreamFs *fs;
417         gint64 start;
418         
419 #ifdef G_OS_WIN32
420         _setmode (fd, O_BINARY);
421 #endif
422         
423         if ((start = lseek (fd, (off_t) 0, SEEK_CUR)) == -1)
424                 start = 0;
425         
426         fs = g_object_newv (GMIME_TYPE_STREAM_FS, 0, NULL);
427         g_mime_stream_construct (GMIME_STREAM (fs), start, -1);
428         fs->owner = TRUE;
429         fs->eos = FALSE;
430         fs->fd = fd;
431         
432         return (GMimeStream *) fs;
433 }
434
435
436 /**
437  * g_mime_stream_fs_new_with_bounds:
438  * @fd: a file descriptor
439  * @start: start boundary
440  * @end: end boundary
441  *
442  * Creates a new #GMimeStreamFs object around @fd with bounds @start
443  * and @end.
444  *
445  * Returns: a stream using @fd with bounds @start and @end.
446  **/
447 GMimeStream *
448 g_mime_stream_fs_new_with_bounds (int fd, gint64 start, gint64 end)
449 {
450         GMimeStreamFs *fs;
451         
452 #ifdef G_OS_WIN32
453         _setmode (fd, O_BINARY);
454 #endif
455         
456         fs = g_object_newv (GMIME_TYPE_STREAM_FS, 0, NULL);
457         g_mime_stream_construct (GMIME_STREAM (fs), start, end);
458         fs->owner = TRUE;
459         fs->eos = FALSE;
460         fs->fd = fd;
461         
462         return (GMimeStream *) fs;
463 }
464
465
466 /**
467  * g_mime_stream_fs_get_owner:
468  * @stream: a #GMimeStreamFs
469  *
470  * Gets whether or not @stream owns the backend file descriptor.
471  *
472  * Returns: %TRUE if @stream owns the backend file descriptor or %FALSE
473  * otherwise.
474  **/
475 gboolean
476 g_mime_stream_fs_get_owner (GMimeStreamFs *stream)
477 {
478         g_return_val_if_fail (GMIME_IS_STREAM_FS (stream), FALSE);
479         
480         return stream->owner;
481 }
482
483
484 /**
485  * g_mime_stream_fs_set_owner:
486  * @stream: a #GMimeStreamFs
487  * @owner: %TRUE if this stream should own the file descriptor or %FALSE otherwise
488  *
489  * Sets whether or not @stream owns the backend file descriptor.
490  *
491  * Note: @owner should be %TRUE if the stream should close() the
492  * backend file descriptor when destroyed or %FALSE otherwise.
493  **/
494 void
495 g_mime_stream_fs_set_owner (GMimeStreamFs *stream, gboolean owner)
496 {
497         g_return_if_fail (GMIME_IS_STREAM_FS (stream));
498         
499         stream->owner = owner;
500 }