1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2014 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
g_mime_stream_file_get_type(void)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
g_mime_stream_file_class_init(GMimeStreamFileClass * klass)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
g_mime_stream_file_init(GMimeStreamFile * stream,GMimeStreamFileClass * klass)118 g_mime_stream_file_init (GMimeStreamFile *stream, GMimeStreamFileClass *klass)
119 {
120 	stream->owner = TRUE;
121 	stream->fp = NULL;
122 }
123 
124 static void
g_mime_stream_file_finalize(GObject * object)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
stream_read(GMimeStream * stream,char * buf,size_t len)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
stream_write(GMimeStream * stream,const char * buf,size_t len)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
stream_flush(GMimeStream * stream)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
stream_close(GMimeStream * stream)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
stream_eos(GMimeStream * stream)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
stream_reset(GMimeStream * stream)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
stream_seek(GMimeStream * stream,gint64 offset,GMimeSeekWhence whence)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
stream_tell(GMimeStream * stream)311 stream_tell (GMimeStream *stream)
312 {
313 	return stream->position;
314 }
315 
316 static gint64
stream_length(GMimeStream * stream)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 *
stream_substream(GMimeStream * stream,gint64 start,gint64 end)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 *
g_mime_stream_file_new(FILE * fp)368 g_mime_stream_file_new (FILE *fp)
369 {
370 	GMimeStreamFile *fstream;
371 	gint64 start;
372 
373 	g_return_val_if_fail (fp != NULL, NULL);
374 
375 #ifdef G_OS_WIN32
376 	_setmode (_fileno (fp), O_BINARY);
377 #endif
378 
379 	if ((start = ftell (fp)) == -1)
380 		start = 0;
381 
382 	fstream = g_object_newv (GMIME_TYPE_STREAM_FILE, 0, NULL);
383 	g_mime_stream_construct (GMIME_STREAM (fstream), start, -1);
384 	fstream->owner = TRUE;
385 	fstream->fp = fp;
386 
387 	return GMIME_STREAM (fstream);
388 }
389 
390 
391 /**
392  * g_mime_stream_file_new_with_bounds:
393  * @fp: a FILE pointer
394  * @start: start boundary
395  * @end: end boundary
396  *
397  * Creates a new #GMimeStreamFile object around @fp with bounds @start
398  * and @end.
399  *
400  * Note: The created #GMimeStreamFile object will own the FILE pointer
401  * passed in.
402  *
403  * Returns: a stream using @fp with bounds @start and @end.
404  **/
405 GMimeStream *
g_mime_stream_file_new_with_bounds(FILE * fp,gint64 start,gint64 end)406 g_mime_stream_file_new_with_bounds (FILE *fp, gint64 start, gint64 end)
407 {
408 	GMimeStreamFile *fstream;
409 
410 	g_return_val_if_fail (fp != NULL, NULL);
411 
412 #ifdef G_OS_WIN32
413 	_setmode (_fileno (fp), O_BINARY);
414 #endif
415 
416 	fstream = g_object_newv (GMIME_TYPE_STREAM_FILE, 0, NULL);
417 	g_mime_stream_construct (GMIME_STREAM (fstream), start, end);
418 	fstream->owner = TRUE;
419 	fstream->fp = fp;
420 
421 	return GMIME_STREAM (fstream);
422 }
423 
424 
425 /**
426  * g_mime_stream_file_new_for_path:
427  * @path: the path to a file
428  * @mode: as in fopen(3)
429  *
430  * Creates a new #GMimeStreamFile object for the specified @path.
431  *
432  * Returns: a stream using for reading and/or writing to the specified
433  * file path or %NULL on error.
434  *
435  * Since: 2.6.18
436  **/
437 GMimeStream *
g_mime_stream_file_new_for_path(const char * path,const char * mode)438 g_mime_stream_file_new_for_path (const char *path, const char *mode)
439 {
440 	FILE *fp;
441 
442 	g_return_val_if_fail (path != NULL, NULL);
443 	g_return_val_if_fail (mode != NULL, NULL);
444 
445 	if (!(fp = fopen (path, mode)))
446 		return NULL;
447 
448 	return g_mime_stream_file_new (fp);
449 }
450 
451 
452 /**
453  * g_mime_stream_file_get_owner:
454  * @stream: a #GMimeStreamFile
455  *
456  * Gets whether or not @stream owns the backend FILE pointer.
457  *
458  * Returns: %TRUE if @stream owns the backend FILE pointer or %FALSE
459  * otherwise.
460  **/
461 gboolean
g_mime_stream_file_get_owner(GMimeStreamFile * stream)462 g_mime_stream_file_get_owner (GMimeStreamFile *stream)
463 {
464 	g_return_val_if_fail (GMIME_IS_STREAM_FILE (stream), FALSE);
465 
466 	return stream->owner;
467 }
468 
469 
470 /**
471  * g_mime_stream_file_set_owner:
472  * @stream: a #GMimeStreamFile
473  * @owner: %TRUE if this stream should own the FILE pointer or %FALSE otherwise
474  *
475  * Sets whether or not @stream owns the backend FILE pointer.
476  *
477  * Note: @owner should be %TRUE if the stream should fclose() the
478  * backend FILE pointer when destroyed or %FALSE otherwise.
479  **/
480 void
g_mime_stream_file_set_owner(GMimeStreamFile * stream,gboolean owner)481 g_mime_stream_file_set_owner (GMimeStreamFile *stream, gboolean owner)
482 {
483 	g_return_if_fail (GMIME_IS_STREAM_FILE (stream));
484 
485 	stream->owner = owner;
486 }
487