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