1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Michael Zucchi <notzed@ximian.com>
18  *          Jeffrey Stedfast <fejj@ximian.com>
19  *          Dan Winship <danw@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 <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 
31 #include <glib/gi18n-lib.h>
32 
33 #include "camel-file-utils.h"
34 #include "camel-object.h"
35 #include "camel-operation.h"
36 #include "camel-url.h"
37 
38 #ifdef G_OS_WIN32
39 #include <winsock2.h>
40 #ifndef EWOULDBLOCK
41 #define EWOULDBLOCK EAGAIN
42 #endif
43 #endif
44 
45 #define IO_TIMEOUT (60*4)
46 
47 #define CHECK_CALL(x) G_STMT_START { \
48 	if ((x) == -1) { \
49 		g_debug ("%s: Call of '" #x "' failed: %s", G_STRFUNC, g_strerror (errno)); \
50 	} \
51 	} G_STMT_END
52 
53 /**
54  * camel_file_util_encode_uint32:
55  * @out: file to output to
56  * @value: value to output
57  *
58  * Utility function to save an uint32 to a file.
59  *
60  * Returns: 0 on success, -1 on error.
61  **/
62 gint
camel_file_util_encode_uint32(FILE * out,guint32 value)63 camel_file_util_encode_uint32 (FILE *out,
64                                guint32 value)
65 {
66 	gint i;
67 
68 	for (i = 28; i > 0; i -= 7) {
69 		if (value >= (1 << i)) {
70 			guint c = (value >> i) & 0x7f;
71 			if (fputc (c, out) == -1)
72 				return -1;
73 		}
74 	}
75 	return fputc (value | 0x80, out);
76 }
77 
78 /**
79  * camel_file_util_decode_uint32:
80  * @in: file to read from
81  * @dest: pointer to a variable to store the value in
82  *
83  * Retrieve an encoded uint32 from a file.
84  *
85  * Returns: 0 on success, -1 on error.  @*dest will contain the
86  * decoded value.
87  **/
88 gint
camel_file_util_decode_uint32(FILE * in,guint32 * dest)89 camel_file_util_decode_uint32 (FILE *in,
90                                guint32 *dest)
91 {
92 	guint32 value = 0;
93 	gint v;
94 
95         /* until we get the last byte, keep decoding 7 bits at a time */
96 	while ( ((v = fgetc (in)) & 0x80) == 0 && v != EOF) {
97 		value |= v;
98 		value <<= 7;
99 	}
100 	if (v == EOF) {
101 		*dest = value >> 7;
102 		return -1;
103 	}
104 	*dest = value | (v & 0x7f);
105 
106 	return 0;
107 }
108 
109 /**
110  * camel_file_util_encode_fixed_int32:
111  * @out: file to output to
112  * @value: value to output
113  *
114  * Encode a gint32, performing no compression, but converting
115  * to network order.
116  *
117  * Returns: 0 on success, -1 on error.
118  **/
119 gint
camel_file_util_encode_fixed_int32(FILE * out,gint32 value)120 camel_file_util_encode_fixed_int32 (FILE *out,
121                                     gint32 value)
122 {
123 	guint32 save;
124 
125 	save = g_htonl (value);
126 	if (fwrite (&save, sizeof (save), 1, out) != 1)
127 		return -1;
128 	return 0;
129 }
130 
131 /**
132  * camel_file_util_decode_fixed_int32:
133  * @in: file to read from
134  * @dest: pointer to a variable to store the value in
135  *
136  * Retrieve a gint32.
137  *
138  * Returns: 0 on success, -1 on error.
139  **/
140 gint
camel_file_util_decode_fixed_int32(FILE * in,gint32 * dest)141 camel_file_util_decode_fixed_int32 (FILE *in,
142                                     gint32 *dest)
143 {
144 	guint32 save;
145 
146 	if (fread (&save, sizeof (save), 1, in) == 1) {
147 		*dest = g_ntohl (save);
148 		return 0;
149 	} else {
150 		return -1;
151 	}
152 }
153 
154 #define CFU_ENCODE_T(type) \
155 gint \
156 camel_file_util_encode_##type (FILE *out, type value) \
157 { \
158 	gint i; \
159  \
160 	for (i = sizeof (type) - 1; i >= 0; i--) { \
161 		if (fputc ((value >> (i * 8)) & 0xff, out) == -1) \
162 			return -1; \
163 	} \
164 	return 0; \
165 }
166 
167 #define CFU_DECODE_T(type) \
168 gint \
169 camel_file_util_decode_##type (FILE *in, type *dest) \
170 { \
171 	type save = 0; \
172 	gint i = sizeof (type) - 1; \
173 	gint v = EOF; \
174  \
175 	while (i >= 0 && (v = fgetc (in)) != EOF) { \
176 		save |= ((type) v) << (i * 8); \
177 		i--; \
178 	} \
179 	*dest = save; \
180 	if (v == EOF) \
181 		return -1; \
182 	return 0; \
183 }
184 
185 /**
186  * camel_file_util_encode_time_t:
187  * @out: file to output to
188  * @value: value to output
189  *
190  * Encode a time_t value to the file.
191  *
192  * Returns: 0 on success, -1 on error.
193  **/
194 CFU_ENCODE_T (time_t)
195 
196 /**
197  * camel_file_util_decode_time_t:
198  * @in: file to read from
199  * @dest: pointer to a variable to store the value in
200  *
201  * Decode a time_t value.
202  *
203  * Returns: 0 on success, -1 on error.
204  **/
CFU_DECODE_T(time_t)205 CFU_DECODE_T (time_t)
206 
207 /**
208  * camel_file_util_encode_off_t:
209  * @out: file to output to
210  * @value: value to output
211  *
212  * Encode an off_t type.
213  *
214  * Returns: 0 on success, -1 on error.
215  **/
216 CFU_ENCODE_T (off_t)
217 
218 /**
219  * camel_file_util_decode_off_t:
220  * @in: file to read from
221  * @dest: pointer to a variable to put the value in
222  *
223  * Decode an off_t type.
224  *
225  * Returns: 0 on success, -1 on failure.
226  **/
227 CFU_DECODE_T (off_t)
228 
229 /**
230  * camel_file_util_encode_gsize:
231  * @out: file to output to
232  * @value: value to output
233  *
234  * Encode an gsize type.
235  *
236  * Returns: 0 on success, -1 on error.
237  **/
238 CFU_ENCODE_T (gsize)
239 
240 /**
241  * camel_file_util_decode_gsize:
242  * @in: file to read from
243  * @dest: pointer to a variable to put the value in
244  *
245  * Decode an gsize type.
246  *
247  * Returns: 0 on success, -1 on failure.
248  **/
249 CFU_DECODE_T (gsize)
250 
251 /**
252  * camel_file_util_encode_string:
253  * @out: file to output to
254  * @str: value to output
255  *
256  * Encode a normal string and save it in the output file.
257  *
258  * Returns: 0 on success, -1 on error.
259  **/
260 gint
261 camel_file_util_encode_string (FILE *out,
262                                const gchar *str)
263 {
264 	register gint len;
265 
266 	if (str == NULL)
267 		return camel_file_util_encode_uint32 (out, 1);
268 
269 	if ((len = strlen (str)) > 65536)
270 		len = 65536;
271 
272 	if (camel_file_util_encode_uint32 (out, len + 1) == -1)
273 		return -1;
274 	if (len == 0 || fwrite (str, sizeof (gchar), len, out) == len)
275 		return 0;
276 	return -1;
277 }
278 
279 /**
280  * camel_file_util_decode_string:
281  * @in: file to read from
282  * @str: pointer to a variable to store the value in
283  *
284  * Decode a normal string from the input file.
285  *
286  * Returns: 0 on success, -1 on error.
287  **/
288 gint
camel_file_util_decode_string(FILE * in,gchar ** str)289 camel_file_util_decode_string (FILE *in,
290                                gchar **str)
291 {
292 	guint32 len;
293 	register gchar *ret;
294 
295 	if (camel_file_util_decode_uint32 (in, &len) == -1) {
296 		*str = NULL;
297 		return -1;
298 	}
299 
300 	len--;
301 	if (len > 65536) {
302 		*str = NULL;
303 		return -1;
304 	}
305 
306 	ret = g_malloc (len + 1);
307 	if (len > 0 && fread (ret, sizeof (gchar), len, in) != len) {
308 		g_free (ret);
309 		*str = NULL;
310 		return -1;
311 	}
312 
313 	ret[len] = 0;
314 	*str = ret;
315 	return 0;
316 }
317 
318 /**
319  * camel_file_util_encode_fixed_string:
320  * @out: file to output to
321  * @str: value to output
322  * @len: total-len of str to store
323  *
324  * Encode a normal string and save it in the output file.
325  * Unlike @camel_file_util_encode_string, it pads the
326  * @str with "NULL" bytes, if @len is > strlen(str)
327  *
328  * Returns: 0 on success, -1 on error.
329  **/
330 gint
camel_file_util_encode_fixed_string(FILE * out,const gchar * str,gsize len)331 camel_file_util_encode_fixed_string (FILE *out,
332                                      const gchar *str,
333                                      gsize len)
334 {
335 	gint retval = -1;
336 
337 	/* Max size is 64K */
338 	if (len > 65536)
339 		len = 65536;
340 
341 	/* Don't allow empty strings to be written. */
342 	if (len > 0) {
343 		gchar *buf;
344 
345 		buf = g_malloc0 (len);
346 		g_strlcpy (buf, str, len);
347 
348 		if (fwrite (buf, sizeof (gchar), len, out) == len)
349 			retval = 0;
350 
351 		g_free (buf);
352 	}
353 
354 	return retval;
355 }
356 
357 /**
358  * camel_file_util_decode_fixed_string:
359  * @in: file to read from
360  * @str: pointer to a variable to store the value in
361  * @len: total-len to decode.
362  *
363  * Decode a normal string from the input file.
364  *
365  * Returns: 0 on success, -1 on error.
366  **/
367 gint
camel_file_util_decode_fixed_string(FILE * in,gchar ** str,gsize len)368 camel_file_util_decode_fixed_string (FILE *in,
369                                      gchar **str,
370                                      gsize len)
371 {
372 	register gchar *ret;
373 
374 	if (len > 65536) {
375 		*str = NULL;
376 		return -1;
377 	}
378 
379 	ret = g_malloc (len + 1);
380 	if (len > 0 && fread (ret, sizeof (gchar), len, in) != len) {
381 		g_free (ret);
382 		*str = NULL;
383 		return -1;
384 	}
385 
386 	ret[len] = 0;
387 	*str = ret;
388 	return 0;
389 }
390 
391 /**
392  * camel_file_util_safe_filename:
393  * @name: string to 'flattened' into a safe filename
394  *
395  * 'Flattens' @name into a safe filename string by hex encoding any
396  * chars that may cause problems on the filesystem.
397  *
398  * Returns: a safe filename string.
399  **/
400 gchar *
camel_file_util_safe_filename(const gchar * name)401 camel_file_util_safe_filename (const gchar *name)
402 {
403 #ifdef G_OS_WIN32
404 	const gchar *unsafe_chars = "/?()'*<>:\"\\|";
405 #else
406 	const gchar *unsafe_chars = "/?()'*";
407 #endif
408 
409 	if (name == NULL)
410 		return NULL;
411 
412 	return camel_url_encode (name, unsafe_chars);
413 }
414 
415 /* FIXME: poll() might be more efficient and more portable? */
416 
417 /**
418  * camel_read:
419  * @fd: file descriptor
420  * @buf: buffer to fill
421  * @n: number of bytes to read into @buf
422  * @cancellable: optional #GCancellable object, or %NULL
423  * @error: return location for a #GError, or %NULL
424  *
425  * Cancellable libc read() replacement.
426  *
427  * Code that intends to be portable to Win32 should call this function
428  * only on file descriptors returned from open(), not on sockets.
429  *
430  * Returns: number of bytes read or -1 on fail. On failure, errno will
431  * be set appropriately.
432  **/
433 gssize
camel_read(gint fd,gchar * buf,gsize n,GCancellable * cancellable,GError ** error)434 camel_read (gint fd,
435             gchar *buf,
436             gsize n,
437             GCancellable *cancellable,
438             GError **error)
439 {
440 	gssize nread;
441 	gint cancel_fd;
442 
443 	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
444 		errno = EINTR;
445 		return -1;
446 	}
447 
448 	cancel_fd = g_cancellable_get_fd (cancellable);
449 
450 	if (cancel_fd == -1) {
451 		do {
452 			nread = read (fd, buf, n);
453 		} while (nread == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
454 	} else {
455 #ifndef G_OS_WIN32
456 		gint errnosav, flags, fdmax;
457 		fd_set rdset;
458 
459 		flags = fcntl (fd, F_GETFL);
460 		CHECK_CALL (fcntl (fd, F_SETFL, flags | O_NONBLOCK));
461 
462 		do {
463 			struct timeval tv;
464 			gint res;
465 
466 			FD_ZERO (&rdset);
467 			FD_SET (fd, &rdset);
468 			FD_SET (cancel_fd, &rdset);
469 			fdmax = MAX (fd, cancel_fd) + 1;
470 			tv.tv_sec = IO_TIMEOUT;
471 			tv.tv_usec = 0;
472 			nread = -1;
473 
474 			res = select (fdmax, &rdset, 0, 0, &tv);
475 			if (res == -1)
476 				;
477 			else if (res == 0)
478 				errno = ETIMEDOUT;
479 			else if (FD_ISSET (cancel_fd, &rdset)) {
480 				errno = EINTR;
481 				goto failed;
482 			} else {
483 				do {
484 					nread = read (fd, buf, n);
485 				} while (nread == -1 && errno == EINTR);
486 			}
487 		} while (nread == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
488 	failed:
489 		errnosav = errno;
490 		CHECK_CALL (fcntl (fd, F_SETFL, flags));
491 		errno = errnosav;
492 #endif
493 	}
494 
495 	g_cancellable_release_fd (cancellable);
496 
497 	if (g_cancellable_set_error_if_cancelled (cancellable, error))
498 		return -1;
499 
500 	if (nread == -1)
501 		g_set_error (
502 			error, G_IO_ERROR,
503 			g_io_error_from_errno (errno),
504 			"%s", g_strerror (errno));
505 
506 	return nread;
507 }
508 
509 /**
510  * camel_write:
511  * @fd: file descriptor
512  * @buf: buffer to write
513  * @n: number of bytes of @buf to write
514  * @cancellable: optional #GCancellable object, or %NULL
515  * @error: return location for a #GError, or %NULL
516  *
517  * Cancellable libc write() replacement.
518  *
519  * Code that intends to be portable to Win32 should call this function
520  * only on file descriptors returned from open(), not on sockets.
521  *
522  * Returns: number of bytes written or -1 on fail. On failure, errno will
523  * be set appropriately.
524  **/
525 gssize
camel_write(gint fd,const gchar * buf,gsize n,GCancellable * cancellable,GError ** error)526 camel_write (gint fd,
527              const gchar *buf,
528              gsize n,
529              GCancellable *cancellable,
530              GError **error)
531 {
532 	gssize w, written = 0;
533 	gint cancel_fd;
534 
535 	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
536 		errno = EINTR;
537 		return -1;
538 	}
539 
540 	cancel_fd = g_cancellable_get_fd (cancellable);
541 
542 	if (cancel_fd == -1) {
543 		do {
544 			do {
545 				w = write (fd, buf + written, n - written);
546 			} while (w == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
547 			if (w > 0)
548 				written += w;
549 		} while (w != -1 && written < n);
550 	} else {
551 #ifndef G_OS_WIN32
552 		gint errnosav, flags, fdmax;
553 		fd_set rdset, wrset;
554 
555 		flags = fcntl (fd, F_GETFL);
556 		CHECK_CALL (fcntl (fd, F_SETFL, flags | O_NONBLOCK));
557 
558 		fdmax = MAX (fd, cancel_fd) + 1;
559 		do {
560 			struct timeval tv;
561 			gint res;
562 
563 			FD_ZERO (&rdset);
564 			FD_ZERO (&wrset);
565 			FD_SET (fd, &wrset);
566 			FD_SET (cancel_fd, &rdset);
567 			tv.tv_sec = IO_TIMEOUT;
568 			tv.tv_usec = 0;
569 			w = -1;
570 
571 			res = select (fdmax, &rdset, &wrset, 0, &tv);
572 			if (res == -1) {
573 				if (errno == EINTR)
574 					w = 0;
575 			} else if (res == 0)
576 				errno = ETIMEDOUT;
577 			else if (FD_ISSET (cancel_fd, &rdset))
578 				errno = EINTR;
579 			else {
580 				do {
581 					w = write (fd, buf + written, n - written);
582 				} while (w == -1 && errno == EINTR);
583 
584 				if (w == -1) {
585 					if (errno == EAGAIN || errno == EWOULDBLOCK)
586 						w = 0;
587 				} else
588 					written += w;
589 			}
590 		} while (w != -1 && written < n);
591 
592 		errnosav = errno;
593 		CHECK_CALL (fcntl (fd, F_SETFL, flags));
594 		errno = errnosav;
595 #endif
596 	}
597 
598 	g_cancellable_release_fd (cancellable);
599 
600 	if (g_cancellable_set_error_if_cancelled (cancellable, error))
601 		return -1;
602 
603 	if (w == -1) {
604 		g_set_error (
605 			error, G_IO_ERROR,
606 			g_io_error_from_errno (errno),
607 			"%s", g_strerror (errno));
608 		return -1;
609 	}
610 
611 	return written;
612 }
613 
614 /**
615  * camel_file_util_savename:
616  * @filename: a pathname
617  *
618  * Builds a pathname where the basename is of the form ".#" + the
619  * basename of @filename, for instance used in a two-stage commit file
620  * write.
621  *
622  * Returns: The new pathname.  It must be free'd with g_free().
623  **/
624 gchar *
camel_file_util_savename(const gchar * filename)625 camel_file_util_savename (const gchar *filename)
626 {
627 	gchar *dirname, *retval;
628 
629 	dirname = g_path_get_dirname (filename);
630 
631 	if (strcmp (dirname, ".") == 0) {
632 		retval = g_strconcat (".#", filename, NULL);
633 	} else {
634 		gchar *basename = g_path_get_basename (filename);
635 		gchar *newbasename = g_strconcat (".#", basename, NULL);
636 
637 		retval = g_build_filename (dirname, newbasename, NULL);
638 
639 		g_free (newbasename);
640 		g_free (basename);
641 	}
642 	g_free (dirname);
643 
644 	return retval;
645 }
646