1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2018 Colin Leroy and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #  include "config.h"
21 #include "claws-features.h"
22 #endif
23 
24 #include <glib.h>
25 
26 #ifndef G_OS_WIN32
27 #include <sys/wait.h>
28 #else
29 #define WEXITSTATUS(x) (x)
30 #endif
31 
32 #include <errno.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 
37 #include "defs.h"
38 #include "codeconv.h"
39 #include "timing.h"
40 #include "file-utils.h"
41 
42 gboolean prefs_common_get_flush_metadata(void);
43 gboolean prefs_common_get_use_shred(void);
44 
safe_fclose(FILE * fp)45 static int safe_fclose(FILE *fp)
46 {
47 	int r;
48 	START_TIMING("");
49 
50 	if (fflush(fp) != 0) {
51 		return EOF;
52 	}
53 	if (prefs_common_get_flush_metadata() && fsync(fileno(fp)) != 0) {
54 		return EOF;
55 	}
56 
57 	r = fclose(fp);
58 	END_TIMING();
59 
60 	return r;
61 }
62 
63 /* Unlock, then safe-close a file pointer
64  * Safe close is done using fflush + fsync
65  * if the according preference says so.
66  */
claws_safe_fclose(FILE * fp)67 int claws_safe_fclose(FILE *fp)
68 {
69 #if HAVE_FGETS_UNLOCKED
70 	funlockfile(fp);
71 #endif
72 	return safe_fclose(fp);
73 }
74 
75 #if HAVE_FGETS_UNLOCKED
76 
77 /* Open a file and locks it once
78  * so subsequent I/O is faster
79  */
claws_fopen(const char * file,const char * mode)80 FILE *claws_fopen(const char *file, const char *mode)
81 {
82 	FILE *fp = fopen(file, mode);
83 	if (!fp)
84 		return NULL;
85 	flockfile(fp);
86 	return fp;
87 }
88 
claws_fdopen(int fd,const char * mode)89 FILE *claws_fdopen(int fd, const char *mode)
90 {
91 	FILE *fp = fdopen(fd, mode);
92 	if (!fp)
93 		return NULL;
94 	flockfile(fp);
95 	return fp;
96 }
97 
98 /* Unlocks and close a file pointer
99  */
100 
claws_fclose(FILE * fp)101 int claws_fclose(FILE *fp)
102 {
103 	funlockfile(fp);
104 	return fclose(fp);
105 }
106 #endif
107 
claws_unlink(const char * filename)108 int claws_unlink(const char *filename)
109 {
110 	GStatBuf s;
111 	static int found_shred = -1;
112 	static const gchar *args[4];
113 
114 	if (filename == NULL)
115 		return 0;
116 
117 	if (prefs_common_get_use_shred()) {
118 		if (found_shred == -1) {
119 			/* init */
120 			args[0] = g_find_program_in_path("shred");
121 			debug_print("found shred: %s\n", args[0]);
122 			found_shred = (args[0] != NULL) ? 1:0;
123 			args[1] = "-f";
124 			args[3] = NULL;
125 		}
126 		if (found_shred == 1) {
127 			if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
128 				if (s.st_nlink == 1) {
129 					gint status=0;
130 					args[2] = filename;
131 					g_spawn_sync(NULL, (gchar **)args, NULL, 0,
132 					 NULL, NULL, NULL, NULL, &status, NULL);
133 					debug_print("%s %s exited with status %d\n",
134 						args[0], filename, WEXITSTATUS(status));
135 					if (truncate(filename, 0) < 0)
136 						g_warning("couln't truncate: %s", filename);
137 				}
138 			}
139 		}
140 	}
141 	return g_unlink(filename);
142 }
143 
file_strip_crs(const gchar * file)144 gint file_strip_crs(const gchar *file)
145 {
146 	FILE *fp = NULL, *outfp = NULL;
147 	gchar buf[4096];
148 	gchar *out = get_tmp_file();
149 	if (file == NULL)
150 		goto freeout;
151 
152 	fp = claws_fopen(file, "rb");
153 	if (!fp)
154 		goto freeout;
155 
156 	outfp = claws_fopen(out, "wb");
157 	if (!outfp) {
158 		claws_fclose(fp);
159 		goto freeout;
160 	}
161 
162 	while (claws_fgets(buf, sizeof (buf), fp) != NULL) {
163 		strcrchomp(buf);
164 		if (claws_fputs(buf, outfp) == EOF) {
165 			claws_fclose(fp);
166 			claws_fclose(outfp);
167 			goto unlinkout;
168 		}
169 	}
170 
171 	claws_fclose(fp);
172 	if (claws_safe_fclose(outfp) == EOF) {
173 		goto unlinkout;
174 	}
175 
176 	if (move_file(out, file, TRUE) < 0)
177 		goto unlinkout;
178 
179 	g_free(out);
180 	return 0;
181 unlinkout:
182 	claws_unlink(out);
183 freeout:
184 	g_free(out);
185 	return -1;
186 }
187 
188 /*
189  * Append src file body to the tail of dest file.
190  * Now keep_backup has no effects.
191  */
append_file(const gchar * src,const gchar * dest,gboolean keep_backup)192 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
193 {
194 	FILE *src_fp, *dest_fp;
195 	gint n_read;
196 	gchar buf[BUFSIZ];
197 
198 	gboolean err = FALSE;
199 
200 	if ((src_fp = claws_fopen(src, "rb")) == NULL) {
201 		FILE_OP_ERROR(src, "claws_fopen");
202 		return -1;
203 	}
204 
205 	if ((dest_fp = claws_fopen(dest, "ab")) == NULL) {
206 		FILE_OP_ERROR(dest, "claws_fopen");
207 		claws_fclose(src_fp);
208 		return -1;
209 	}
210 
211 	if (change_file_mode_rw(dest_fp, dest) < 0) {
212 		FILE_OP_ERROR(dest, "chmod");
213 		g_warning("can't change file mode: %s", dest);
214 	}
215 
216 	while ((n_read = claws_fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
217 		if (n_read < sizeof(buf) && claws_ferror(src_fp))
218 			break;
219 		if (claws_fwrite(buf, 1, n_read, dest_fp) < n_read) {
220 			g_warning("writing to %s failed.", dest);
221 			claws_fclose(dest_fp);
222 			claws_fclose(src_fp);
223 			claws_unlink(dest);
224 			return -1;
225 		}
226 	}
227 
228 	if (claws_ferror(src_fp)) {
229 		FILE_OP_ERROR(src, "claws_fread");
230 		err = TRUE;
231 	}
232 	claws_fclose(src_fp);
233 	if (claws_fclose(dest_fp) == EOF) {
234 		FILE_OP_ERROR(dest, "claws_fclose");
235 		err = TRUE;
236 	}
237 
238 	if (err) {
239 		claws_unlink(dest);
240 		return -1;
241 	}
242 
243 	return 0;
244 }
245 
copy_file(const gchar * src,const gchar * dest,gboolean keep_backup)246 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
247 {
248 	FILE *src_fp, *dest_fp;
249 	gint n_read;
250 	gchar buf[BUFSIZ];
251 	gchar *dest_bak = NULL;
252 	gboolean err = FALSE;
253 
254 	if ((src_fp = claws_fopen(src, "rb")) == NULL) {
255 		FILE_OP_ERROR(src, "claws_fopen");
256 		return -1;
257 	}
258 	if (is_file_exist(dest)) {
259 		dest_bak = g_strconcat(dest, ".bak", NULL);
260 		if (rename_force(dest, dest_bak) < 0) {
261 			FILE_OP_ERROR(dest, "rename");
262 			claws_fclose(src_fp);
263 			g_free(dest_bak);
264 			return -1;
265 		}
266 	}
267 
268 	if ((dest_fp = claws_fopen(dest, "wb")) == NULL) {
269 		FILE_OP_ERROR(dest, "claws_fopen");
270 		claws_fclose(src_fp);
271 		if (dest_bak) {
272 			if (rename_force(dest_bak, dest) < 0)
273 				FILE_OP_ERROR(dest_bak, "rename");
274 			g_free(dest_bak);
275 		}
276 		return -1;
277 	}
278 
279 	if (change_file_mode_rw(dest_fp, dest) < 0) {
280 		FILE_OP_ERROR(dest, "chmod");
281 		g_warning("can't change file mode: %s", dest);
282 	}
283 
284 	while ((n_read = claws_fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
285 		if (n_read < sizeof(buf) && claws_ferror(src_fp))
286 			break;
287 		if (claws_fwrite(buf, 1, n_read, dest_fp) < n_read) {
288 			g_warning("writing to %s failed.", dest);
289 			claws_fclose(dest_fp);
290 			claws_fclose(src_fp);
291 			claws_unlink(dest);
292 			if (dest_bak) {
293 				if (rename_force(dest_bak, dest) < 0)
294 					FILE_OP_ERROR(dest_bak, "rename");
295 				g_free(dest_bak);
296 			}
297 			return -1;
298 		}
299 	}
300 
301 	if (claws_ferror(src_fp)) {
302 		FILE_OP_ERROR(src, "claws_fread");
303 		err = TRUE;
304 	}
305 	claws_fclose(src_fp);
306 	if (claws_safe_fclose(dest_fp) == EOF) {
307 		FILE_OP_ERROR(dest, "claws_fclose");
308 		err = TRUE;
309 	}
310 
311 	if (err) {
312 		claws_unlink(dest);
313 		if (dest_bak) {
314 			if (rename_force(dest_bak, dest) < 0)
315 				FILE_OP_ERROR(dest_bak, "rename");
316 			g_free(dest_bak);
317 		}
318 		return -1;
319 	}
320 
321 	if (keep_backup == FALSE && dest_bak)
322 		claws_unlink(dest_bak);
323 
324 	g_free(dest_bak);
325 
326 	return 0;
327 }
328 
move_file(const gchar * src,const gchar * dest,gboolean overwrite)329 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
330 {
331 	if (overwrite == FALSE && is_file_exist(dest)) {
332 		g_warning("move_file(): file %s already exists.", dest);
333 		return -1;
334 	}
335 
336 	if (rename_force(src, dest) == 0) return 0;
337 
338 	if (EXDEV != errno) {
339 		FILE_OP_ERROR(src, "rename");
340 		return -1;
341 	}
342 
343 	if (copy_file(src, dest, FALSE) < 0) return -1;
344 
345 	claws_unlink(src);
346 
347 	return 0;
348 }
349 
copy_file_part_to_fp(FILE * fp,off_t offset,size_t length,FILE * dest_fp)350 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
351 {
352 	gint n_read;
353 	gint bytes_left, to_read;
354 	gchar buf[BUFSIZ];
355 
356 	if (fseek(fp, offset, SEEK_SET) < 0) {
357 		perror("fseek");
358 		return -1;
359 	}
360 
361 	bytes_left = length;
362 	to_read = MIN(bytes_left, sizeof(buf));
363 
364 	while ((n_read = claws_fread(buf, sizeof(gchar), to_read, fp)) > 0) {
365 		if (n_read < to_read && claws_ferror(fp))
366 			break;
367 		if (claws_fwrite(buf, 1, n_read, dest_fp) < n_read) {
368 			return -1;
369 		}
370 		bytes_left -= n_read;
371 		if (bytes_left == 0)
372 			break;
373 		to_read = MIN(bytes_left, sizeof(buf));
374 	}
375 
376 	if (claws_ferror(fp)) {
377 		perror("claws_fread");
378 		return -1;
379 	}
380 
381 	return 0;
382 }
383 
copy_file_part(FILE * fp,off_t offset,size_t length,const gchar * dest)384 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
385 {
386 	FILE *dest_fp;
387 	gboolean err = FALSE;
388 
389 	if ((dest_fp = claws_fopen(dest, "wb")) == NULL) {
390 		FILE_OP_ERROR(dest, "claws_fopen");
391 		return -1;
392 	}
393 
394 	if (change_file_mode_rw(dest_fp, dest) < 0) {
395 		FILE_OP_ERROR(dest, "chmod");
396 		g_warning("can't change file mode: %s", dest);
397 	}
398 
399 	if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
400 		err = TRUE;
401 
402 	if (claws_safe_fclose(dest_fp) == EOF) {
403 		FILE_OP_ERROR(dest, "claws_fclose");
404 		err = TRUE;
405 	}
406 
407 	if (err) {
408 		g_warning("writing to %s failed.", dest);
409 		claws_unlink(dest);
410 		return -1;
411 	}
412 
413 	return 0;
414 }
415 
canonicalize_file(const gchar * src,const gchar * dest)416 gint canonicalize_file(const gchar *src, const gchar *dest)
417 {
418 	FILE *src_fp, *dest_fp;
419 	gchar buf[BUFFSIZE];
420 	gint len;
421 	gboolean err = FALSE;
422 	gboolean last_linebreak = FALSE;
423 
424 	if (src == NULL || dest == NULL)
425 		return -1;
426 
427 	if ((src_fp = claws_fopen(src, "rb")) == NULL) {
428 		FILE_OP_ERROR(src, "claws_fopen");
429 		return -1;
430 	}
431 
432 	if ((dest_fp = claws_fopen(dest, "wb")) == NULL) {
433 		FILE_OP_ERROR(dest, "claws_fopen");
434 		claws_fclose(src_fp);
435 		return -1;
436 	}
437 
438 	if (change_file_mode_rw(dest_fp, dest) < 0) {
439 		FILE_OP_ERROR(dest, "chmod");
440 		g_warning("can't change file mode: %s", dest);
441 	}
442 
443 	while (claws_fgets(buf, sizeof(buf), src_fp) != NULL) {
444 		gint r = 0;
445 
446 		len = strlen(buf);
447 		if (len == 0) break;
448 		last_linebreak = FALSE;
449 
450 		if (buf[len - 1] != '\n') {
451 			last_linebreak = TRUE;
452 			r = claws_fputs(buf, dest_fp);
453 		} else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
454 			r = claws_fputs(buf, dest_fp);
455 		} else {
456 			if (len > 1) {
457 				r = claws_fwrite(buf, 1, len - 1, dest_fp);
458 				if (r != (len -1))
459 					r = EOF;
460 			}
461 			if (r != EOF)
462 				r = claws_fputs("\r\n", dest_fp);
463 		}
464 
465 		if (r == EOF) {
466 			g_warning("writing to %s failed.", dest);
467 			claws_fclose(dest_fp);
468 			claws_fclose(src_fp);
469 			claws_unlink(dest);
470 			return -1;
471 		}
472 	}
473 
474 	if (last_linebreak == TRUE) {
475 		if (claws_fputs("\r\n", dest_fp) == EOF)
476 			err = TRUE;
477 	}
478 
479 	if (claws_ferror(src_fp)) {
480 		FILE_OP_ERROR(src, "claws_fgets");
481 		err = TRUE;
482 	}
483 	claws_fclose(src_fp);
484 	if (claws_safe_fclose(dest_fp) == EOF) {
485 		FILE_OP_ERROR(dest, "claws_fclose");
486 		err = TRUE;
487 	}
488 
489 	if (err) {
490 		claws_unlink(dest);
491 		return -1;
492 	}
493 
494 	return 0;
495 }
496 
canonicalize_file_replace(const gchar * file)497 gint canonicalize_file_replace(const gchar *file)
498 {
499 	gchar *tmp_file;
500 
501 	tmp_file = get_tmp_file();
502 
503 	if (canonicalize_file(file, tmp_file) < 0) {
504 		g_free(tmp_file);
505 		return -1;
506 	}
507 
508 	if (move_file(tmp_file, file, TRUE) < 0) {
509 		g_warning("can't replace file: %s", file);
510 		claws_unlink(tmp_file);
511 		g_free(tmp_file);
512 		return -1;
513 	}
514 
515 	g_free(tmp_file);
516 	return 0;
517 }
518 
519 
str_write_to_file(const gchar * str,const gchar * file,gboolean safe)520 gint str_write_to_file(const gchar *str, const gchar *file, gboolean safe)
521 {
522 	FILE *fp;
523 	size_t len;
524 	int r;
525 
526 	cm_return_val_if_fail(str != NULL, -1);
527 	cm_return_val_if_fail(file != NULL, -1);
528 
529 	if ((fp = claws_fopen(file, "wb")) == NULL) {
530 		FILE_OP_ERROR(file, "claws_fopen");
531 		return -1;
532 	}
533 
534 	len = strlen(str);
535 	if (len == 0) {
536 		claws_fclose(fp);
537 		return 0;
538 	}
539 
540 	if (claws_fwrite(str, 1, len, fp) != len) {
541 		FILE_OP_ERROR(file, "claws_fwrite");
542 		claws_fclose(fp);
543 		claws_unlink(file);
544 		return -1;
545 	}
546 
547 	if (safe) {
548 		r = claws_safe_fclose(fp);
549 	} else {
550 		r = claws_fclose(fp);
551 	}
552 
553 	if (r == EOF) {
554 		FILE_OP_ERROR(file, "claws_fclose");
555 		claws_unlink(file);
556 		return -1;
557 	}
558 
559 	return 0;
560 }
561 
file_read_stream_to_str_full(FILE * fp,gboolean recode)562 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
563 {
564 	GByteArray *array;
565 	guchar buf[BUFSIZ];
566 	gint n_read;
567 	gchar *str;
568 
569 	cm_return_val_if_fail(fp != NULL, NULL);
570 
571 	array = g_byte_array_new();
572 
573 	while ((n_read = claws_fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
574 		if (n_read < sizeof(buf) && claws_ferror(fp))
575 			break;
576 		g_byte_array_append(array, buf, n_read);
577 	}
578 
579 	if (claws_ferror(fp)) {
580 		FILE_OP_ERROR("file stream", "claws_fread");
581 		g_byte_array_free(array, TRUE);
582 		return NULL;
583 	}
584 
585 	buf[0] = '\0';
586 	g_byte_array_append(array, buf, 1);
587 	str = (gchar *)array->data;
588 	g_byte_array_free(array, FALSE);
589 
590 	if (recode && !g_utf8_validate(str, -1, NULL)) {
591 		const gchar *src_codeset, *dest_codeset;
592 		gchar *tmp = NULL;
593 		src_codeset = conv_get_locale_charset_str();
594 		dest_codeset = CS_UTF_8;
595 		tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
596 		g_free(str);
597 		str = tmp;
598 	}
599 
600 	return str;
601 }
602 
file_read_to_str_full(const gchar * file,gboolean recode)603 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
604 {
605 	FILE *fp;
606 	gchar *str;
607 	GStatBuf s;
608 #ifndef G_OS_WIN32
609 	gint fd, err;
610 	struct timeval timeout = {1, 0};
611 	fd_set fds;
612 	int fflags = 0;
613 #endif
614 
615 	cm_return_val_if_fail(file != NULL, NULL);
616 
617 	if (g_stat(file, &s) != 0) {
618 		FILE_OP_ERROR(file, "stat");
619 		return NULL;
620 	}
621 	if (S_ISDIR(s.st_mode)) {
622 		g_warning("%s: is a directory", file);
623 		return NULL;
624 	}
625 
626 #ifdef G_OS_WIN32
627 	fp = claws_fopen (file, "rb");
628 	if (fp == NULL) {
629 		FILE_OP_ERROR(file, "open");
630 		return NULL;
631 	}
632 #else
633 	/* test whether the file is readable without blocking */
634 	fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
635 	if (fd == -1) {
636 		FILE_OP_ERROR(file, "open");
637 		return NULL;
638 	}
639 
640 	FD_ZERO(&fds);
641 	FD_SET(fd, &fds);
642 
643 	/* allow for one second */
644 	err = select(fd+1, &fds, NULL, NULL, &timeout);
645 	if (err <= 0 || !FD_ISSET(fd, &fds)) {
646 		if (err < 0) {
647 			FILE_OP_ERROR(file, "select");
648 		} else {
649 			g_warning("%s: doesn't seem readable", file);
650 		}
651 		close(fd);
652 		return NULL;
653 	}
654 
655 	/* Now clear O_NONBLOCK */
656 	if ((fflags = fcntl(fd, F_GETFL)) < 0) {
657 		FILE_OP_ERROR(file, "fcntl (F_GETFL)");
658 		close(fd);
659 		return NULL;
660 	}
661 	if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
662 		FILE_OP_ERROR(file, "fcntl (F_SETFL)");
663 		close(fd);
664 		return NULL;
665 	}
666 
667 	/* get the FILE pointer */
668 	fp = claws_fdopen(fd, "rb");
669 
670 	if (fp == NULL) {
671 		FILE_OP_ERROR(file, "claws_fdopen");
672 		close(fd); /* if fp isn't NULL, we'll use claws_fclose instead! */
673 		return NULL;
674 	}
675 #endif
676 
677 	str = file_read_stream_to_str_full(fp, recode);
678 
679 	claws_fclose(fp);
680 
681 	return str;
682 }
683 
file_read_to_str(const gchar * file)684 gchar *file_read_to_str(const gchar *file)
685 {
686 	return file_read_to_str_full(file, TRUE);
687 }
file_read_stream_to_str(FILE * fp)688 gchar *file_read_stream_to_str(FILE *fp)
689 {
690 	return file_read_stream_to_str_full(fp, TRUE);
691 }
692 
file_read_to_str_no_recode(const gchar * file)693 gchar *file_read_to_str_no_recode(const gchar *file)
694 {
695 	return file_read_to_str_full(file, FALSE);
696 }
file_read_stream_to_str_no_recode(FILE * fp)697 gchar *file_read_stream_to_str_no_recode(FILE *fp)
698 {
699 	return file_read_stream_to_str_full(fp, FALSE);
700 }
701 
rename_force(const gchar * oldpath,const gchar * newpath)702 gint rename_force(const gchar *oldpath, const gchar *newpath)
703 {
704 #ifndef G_OS_UNIX
705 	if (!is_file_entry_exist(oldpath)) {
706 		errno = ENOENT;
707 		return -1;
708 	}
709 	if (is_file_exist(newpath)) {
710 		if (claws_unlink(newpath) < 0)
711 			FILE_OP_ERROR(newpath, "unlink");
712 	}
713 #endif
714 	return g_rename(oldpath, newpath);
715 }
716 
copy_dir(const gchar * src,const gchar * dst)717 gint copy_dir(const gchar *src, const gchar *dst)
718 {
719 	GDir *dir;
720 	const gchar *name;
721 
722 	if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
723 		g_warning("failed to open directory: %s", src);
724 		return -1;
725 	}
726 
727 	if (make_dir(dst) < 0)
728 		return -1;
729 
730 	while ((name = g_dir_read_name(dir)) != NULL) {
731 		gchar *old_file, *new_file;
732 		old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
733 		new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
734 		debug_print("copying: %s -> %s\n", old_file, new_file);
735 		if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
736 			gint r = copy_file(old_file, new_file, TRUE);
737 			if (r < 0) {
738 				g_dir_close(dir);
739 				return r;
740 			}
741                 }
742 #ifndef G_OS_WIN32
743                 /* Windows has no symlinks.  Or well, Vista seems to
744                    have something like this but the semantics might be
745                    different.  Thus we don't use it under Windows. */
746 		 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
747 			GError *error = NULL;
748 			gint r = 0;
749 			gchar *target = g_file_read_link(old_file, &error);
750 			if (target)
751 				r = symlink(target, new_file);
752 			g_free(target);
753 			if (r < 0) {
754 				g_dir_close(dir);
755 				return r;
756 			}
757                  }
758 #endif /*G_OS_WIN32*/
759 	        else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
760 			gint r = copy_dir(old_file, new_file);
761 			if (r < 0) {
762 				g_dir_close(dir);
763 				return r;
764 			}
765 		}
766 	}
767 	g_dir_close(dir);
768 	return 0;
769 }
770 
change_file_mode_rw(FILE * fp,const gchar * file)771 gint change_file_mode_rw(FILE *fp, const gchar *file)
772 {
773 #if HAVE_FCHMOD
774 	return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
775 #else
776 	return g_chmod(file, S_IRUSR|S_IWUSR);
777 #endif
778 }
779 
my_tmpfile(void)780 FILE *my_tmpfile(void)
781 {
782 	const gchar suffix[] = ".XXXXXX";
783 	const gchar *tmpdir;
784 	guint tmplen;
785 	const gchar *progname;
786 	guint proglen;
787 	gchar *fname;
788 	gint fd;
789 	FILE *fp;
790 #ifndef G_OS_WIN32
791 	gchar buf[2]="\0";
792 #endif
793 
794 	tmpdir = get_tmp_dir();
795 	tmplen = strlen(tmpdir);
796 	progname = g_get_prgname();
797 	if (progname == NULL)
798 		progname = "claws-mail";
799 	proglen = strlen(progname);
800 	Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
801 		return tmpfile());
802 
803 	memcpy(fname, tmpdir, tmplen);
804 	fname[tmplen] = G_DIR_SEPARATOR;
805 	memcpy(fname + tmplen + 1, progname, proglen);
806 	memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
807 
808 	fd = g_mkstemp(fname);
809 	if (fd < 0)
810 		return tmpfile();
811 
812 #ifndef G_OS_WIN32
813 	claws_unlink(fname);
814 
815 	/* verify that we can write in the file after unlinking */
816 	if (write(fd, buf, 1) < 0) {
817 		close(fd);
818 		return tmpfile();
819 	}
820 
821 #endif
822 
823 	fp = claws_fdopen(fd, "w+b");
824 	if (!fp)
825 		close(fd);
826 	else {
827 		rewind(fp);
828 		return fp;
829 	}
830 
831 	return tmpfile();
832 }
833 
get_tmpfile_in_dir(const gchar * dir,gchar ** filename)834 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
835 {
836 	int fd;
837 	*filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
838 	fd = g_mkstemp(*filename);
839 	if (fd < 0)
840 		return NULL;
841 	return claws_fdopen(fd, "w+");
842 }
843 
str_open_as_stream(const gchar * str)844 FILE *str_open_as_stream(const gchar *str)
845 {
846 	FILE *fp;
847 	size_t len;
848 
849 	cm_return_val_if_fail(str != NULL, NULL);
850 
851 	len = strlen(str);
852 
853 	fp = my_tmpfile();
854 
855 	if (!fp) {
856 		FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
857 		return NULL;
858 	}
859 
860 	len = strlen(str);
861 	if (len == 0) return fp;
862 
863 	if (claws_fwrite(str, 1, len, fp) != len) {
864 		FILE_OP_ERROR("str_open_as_stream", "claws_fwrite");
865 		claws_fclose(fp);
866 		return NULL;
867 	}
868 
869 	rewind(fp);
870 	return fp;
871 }
872