update copyright year
[claws.git] / src / common / file-utils.c
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
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  */
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  */
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
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
101 int claws_fclose(FILE *fp)
102 {
103         funlockfile(fp);
104         return fclose(fp);
105 }
106 #endif
107
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("couldn't truncate: %s", filename);
137                                 }
138                         }
139                 }
140         }
141         return g_unlink(filename);
142 }
143
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  */
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
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                         if (claws_unlink(dest) < 0)
292                                 FILE_OP_ERROR(dest, "claws_unlink");
293                         if (dest_bak) {
294                                 if (rename_force(dest_bak, dest) < 0)
295                                         FILE_OP_ERROR(dest_bak, "rename");
296                                 g_free(dest_bak);
297                         }
298                         return -1;
299                 }
300         }
301
302         if (claws_ferror(src_fp)) {
303                 FILE_OP_ERROR(src, "claws_fread");
304                 err = TRUE;
305         }
306         claws_fclose(src_fp);
307         if (claws_safe_fclose(dest_fp) == EOF) {
308                 FILE_OP_ERROR(dest, "claws_fclose");
309                 err = TRUE;
310         }
311
312         if (err) {
313                 if (claws_unlink(dest) < 0)
314                         FILE_OP_ERROR(dest, "claws_unlink");
315                 if (dest_bak) {
316                         if (rename_force(dest_bak, dest) < 0)
317                                 FILE_OP_ERROR(dest_bak, "rename");
318                         g_free(dest_bak);
319                 }
320                 return -1;
321         }
322
323         if (keep_backup == FALSE && dest_bak)
324                 if (claws_unlink(dest_bak) < 0)
325                         FILE_OP_ERROR(dest_bak, "claws_unlink");
326
327         g_free(dest_bak);
328
329         return 0;
330 }
331
332 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
333 {
334         if (overwrite == FALSE && is_file_exist(dest)) {
335                 g_warning("move_file(): file %s already exists", dest);
336                 return -1;
337         }
338
339         if (rename_force(src, dest) == 0) return 0;
340
341         if (EXDEV != errno) {
342                 FILE_OP_ERROR(src, "rename");
343                 return -1;
344         }
345
346         if (copy_file(src, dest, FALSE) < 0) return -1;
347
348         claws_unlink(src);
349
350         return 0;
351 }
352
353 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
354 {
355         gint n_read;
356         gint bytes_left, to_read;
357         gchar buf[BUFSIZ];
358
359         if (fseek(fp, offset, SEEK_SET) < 0) {
360                 perror("fseek");
361                 return -1;
362         }
363
364         bytes_left = length;
365         to_read = MIN(bytes_left, sizeof(buf));
366
367         while ((n_read = claws_fread(buf, sizeof(gchar), to_read, fp)) > 0) {
368                 if (n_read < to_read && claws_ferror(fp))
369                         break;
370                 if (claws_fwrite(buf, 1, n_read, dest_fp) < n_read) {
371                         return -1;
372                 }
373                 bytes_left -= n_read;
374                 if (bytes_left == 0)
375                         break;
376                 to_read = MIN(bytes_left, sizeof(buf));
377         }
378
379         if (claws_ferror(fp)) {
380                 perror("claws_fread");
381                 return -1;
382         }
383
384         return 0;
385 }
386
387 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
388 {
389         FILE *dest_fp;
390         gboolean err = FALSE;
391
392         if ((dest_fp = claws_fopen(dest, "wb")) == NULL) {
393                 FILE_OP_ERROR(dest, "claws_fopen");
394                 return -1;
395         }
396
397         if (change_file_mode_rw(dest_fp, dest) < 0) {
398                 FILE_OP_ERROR(dest, "chmod");
399                 g_warning("can't change file mode: %s", dest);
400         }
401
402         if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
403                 err = TRUE;
404
405         if (claws_safe_fclose(dest_fp) == EOF) {
406                 FILE_OP_ERROR(dest, "claws_fclose");
407                 err = TRUE;
408         }
409
410         if (err) {
411                 g_warning("writing to %s failed", dest);
412                 claws_unlink(dest);
413                 return -1;
414         }
415
416         return 0;
417 }
418
419 gint canonicalize_file(const gchar *src, const gchar *dest)
420 {
421         FILE *src_fp, *dest_fp;
422         gchar buf[BUFFSIZE];
423         gint len;
424         gboolean err = FALSE;
425         gboolean last_linebreak = FALSE;
426
427         if (src == NULL || dest == NULL)
428                 return -1;
429
430         if ((src_fp = claws_fopen(src, "rb")) == NULL) {
431                 FILE_OP_ERROR(src, "claws_fopen");
432                 return -1;
433         }
434
435         if ((dest_fp = claws_fopen(dest, "wb")) == NULL) {
436                 FILE_OP_ERROR(dest, "claws_fopen");
437                 claws_fclose(src_fp);
438                 return -1;
439         }
440
441         if (change_file_mode_rw(dest_fp, dest) < 0) {
442                 FILE_OP_ERROR(dest, "chmod");
443                 g_warning("can't change file mode: %s", dest);
444         }
445
446         while (claws_fgets(buf, sizeof(buf), src_fp) != NULL) {
447                 gint r = 0;
448
449                 len = strlen(buf);
450                 if (len == 0) break;
451                 last_linebreak = FALSE;
452
453                 if (buf[len - 1] != '\n') {
454                         last_linebreak = TRUE;
455                         r = claws_fputs(buf, dest_fp);
456                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
457                         r = claws_fputs(buf, dest_fp);
458                 } else {
459                         if (len > 1) {
460                                 r = claws_fwrite(buf, 1, len - 1, dest_fp);
461                                 if (r != (len -1))
462                                         r = EOF;
463                         }
464                         if (r != EOF)
465                                 r = claws_fputs("\r\n", dest_fp);
466                 }
467
468                 if (r == EOF) {
469                         g_warning("writing to %s failed", dest);
470                         claws_fclose(dest_fp);
471                         claws_fclose(src_fp);
472                         claws_unlink(dest);
473                         return -1;
474                 }
475         }
476
477         if (last_linebreak == TRUE) {
478                 if (claws_fputs("\r\n", dest_fp) == EOF)
479                         err = TRUE;
480         }
481
482         if (claws_ferror(src_fp)) {
483                 FILE_OP_ERROR(src, "claws_fgets");
484                 err = TRUE;
485         }
486         claws_fclose(src_fp);
487         if (claws_safe_fclose(dest_fp) == EOF) {
488                 FILE_OP_ERROR(dest, "claws_fclose");
489                 err = TRUE;
490         }
491
492         if (err) {
493                 claws_unlink(dest);
494                 return -1;
495         }
496
497         return 0;
498 }
499
500 gint canonicalize_file_replace(const gchar *file)
501 {
502         gchar *tmp_file;
503
504         tmp_file = get_tmp_file();
505
506         if (canonicalize_file(file, tmp_file) < 0) {
507                 g_free(tmp_file);
508                 return -1;
509         }
510
511         if (move_file(tmp_file, file, TRUE) < 0) {
512                 g_warning("can't replace file: %s", file);
513                 claws_unlink(tmp_file);
514                 g_free(tmp_file);
515                 return -1;
516         }
517
518         g_free(tmp_file);
519         return 0;
520 }
521
522
523 gint str_write_to_file(const gchar *str, const gchar *file, gboolean safe)
524 {
525         FILE *fp;
526         size_t len;
527         int r;
528
529         cm_return_val_if_fail(str != NULL, -1);
530         cm_return_val_if_fail(file != NULL, -1);
531
532         if ((fp = claws_fopen(file, "wb")) == NULL) {
533                 FILE_OP_ERROR(file, "claws_fopen");
534                 return -1;
535         }
536
537         len = strlen(str);
538         if (len == 0) {
539                 claws_fclose(fp);
540                 return 0;
541         }
542
543         if (claws_fwrite(str, 1, len, fp) != len) {
544                 FILE_OP_ERROR(file, "claws_fwrite");
545                 claws_fclose(fp);
546                 claws_unlink(file);
547                 return -1;
548         }
549
550         if (safe) {
551                 r = claws_safe_fclose(fp);
552         } else {
553                 r = claws_fclose(fp);
554         }
555
556         if (r == EOF) {
557                 FILE_OP_ERROR(file, "claws_fclose");
558                 claws_unlink(file);
559                 return -1;
560         }
561
562         return 0;
563 }
564
565 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
566 {
567         GByteArray *array;
568         guchar buf[BUFSIZ];
569         gint n_read;
570         gchar *str;
571
572         cm_return_val_if_fail(fp != NULL, NULL);
573
574         array = g_byte_array_new();
575
576         while ((n_read = claws_fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
577                 if (n_read < sizeof(buf) && claws_ferror(fp))
578                         break;
579                 g_byte_array_append(array, buf, n_read);
580         }
581
582         if (claws_ferror(fp)) {
583                 FILE_OP_ERROR("file stream", "claws_fread");
584                 g_byte_array_free(array, TRUE);
585                 return NULL;
586         }
587
588         buf[0] = '\0';
589         g_byte_array_append(array, buf, 1);
590         str = (gchar *)array->data;
591         g_byte_array_free(array, FALSE);
592
593         if (recode && !g_utf8_validate(str, -1, NULL)) {
594                 const gchar *src_codeset, *dest_codeset;
595                 gchar *tmp = NULL;
596                 src_codeset = conv_get_locale_charset_str();
597                 dest_codeset = CS_UTF_8;
598                 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
599                 g_free(str);
600                 str = tmp;
601         }
602
603         return str;
604 }
605
606 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
607 {
608         FILE *fp;
609         gchar *str;
610         GStatBuf s;
611 #ifndef G_OS_WIN32
612         gint fd, err;
613         struct timeval timeout = {1, 0};
614         fd_set fds;
615         int fflags = 0;
616 #endif
617
618         cm_return_val_if_fail(file != NULL, NULL);
619
620         if (g_stat(file, &s) != 0) {
621                 FILE_OP_ERROR(file, "stat");
622                 return NULL;
623         }
624         if (S_ISDIR(s.st_mode)) {
625                 g_warning("%s: is a directory", file);
626                 return NULL;
627         }
628
629 #ifdef G_OS_WIN32
630         fp = claws_fopen (file, "rb");
631         if (fp == NULL) {
632                 FILE_OP_ERROR(file, "open");
633                 return NULL;
634         }
635 #else     
636         /* test whether the file is readable without blocking */
637         fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
638         if (fd == -1) {
639                 FILE_OP_ERROR(file, "open");
640                 return NULL;
641         }
642
643         FD_ZERO(&fds);
644         FD_SET(fd, &fds);
645
646         /* allow for one second */
647         err = select(fd+1, &fds, NULL, NULL, &timeout);
648         if (err <= 0 || !FD_ISSET(fd, &fds)) {
649                 if (err < 0) {
650                         FILE_OP_ERROR(file, "select");
651                 } else {
652                         g_warning("%s: doesn't seem readable", file);
653                 }
654                 close(fd);
655                 return NULL;
656         }
657         
658         /* Now clear O_NONBLOCK */
659         if ((fflags = fcntl(fd, F_GETFL)) < 0) {
660                 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
661                 close(fd);
662                 return NULL;
663         }
664         if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
665                 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
666                 close(fd);
667                 return NULL;
668         }
669         
670         /* get the FILE pointer */
671         fp = claws_fdopen(fd, "rb");
672
673         if (fp == NULL) {
674                 FILE_OP_ERROR(file, "claws_fdopen");
675                 close(fd); /* if fp isn't NULL, we'll use claws_fclose instead! */
676                 return NULL;
677         }
678 #endif
679
680         str = file_read_stream_to_str_full(fp, recode);
681
682         claws_fclose(fp);
683
684         return str;
685 }
686
687 gchar *file_read_to_str(const gchar *file)
688 {
689         return file_read_to_str_full(file, TRUE);
690 }
691 gchar *file_read_stream_to_str(FILE *fp)
692 {
693         return file_read_stream_to_str_full(fp, TRUE);
694 }
695
696 gchar *file_read_to_str_no_recode(const gchar *file)
697 {
698         return file_read_to_str_full(file, FALSE);
699 }
700 gchar *file_read_stream_to_str_no_recode(FILE *fp)
701 {
702         return file_read_stream_to_str_full(fp, FALSE);
703 }
704
705 gint rename_force(const gchar *oldpath, const gchar *newpath)
706 {
707 #ifndef G_OS_UNIX
708         if (!is_file_entry_exist(oldpath)) {
709                 errno = ENOENT;
710                 return -1;
711         }
712         if (is_file_exist(newpath)) {
713                 if (claws_unlink(newpath) < 0)
714                         FILE_OP_ERROR(newpath, "unlink");
715         }
716 #endif
717         return g_rename(oldpath, newpath);
718 }
719
720 gint copy_dir(const gchar *src, const gchar *dst)
721 {
722         GDir *dir;
723         const gchar *name;
724
725         if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
726                 g_warning("failed to open directory: %s", src);
727                 return -1;
728         }
729
730         if (make_dir(dst) < 0) {
731                 g_dir_close(dir);
732                 return -1;
733         }
734
735         while ((name = g_dir_read_name(dir)) != NULL) {
736                 gchar *old_file, *new_file;
737                 gint r = 0;
738                 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
739                 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
740                 debug_print("copying: %s -> %s\n", old_file, new_file);
741                 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
742                         r = copy_file(old_file, new_file, TRUE);
743                 }
744 #ifndef G_OS_WIN32
745                 /* Windows has no symlinks.  Or well, Vista seems to
746                    have something like this but the semantics might be
747                    different. Thus we don't use it under Windows. */
748                 else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
749                         GError *error = NULL;
750                         gchar *target = g_file_read_link(old_file, &error);
751                         if (error) {
752                                 g_warning("couldn't read link: %s", error->message);
753                                 g_error_free(error);
754                         }
755                         if (target) {
756                                 r = symlink(target, new_file);
757                                 g_free(target);
758                         }
759                 }
760 #endif /*G_OS_WIN32*/
761                 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
762                         r = copy_dir(old_file, new_file);
763                 }
764                 g_free(old_file);
765                 g_free(new_file);
766                 if (r < 0) {
767                         g_dir_close(dir);
768                         return r;
769                 }
770         }
771         g_dir_close(dir);
772         return 0;
773 }
774
775 gint change_file_mode_rw(FILE *fp, const gchar *file)
776 {
777 #if HAVE_FCHMOD
778         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
779 #else
780         return g_chmod(file, S_IRUSR|S_IWUSR);
781 #endif
782 }
783
784 FILE *my_tmpfile(void)
785 {
786         const gchar suffix[] = ".XXXXXX";
787         const gchar *tmpdir;
788         guint tmplen;
789         const gchar *progname;
790         guint proglen;
791         gchar *fname;
792         gint fd;
793         FILE *fp;
794 #ifndef G_OS_WIN32
795         gchar buf[2]="\0";
796 #endif
797
798         tmpdir = get_tmp_dir();
799         tmplen = strlen(tmpdir);
800         progname = g_get_prgname();
801         if (progname == NULL)
802                 progname = "claws-mail";
803         proglen = strlen(progname);
804         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
805                 return tmpfile());
806
807         memcpy(fname, tmpdir, tmplen);
808         fname[tmplen] = G_DIR_SEPARATOR;
809         memcpy(fname + tmplen + 1, progname, proglen);
810         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
811
812         fd = g_mkstemp(fname);
813         if (fd < 0)
814                 return tmpfile();
815
816 #ifndef G_OS_WIN32
817         claws_unlink(fname);
818         
819         /* verify that we can write in the file after unlinking */
820         if (write(fd, buf, 1) < 0) {
821                 close(fd);
822                 return tmpfile();
823         }
824         
825 #endif
826
827         fp = claws_fdopen(fd, "w+b");
828         if (!fp)
829                 close(fd);
830         else {
831                 rewind(fp);
832                 return fp;
833         }
834
835         return tmpfile();
836 }
837
838 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
839 {
840         int fd;
841         *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
842         fd = g_mkstemp(*filename);
843         if (fd < 0)
844                 return NULL;
845         return claws_fdopen(fd, "w+");
846 }
847
848 FILE *str_open_as_stream(const gchar *str)
849 {
850         FILE *fp;
851         size_t len;
852
853         cm_return_val_if_fail(str != NULL, NULL);
854
855         len = strlen(str);
856
857         fp = my_tmpfile();
858
859         if (!fp) {
860                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
861                 return NULL;
862         }
863
864         len = strlen(str);
865         if (len == 0) return fp;
866
867         if (claws_fwrite(str, 1, len, fp) != len) {
868                 FILE_OP_ERROR("str_open_as_stream", "claws_fwrite");
869                 claws_fclose(fp);
870                 return NULL;
871         }
872
873         rewind(fp);
874         return fp;
875 }