lease complete the changelog entry below
[claws.git] / src / common / utils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto & 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
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27
28 #include <glib/gi18n.h>
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <sys/param.h>
35
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
37 #  include <wchar.h>
38 #  include <wctype.h>
39 #endif
40 #include <stdlib.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <stdarg.h>
44 #include <sys/types.h>
45 #if HAVE_SYS_WAIT_H
46 #  include <sys/wait.h>
47 #endif
48 #include <dirent.h>
49 #include <time.h>
50 #include <regex.h>
51
52 #ifdef G_OS_UNIX
53 #include <sys/utsname.h>
54 #endif
55
56 #ifdef G_OS_WIN32
57 #  include <direct.h>
58 #  include <io.h>
59 #  include <fcntl.h>
60 #  include <w32lib.h>
61 #endif
62
63 #ifdef MAEMO
64 #include <libosso.h>
65 #ifdef CHINOOK
66 # include <tablet-browser-interface.h>
67 #else
68 # include <osso-browser-interface.h>
69 #endif
70 #endif
71
72 #include "utils.h"
73 #include "socket.h"
74 #include "../codeconv.h"
75
76 #define BUFFSIZE        8192
77
78 static gboolean debug_mode = FALSE;
79 #ifdef G_OS_WIN32
80 static GSList *tempfiles=NULL;
81 #endif
82
83 /* Return true if we are running as root.  This function should beused
84    instead of getuid () == 0.  */
85 gboolean superuser_p (void)
86 {
87 #ifdef G_OS_WIN32
88   return w32_is_administrator ();
89 #else
90   return !getuid();
91 #endif  
92 }
93
94
95
96 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
97 gint g_chdir(const gchar *path)
98 {
99 #ifdef G_OS_WIN32
100         if (G_WIN32_HAVE_WIDECHAR_API()) {
101                 wchar_t *wpath;
102                 gint retval;
103                 gint save_errno;
104
105                 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
106                 if (wpath == NULL) {
107                         errno = EINVAL;
108                         return -1;
109                 }
110
111                 retval = _wchdir(wpath);
112                 save_errno = errno;
113
114                 g_free(wpath);
115
116                 errno = save_errno;
117                 return retval;
118         } else {
119                 gchar *cp_path;
120                 gint retval;
121                 gint save_errno;
122
123                 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
124                 if (cp_path == NULL) {
125                         errno = EINVAL;
126                         return -1;
127                 }
128
129                 retval = chdir(cp_path);
130                 save_errno = errno;
131
132                 g_free(cp_path);
133
134                 errno = save_errno;
135                 return retval;
136         }
137 #else
138         return chdir(path);
139 #endif
140 }
141
142 gint g_chmod(const gchar *path, gint mode)
143 {
144 #ifdef G_OS_WIN32
145         if (G_WIN32_HAVE_WIDECHAR_API()) {
146                 wchar_t *wpath;
147                 gint retval;
148                 gint save_errno;
149
150                 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
151                 if (wpath == NULL) {
152                         errno = EINVAL;
153                         return -1;
154                 }
155
156                 retval = _wchmod(wpath, mode);
157                 save_errno = errno;
158
159                 g_free(wpath);
160
161                 errno = save_errno;
162                 return retval;
163         } else {
164                 gchar *cp_path;
165                 gint retval;
166                 gint save_errno;
167
168                 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
169                 if (cp_path == NULL) {
170                         errno = EINVAL;
171                         return -1;
172                 }
173
174                 retval = chmod(cp_path, mode);
175                 save_errno = errno;
176
177                 g_free(cp_path);
178
179                 errno = save_errno;
180                 return retval;
181         }
182 #else
183         return chmod(path, mode);
184 #endif
185 }
186 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
187
188
189 #ifdef G_OS_WIN32
190 gint mkstemp_name(const gchar *template, gchar **name_used)
191 {
192         static gulong count=0; /* W32-_mktemp only supports up to 27
193                                   tempfiles... */
194         int tmpfd;
195
196         *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
197         tmpfd = open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
198                                     (S_IRUSR | S_IWUSR));
199
200         tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
201         if (tmpfd<0) {
202                 perror(g_strdup_printf("cant create %s",*name_used));
203                 return -1;
204         }
205         else
206                 return tmpfd;
207 }
208 #endif /* G_OS_WIN32 */
209
210 #ifdef G_OS_WIN32
211 gint mkstemp(const gchar *template)
212 {
213         gchar *dummyname;
214         gint res = mkstemp_name(template, &dummyname);
215         g_free(dummyname);
216         return res;
217 }
218 #endif /* G_OS_WIN32 */
219
220 void list_free_strings(GList *list)
221 {
222         list = g_list_first(list);
223
224         while (list != NULL) {
225                 g_free(list->data);
226                 list = list->next;
227         }
228 }
229
230 void slist_free_strings(GSList *list)
231 {
232         while (list != NULL) {
233                 g_free(list->data);
234                 list = list->next;
235         }
236 }
237
238 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
239 {
240         g_free(key);
241 }
242
243 void hash_free_strings(GHashTable *table)
244 {
245         g_hash_table_foreach(table, hash_free_strings_func, NULL);
246 }
247
248 gint str_case_equal(gconstpointer v, gconstpointer v2)
249 {
250         return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
251 }
252
253 guint str_case_hash(gconstpointer key)
254 {
255         const gchar *p = key;
256         guint h = *p;
257
258         if (h) {
259                 h = g_ascii_tolower(h);
260                 for (p += 1; *p != '\0'; p++)
261                         h = (h << 5) - h + g_ascii_tolower(*p);
262         }
263
264         return h;
265 }
266
267 void ptr_array_free_strings(GPtrArray *array)
268 {
269         gint i;
270         gchar *str;
271
272         g_return_if_fail(array != NULL);
273
274         for (i = 0; i < array->len; i++) {
275                 str = g_ptr_array_index(array, i);
276                 g_free(str);
277         }
278 }
279
280 gboolean str_find(const gchar *haystack, const gchar *needle)
281 {
282         return strstr(haystack, needle) != NULL ? TRUE : FALSE;
283 }
284
285 gboolean str_case_find(const gchar *haystack, const gchar *needle)
286 {
287         return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
288 }
289
290 gint to_number(const gchar *nstr)
291 {
292         register const gchar *p;
293
294         if (*nstr == '\0') return -1;
295
296         for (p = nstr; *p != '\0'; p++)
297                 if (!g_ascii_isdigit(*p)) return -1;
298
299         return atoi(nstr);
300 }
301
302 /* convert integer into string,
303    nstr must be not lower than 11 characters length */
304 gchar *itos_buf(gchar *nstr, gint n)
305 {
306         g_snprintf(nstr, 11, "%d", n);
307         return nstr;
308 }
309
310 /* convert integer into string */
311 gchar *itos(gint n)
312 {
313         static gchar nstr[11];
314
315         return itos_buf(nstr, n);
316 }
317
318 #define divide(num,divisor,i,d)         \
319 {                                       \
320         i = num >> divisor;             \
321         d = num & ((1<<divisor)-1);     \
322         d = (d*100) >> divisor;         \
323 }
324
325 gchar *to_human_readable(size_t size)
326 {
327         static gchar str[14];
328         static gchar *b_format = NULL, *kb_format = NULL, 
329                      *mb_format = NULL, *gb_format = NULL;
330         register int t = 0, r = 0;
331         if (b_format == NULL) {
332                 b_format  = _("%dB");
333                 kb_format = _("%d.%02dKB");
334                 mb_format = _("%d.%02dMB");
335                 gb_format = _("%.2fGB");
336         }
337
338         if (size < (size_t)1024) {
339                 g_snprintf(str, sizeof(str), b_format, (gint)size);
340                 return str;
341         } else if (size >> 10 < (size_t)1024) {
342                 divide(size, 10, t, r);
343                 g_snprintf(str, sizeof(str), kb_format, t, r);
344                 return str;
345         } else if (size >> 20 < (size_t)1024) {
346                 divide(size, 20, t, r);
347                 g_snprintf(str, sizeof(str), mb_format, t, r);
348                 return str;
349         } else {
350                 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size) / (1 << 30));
351                 return str;
352         }
353 }
354
355 /* strcmp with NULL-checking */
356 gint strcmp2(const gchar *s1, const gchar *s2)
357 {
358         if (s1 == NULL || s2 == NULL)
359                 return -1;
360         else
361                 return strcmp(s1, s2);
362 }
363 /* strstr with NULL-checking */
364 gchar *strstr2(const gchar *s1, const gchar *s2)
365 {
366         if (s1 == NULL || s2 == NULL)
367                 return NULL;
368         else
369                 return strstr(s1, s2);
370 }
371 /* compare paths */
372 gint path_cmp(const gchar *s1, const gchar *s2)
373 {
374         gint len1, len2;
375         int rc;
376 #ifdef G_OS_WIN32
377         gchar *s1buf, *s2buf;
378 #endif
379
380         if (s1 == NULL || s2 == NULL) return -1;
381         if (*s1 == '\0' || *s2 == '\0') return -1;
382
383 #ifdef G_OS_WIN32
384         s1buf = g_strdup (s1);
385         s2buf = g_strdup (s2);
386         subst_char (s1buf, '/', G_DIR_SEPARATOR);
387         subst_char (s2buf, '/', G_DIR_SEPARATOR);
388         s1 = s1buf;
389         s2 = s2buf;
390 #endif /* !G_OS_WIN32 */
391
392         len1 = strlen(s1);
393         len2 = strlen(s2);
394
395         if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
396         if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
397
398         rc = strncmp(s1, s2, MAX(len1, len2));
399 #ifdef G_OS_WIN32
400         g_free (s1buf);
401         g_free (s2buf);
402 #endif /* !G_OS_WIN32 */
403         return rc;
404 }
405
406 /* remove trailing return code */
407 gchar *strretchomp(gchar *str)
408 {
409         register gchar *s;
410
411         if (!*str) return str;
412
413         for (s = str + strlen(str) - 1;
414              s >= str && (*s == '\n' || *s == '\r');
415              s--)
416                 *s = '\0';
417
418         return str;
419 }
420
421 /* remove trailing character */
422 gchar *strtailchomp(gchar *str, gchar tail_char)
423 {
424         register gchar *s;
425
426         if (!*str) return str;
427         if (tail_char == '\0') return str;
428
429         for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
430                 *s = '\0';
431
432         return str;
433 }
434
435 /* remove CR (carriage return) */
436 gchar *strcrchomp(gchar *str)
437 {
438         register gchar *s;
439
440         if (!*str) return str;
441
442         s = str + strlen(str) - 1;
443         if (*s == '\n' && s > str && *(s - 1) == '\r') {
444                 *(s - 1) = '\n';
445                 *s = '\0';
446         }
447
448         return str;
449 }
450
451 gint file_strip_crs(const gchar *file)
452 {
453         FILE *fp = NULL, *outfp = NULL;
454         gchar buf[4096];
455         gchar *out = get_tmp_file();
456         if (file == NULL)
457                 goto freeout;
458
459         fp = fopen(file, "rb");
460         if (!fp)
461                 goto freeout;
462
463         outfp = fopen(out, "wb");
464         if (!outfp) {
465                 fclose(fp);
466                 goto freeout;
467         }
468
469         while (fgets(buf, sizeof (buf), fp) != NULL) {
470                 strcrchomp(buf);
471                 if (fputs(buf, outfp) == EOF) {
472                         fclose(fp);
473                         fclose(outfp);
474                         goto unlinkout;
475                 }
476         }
477
478         fclose(fp);
479         if (fclose(outfp) == EOF) {
480                 goto unlinkout;
481         }
482         
483         if (rename_force(out, file) < 0)
484                 goto unlinkout;
485         
486         return 0;
487 unlinkout:
488         claws_unlink(out);
489 freeout:
490         g_free(out);
491         return -1;
492 }
493
494 /* Similar to `strstr' but this function ignores the case of both strings.  */
495 gchar *strcasestr(const gchar *haystack, const gchar *needle)
496 {
497         register size_t haystack_len, needle_len;
498
499         haystack_len = strlen(haystack);
500         needle_len   = strlen(needle);
501
502         if (haystack_len < needle_len || needle_len == 0)
503                 return NULL;
504
505         while (haystack_len >= needle_len) {
506                 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
507                         return (gchar *)haystack;
508                 else {
509                         haystack++;
510                         haystack_len--;
511                 }
512         }
513
514         return NULL;
515 }
516
517 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
518                    gconstpointer needle, size_t needlelen)
519 {
520         const gchar *haystack_ = (const gchar *)haystack;
521         const gchar *needle_ = (const gchar *)needle;
522         const gchar *haystack_cur = (const gchar *)haystack;
523         size_t haystack_left = haystacklen;
524
525         if (needlelen == 1)
526                 return memchr(haystack_, *needle_, haystacklen);
527
528         while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
529                != NULL) {
530                 if (haystacklen - (haystack_cur - haystack_) < needlelen)
531                         break;
532                 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
533                         return (gpointer)haystack_cur;
534                 else{
535                         haystack_cur++;
536                         haystack_left = haystacklen - (haystack_cur - haystack_);
537                 }
538         }
539
540         return NULL;
541 }
542
543 /* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
544 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
545 {
546         register const gchar *s = src;
547         register gchar *d = dest;
548
549         while (--n && *s)
550                 *d++ = *s++;
551         *d = '\0';
552
553         return dest;
554 }
555
556
557 /* Examine if next block is non-ASCII string */
558 gboolean is_next_nonascii(const gchar *s)
559 {
560         const gchar *p;
561
562         /* skip head space */
563         for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
564                 ;
565         for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
566                 if (*(guchar *)p > 127 || *(guchar *)p < 32)
567                         return TRUE;
568         }
569
570         return FALSE;
571 }
572
573 gint get_next_word_len(const gchar *s)
574 {
575         gint len = 0;
576
577         for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
578                 ;
579
580         return len;
581 }
582
583 static void trim_subject_for_compare(gchar *str)
584 {
585         gchar *srcp;
586
587         eliminate_parenthesis(str, '[', ']');
588         eliminate_parenthesis(str, '(', ')');
589         g_strstrip(str);
590
591         srcp = str + subject_get_prefix_length(str);
592         if (srcp != str)
593                 memmove(str, srcp, strlen(srcp) + 1);
594 }
595
596 static void trim_subject_for_sort(gchar *str)
597 {
598         gchar *srcp;
599
600         g_strstrip(str);
601
602         srcp = str + subject_get_prefix_length(str);
603         if (srcp != str)
604                 memmove(str, srcp, strlen(srcp) + 1);
605 }
606
607 /* compare subjects */
608 gint subject_compare(const gchar *s1, const gchar *s2)
609 {
610         gchar *str1, *str2;
611
612         if (!s1 || !s2) return -1;
613         if (!*s1 || !*s2) return -1;
614
615         Xstrdup_a(str1, s1, return -1);
616         Xstrdup_a(str2, s2, return -1);
617
618         trim_subject_for_compare(str1);
619         trim_subject_for_compare(str2);
620
621         if (!*str1 || !*str2) return -1;
622
623         return strcmp(str1, str2);
624 }
625
626 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
627 {
628         gchar *str1, *str2;
629
630         if (!s1 || !s2) return -1;
631
632         Xstrdup_a(str1, s1, return -1);
633         Xstrdup_a(str2, s2, return -1);
634
635         trim_subject_for_sort(str1);
636         trim_subject_for_sort(str2);
637
638         return g_utf8_collate(str1, str2);
639 }
640
641 void trim_subject(gchar *str)
642 {
643         register gchar *srcp;
644         gchar op, cl;
645         gint in_brace;
646
647         g_strstrip(str);
648
649         srcp = str + subject_get_prefix_length(str);
650
651         if (*srcp == '[') {
652                 op = '[';
653                 cl = ']';
654         } else if (*srcp == '(') {
655                 op = '(';
656                 cl = ')';
657         } else
658                 op = 0;
659
660         if (op) {
661                 ++srcp;
662                 in_brace = 1;
663                 while (*srcp) {
664                         if (*srcp == op)
665                                 in_brace++;
666                         else if (*srcp == cl)
667                                 in_brace--;
668                         srcp++;
669                         if (in_brace == 0)
670                                 break;
671                 }
672         }
673         while (g_ascii_isspace(*srcp)) srcp++;
674         memmove(str, srcp, strlen(srcp) + 1);
675 }
676
677 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
678 {
679         register gchar *srcp, *destp;
680         gint in_brace;
681
682         srcp = destp = str;
683
684         while ((destp = strchr(destp, op))) {
685                 in_brace = 1;
686                 srcp = destp + 1;
687                 while (*srcp) {
688                         if (*srcp == op)
689                                 in_brace++;
690                         else if (*srcp == cl)
691                                 in_brace--;
692                         srcp++;
693                         if (in_brace == 0)
694                                 break;
695                 }
696                 while (g_ascii_isspace(*srcp)) srcp++;
697                 memmove(destp, srcp, strlen(srcp) + 1);
698         }
699 }
700
701 void extract_parenthesis(gchar *str, gchar op, gchar cl)
702 {
703         register gchar *srcp, *destp;
704         gint in_brace;
705
706         srcp = destp = str;
707
708         while ((srcp = strchr(destp, op))) {
709                 if (destp > str)
710                         *destp++ = ' ';
711                 memmove(destp, srcp + 1, strlen(srcp));
712                 in_brace = 1;
713                 while(*destp) {
714                         if (*destp == op)
715                                 in_brace++;
716                         else if (*destp == cl)
717                                 in_brace--;
718
719                         if (in_brace == 0)
720                                 break;
721
722                         destp++;
723                 }
724         }
725         *destp = '\0';
726 }
727
728 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
729                                          gchar op, gchar cl)
730 {
731         register gchar *srcp, *destp;
732         gint in_brace;
733         gboolean in_quote = FALSE;
734
735         srcp = destp = str;
736
737         while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
738                 if (destp > str)
739                         *destp++ = ' ';
740                 memmove(destp, srcp + 1, strlen(srcp));
741                 in_brace = 1;
742                 while(*destp) {
743                         if (*destp == op && !in_quote)
744                                 in_brace++;
745                         else if (*destp == cl && !in_quote)
746                                 in_brace--;
747                         else if (*destp == quote_chr)
748                                 in_quote ^= TRUE;
749
750                         if (in_brace == 0)
751                                 break;
752
753                         destp++;
754                 }
755         }
756         *destp = '\0';
757 }
758
759 void extract_quote(gchar *str, gchar quote_chr)
760 {
761         register gchar *p;
762
763         if ((str = strchr(str, quote_chr))) {
764                 p = str;
765                 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
766                         memmove(p - 1, p, strlen(p) + 1);
767                         p--;
768                 }
769                 if(p) {
770                         *p = '\0';
771                         memmove(str, str + 1, p - str);
772                 }
773         }
774 }
775
776 void eliminate_address_comment(gchar *str)
777 {
778         register gchar *srcp, *destp;
779         gint in_brace;
780
781         srcp = destp = str;
782
783         while ((destp = strchr(destp, '"'))) {
784                 if ((srcp = strchr(destp + 1, '"'))) {
785                         srcp++;
786                         if (*srcp == '@') {
787                                 destp = srcp + 1;
788                         } else {
789                                 while (g_ascii_isspace(*srcp)) srcp++;
790                                 memmove(destp, srcp, strlen(srcp) + 1);
791                         }
792                 } else {
793                         *destp = '\0';
794                         break;
795                 }
796         }
797
798         srcp = destp = str;
799
800         while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
801                 in_brace = 1;
802                 srcp = destp + 1;
803                 while (*srcp) {
804                         if (*srcp == '(')
805                                 in_brace++;
806                         else if (*srcp == ')')
807                                 in_brace--;
808                         srcp++;
809                         if (in_brace == 0)
810                                 break;
811                 }
812                 while (g_ascii_isspace(*srcp)) srcp++;
813                 memmove(destp, srcp, strlen(srcp) + 1);
814         }
815 }
816
817 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
818 {
819         gboolean in_quote = FALSE;
820
821         while (*str) {
822                 if (*str == c && !in_quote)
823                         return (gchar *)str;
824                 if (*str == quote_chr)
825                         in_quote ^= TRUE;
826                 str++;
827         }
828
829         return NULL;
830 }
831
832 void extract_address(gchar *str)
833 {
834         eliminate_address_comment(str);
835         if (strchr_with_skip_quote(str, '"', '<'))
836                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
837         g_strstrip(str);
838 }
839
840 void extract_list_id_str(gchar *str)
841 {
842         if (strchr_with_skip_quote(str, '"', '<'))
843                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
844         g_strstrip(str);
845 }
846
847 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
848 {
849         gchar *work;
850         gchar *workp;
851
852         if (!str) return addr_list;
853
854         Xstrdup_a(work, str, return addr_list);
855
856         if (removecomments)
857                 eliminate_address_comment(work);
858         workp = work;
859
860         while (workp && *workp) {
861                 gchar *p, *next;
862
863                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
864                         *p = '\0';
865                         next = p + 1;
866                 } else
867                         next = NULL;
868
869                 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
870                         extract_parenthesis_with_skip_quote
871                                 (workp, '"', '<', '>');
872
873                 g_strstrip(workp);
874                 if (*workp)
875                         addr_list = g_slist_append(addr_list, g_strdup(workp));
876
877                 workp = next;
878         }
879
880         return addr_list;
881 }
882
883 GSList *address_list_append(GSList *addr_list, const gchar *str)
884 {
885         return address_list_append_real(addr_list, str, TRUE);
886 }
887
888 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
889 {
890         return address_list_append_real(addr_list, str, FALSE);
891 }
892
893 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
894 {
895         const gchar *strp;
896
897         if (!str) return msgid_list;
898         strp = str;
899
900         while (strp && *strp) {
901                 const gchar *start, *end;
902                 gchar *msgid;
903
904                 if ((start = strchr(strp, '<')) != NULL) {
905                         end = strchr(start + 1, '>');
906                         if (!end) break;
907                 } else
908                         break;
909
910                 msgid = g_strndup(start + 1, end - start - 1);
911                 g_strstrip(msgid);
912                 if (*msgid)
913                         msgid_list = g_slist_prepend(msgid_list, msgid);
914                 else
915                         g_free(msgid);
916
917                 strp = end + 1;
918         }
919
920         return msgid_list;
921 }
922
923 GSList *references_list_append(GSList *msgid_list, const gchar *str)
924 {
925         GSList *list;
926
927         list = references_list_prepend(NULL, str);
928         list = g_slist_reverse(list);
929         msgid_list = g_slist_concat(msgid_list, list);
930
931         return msgid_list;
932 }
933
934 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
935 {
936         gchar *work;
937         gchar *workp;
938
939         if (!str) return group_list;
940
941         Xstrdup_a(work, str, return group_list);
942
943         workp = work;
944
945         while (workp && *workp) {
946                 gchar *p, *next;
947
948                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
949                         *p = '\0';
950                         next = p + 1;
951                 } else
952                         next = NULL;
953
954                 g_strstrip(workp);
955                 if (*workp)
956                         group_list = g_slist_append(group_list,
957                                                     g_strdup(workp));
958
959                 workp = next;
960         }
961
962         return group_list;
963 }
964
965 GList *add_history(GList *list, const gchar *str)
966 {
967         GList *old;
968
969         g_return_val_if_fail(str != NULL, list);
970
971         old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
972         if (old) {
973                 g_free(old->data);
974                 list = g_list_remove(list, old->data);
975         } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
976                 GList *last;
977
978                 last = g_list_last(list);
979                 if (last) {
980                         g_free(last->data);
981                         list = g_list_remove(list, last->data);
982                 }
983         }
984
985         list = g_list_prepend(list, g_strdup(str));
986
987         return list;
988 }
989
990 void remove_return(gchar *str)
991 {
992         register gchar *p = str;
993
994         while (*p) {
995                 if (*p == '\n' || *p == '\r')
996                         memmove(p, p + 1, strlen(p));
997                 else
998                         p++;
999         }
1000 }
1001
1002 void remove_space(gchar *str)
1003 {
1004         register gchar *p = str;
1005         register gint spc;
1006
1007         while (*p) {
1008                 spc = 0;
1009                 while (g_ascii_isspace(*(p + spc)))
1010                         spc++;
1011                 if (spc)
1012                         memmove(p, p + spc, strlen(p + spc) + 1);
1013                 else
1014                         p++;
1015         }
1016 }
1017
1018 void unfold_line(gchar *str)
1019 {
1020         register gchar *p = str;
1021         register gint spc;
1022
1023         while (*p) {
1024                 if (*p == '\n' || *p == '\r') {
1025                         *p++ = ' ';
1026                         spc = 0;
1027                         while (g_ascii_isspace(*(p + spc)))
1028                                 spc++;
1029                         if (spc)
1030                                 memmove(p, p + spc, strlen(p + spc) + 1);
1031                 } else
1032                         p++;
1033         }
1034 }
1035
1036 void subst_char(gchar *str, gchar orig, gchar subst)
1037 {
1038         register gchar *p = str;
1039
1040         while (*p) {
1041                 if (*p == orig)
1042                         *p = subst;
1043                 p++;
1044         }
1045 }
1046
1047 void subst_chars(gchar *str, gchar *orig, gchar subst)
1048 {
1049         register gchar *p = str;
1050
1051         while (*p) {
1052                 if (strchr(orig, *p) != NULL)
1053                         *p = subst;
1054                 p++;
1055         }
1056 }
1057
1058 void subst_for_filename(gchar *str)
1059 {
1060         if (!str)
1061                 return;
1062 #ifdef G_OS_WIN32
1063         subst_chars(str, "\t\r\n\\/*:", '_');
1064 #else
1065         subst_chars(str, "\t\r\n\\/*", '_');
1066 #endif
1067 }
1068
1069 void subst_for_shellsafe_filename(gchar *str)
1070 {
1071         if (!str)
1072                 return;
1073         subst_for_filename(str);
1074         subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1075 }
1076
1077 gboolean is_ascii_str(const gchar *str)
1078 {
1079         const guchar *p = (const guchar *)str;
1080
1081         while (*p != '\0') {
1082                 if (*p != '\t' && *p != ' ' &&
1083                     *p != '\r' && *p != '\n' &&
1084                     (*p < 32 || *p >= 127))
1085                         return FALSE;
1086                 p++;
1087         }
1088
1089         return TRUE;
1090 }
1091
1092 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1093 {
1094         gchar * position = NULL;
1095         gchar * tmp_pos = NULL;
1096         int i;
1097
1098         if (quote_chars == NULL)
1099                 return FALSE;
1100
1101         for (i = 0; i < strlen(quote_chars); i++) {
1102                 tmp_pos = strrchr (str, quote_chars[i]);
1103                 if(position == NULL
1104                    || (tmp_pos != NULL && position <= tmp_pos) )
1105                         position = tmp_pos;
1106         }
1107         return position;
1108 }
1109
1110 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1111 {
1112         const gchar *first_pos;
1113         const gchar *last_pos;
1114         const gchar *p = str;
1115         gint quote_level = -1;
1116
1117         /* speed up line processing by only searching to the last '>' */
1118         if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1119                 /* skip a line if it contains a '<' before the initial '>' */
1120                 if (memchr(str, '<', first_pos - str) != NULL)
1121                         return -1;
1122                 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1123         } else
1124                 return -1;
1125
1126         while (p <= last_pos) {
1127                 while (p < last_pos) {
1128                         if (g_ascii_isspace(*p))
1129                                 p++;
1130                         else
1131                                 break;
1132                 }
1133
1134                 if (strchr(quote_chars, *p))
1135                         quote_level++;
1136                 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1137                         /* any characters are allowed except '-' and space */
1138                         while (*p != '-'
1139                                && !strchr(quote_chars, *p)
1140                                && !g_ascii_isspace(*p)
1141                                && p < last_pos)
1142                                 p++;
1143                         if (strchr(quote_chars, *p))
1144                                 quote_level++;
1145                         else
1146                                 break;
1147                 }
1148
1149                 p++;
1150         }
1151
1152         return quote_level;
1153 }
1154
1155 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1156 {
1157         const gchar *p = str, *q;
1158         gint cur_line = 0, len;
1159
1160         while ((q = strchr(p, '\n')) != NULL) {
1161                 len = q - p + 1;
1162                 if (len > max_chars) {
1163                         if (line)
1164                                 *line = cur_line;
1165                         return -1;
1166                 }
1167                 p = q + 1;
1168                 ++cur_line;
1169         }
1170
1171         len = strlen(p);
1172         if (len > max_chars) {
1173                 if (line)
1174                         *line = cur_line;
1175                 return -1;
1176         }
1177
1178         return 0;
1179 }
1180
1181 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1182 {
1183         gchar * position = NULL;
1184         gchar * tmp_pos = NULL;
1185         int i;
1186
1187         if (quote_chars == NULL)
1188                 return FALSE;
1189
1190         for (i = 0; i < strlen(quote_chars); i++) {
1191                 tmp_pos = strchr (str,  quote_chars[i]);
1192                 if(position == NULL
1193                    || (tmp_pos != NULL && position >= tmp_pos) )
1194                         position = tmp_pos;
1195         }
1196         return position;
1197 }
1198
1199 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1200 {
1201         register guint haystack_len, needle_len;
1202         gboolean in_squote = FALSE, in_dquote = FALSE;
1203
1204         haystack_len = strlen(haystack);
1205         needle_len   = strlen(needle);
1206
1207         if (haystack_len < needle_len || needle_len == 0)
1208                 return NULL;
1209
1210         while (haystack_len >= needle_len) {
1211                 if (!in_squote && !in_dquote &&
1212                     !strncmp(haystack, needle, needle_len))
1213                         return (gchar *)haystack;
1214
1215                 /* 'foo"bar"' -> foo"bar"
1216                    "foo'bar'" -> foo'bar' */
1217                 if (*haystack == '\'') {
1218                         if (in_squote)
1219                                 in_squote = FALSE;
1220                         else if (!in_dquote)
1221                                 in_squote = TRUE;
1222                 } else if (*haystack == '\"') {
1223                         if (in_dquote)
1224                                 in_dquote = FALSE;
1225                         else if (!in_squote)
1226                                 in_dquote = TRUE;
1227                 }
1228
1229                 haystack++;
1230                 haystack_len--;
1231         }
1232
1233         return NULL;
1234 }
1235
1236 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1237                             gint max_tokens)
1238 {
1239         GSList *string_list = NULL, *slist;
1240         gchar **str_array, *s, *new_str;
1241         guint i, n = 1, len;
1242
1243         g_return_val_if_fail(str != NULL, NULL);
1244         g_return_val_if_fail(delim != NULL, NULL);
1245
1246         if (max_tokens < 1)
1247                 max_tokens = G_MAXINT;
1248
1249         s = strstr_with_skip_quote(str, delim);
1250         if (s) {
1251                 guint delimiter_len = strlen(delim);
1252
1253                 do {
1254                         len = s - str;
1255                         new_str = g_strndup(str, len);
1256
1257                         if (new_str[0] == '\'' || new_str[0] == '\"') {
1258                                 if (new_str[len - 1] == new_str[0]) {
1259                                         new_str[len - 1] = '\0';
1260                                         memmove(new_str, new_str + 1, len - 1);
1261                                 }
1262                         }
1263                         string_list = g_slist_prepend(string_list, new_str);
1264                         n++;
1265                         str = s + delimiter_len;
1266                         s = strstr_with_skip_quote(str, delim);
1267                 } while (--max_tokens && s);
1268         }
1269
1270         if (*str) {
1271                 new_str = g_strdup(str);
1272                 if (new_str[0] == '\'' || new_str[0] == '\"') {
1273                         len = strlen(str);
1274                         if (new_str[len - 1] == new_str[0]) {
1275                                 new_str[len - 1] = '\0';
1276                                 memmove(new_str, new_str + 1, len - 1);
1277                         }
1278                 }
1279                 string_list = g_slist_prepend(string_list, new_str);
1280                 n++;
1281         }
1282
1283         str_array = g_new(gchar*, n);
1284
1285         i = n - 1;
1286
1287         str_array[i--] = NULL;
1288         for (slist = string_list; slist; slist = slist->next)
1289                 str_array[i--] = slist->data;
1290
1291         g_slist_free(string_list);
1292
1293         return str_array;
1294 }
1295
1296 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1297 {
1298         gchar *abbrev_group;
1299         gchar *ap;
1300         const gchar *p = group;
1301         const gchar *last;
1302
1303         g_return_val_if_fail(group != NULL, NULL);
1304
1305         last = group + strlen(group);
1306         abbrev_group = ap = g_malloc(strlen(group) + 1);
1307
1308         while (*p) {
1309                 while (*p == '.')
1310                         *ap++ = *p++;
1311                 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1312                         *ap++ = *p++;
1313                         while (*p != '.') p++;
1314                 } else {
1315                         strcpy(ap, p);
1316                         return abbrev_group;
1317                 }
1318         }
1319
1320         *ap = '\0';
1321         return abbrev_group;
1322 }
1323
1324 gchar *trim_string(const gchar *str, gint len)
1325 {
1326         const gchar *p = str;
1327         gint mb_len;
1328         gchar *new_str;
1329         gint new_len = 0;
1330
1331         if (!str) return NULL;
1332         if (strlen(str) <= len)
1333                 return g_strdup(str);
1334         if (g_utf8_validate(str, -1, NULL) == FALSE)
1335                 return g_strdup(str);
1336
1337         while (*p != '\0') {
1338                 mb_len = g_utf8_skip[*(guchar *)p];
1339                 if (mb_len == 0)
1340                         break;
1341                 else if (new_len + mb_len > len)
1342                         break;
1343
1344                 new_len += mb_len;
1345                 p += mb_len;
1346         }
1347
1348         Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1349         return g_strconcat(new_str, "...", NULL);
1350 }
1351
1352 GList *uri_list_extract_filenames(const gchar *uri_list)
1353 {
1354         GList *result = NULL;
1355         const gchar *p, *q;
1356         gchar *escaped_utf8uri;
1357
1358         p = uri_list;
1359
1360         while (p) {
1361                 if (*p != '#') {
1362                         while (g_ascii_isspace(*p)) p++;
1363                         if (!strncmp(p, "file:", 5)) {
1364                                 q = p;
1365                                 q += 5;
1366                                 while (*q && *q != '\n' && *q != '\r') q++;
1367
1368                                 if (q > p) {
1369                                         gchar *file, *locale_file = NULL;
1370                                         q--;
1371                                         while (q > p && g_ascii_isspace(*q))
1372                                                 q--;
1373                                         Xalloca(escaped_utf8uri, q - p + 2,
1374                                                 return result);
1375                                         Xalloca(file, q - p + 2,
1376                                                 return result);
1377                                         *file = '\0';
1378                                         strncpy(escaped_utf8uri, p, q - p + 1);
1379                                         escaped_utf8uri[q - p + 1] = '\0';
1380                                         decode_uri(file, escaped_utf8uri);
1381                     /*
1382                      * g_filename_from_uri() rejects escaped/locale encoded uri
1383                      * string which come from Nautilus.
1384                      */
1385                                         if (g_utf8_validate(file, -1, NULL))
1386                                                 locale_file
1387                                                         = conv_codeset_strdup(
1388                                                                 file + 5,
1389                                                                 CS_UTF_8,
1390                                                                 conv_get_locale_charset_str());
1391                                         if (!locale_file)
1392                                                 locale_file = g_strdup(file + 5);
1393                                         result = g_list_append(result, locale_file);
1394                                 }
1395                         }
1396                 }
1397                 p = strchr(p, '\n');
1398                 if (p) p++;
1399         }
1400
1401         return result;
1402 }
1403
1404 /* Converts two-digit hexadecimal to decimal.  Used for unescaping escaped
1405  * characters
1406  */
1407 static gint axtoi(const gchar *hexstr)
1408 {
1409         gint hi, lo, result;
1410
1411         hi = hexstr[0];
1412         if ('0' <= hi && hi <= '9') {
1413                 hi -= '0';
1414         } else
1415                 if ('a' <= hi && hi <= 'f') {
1416                         hi -= ('a' - 10);
1417                 } else
1418                         if ('A' <= hi && hi <= 'F') {
1419                                 hi -= ('A' - 10);
1420                         }
1421
1422         lo = hexstr[1];
1423         if ('0' <= lo && lo <= '9') {
1424                 lo -= '0';
1425         } else
1426                 if ('a' <= lo && lo <= 'f') {
1427                         lo -= ('a'-10);
1428                 } else
1429                         if ('A' <= lo && lo <= 'F') {
1430                                 lo -= ('A' - 10);
1431                         }
1432         result = lo + (16 * hi);
1433         return result;
1434 }
1435
1436 gboolean is_uri_string(const gchar *str)
1437 {
1438         while (str && *str && g_ascii_isspace(*str))
1439                 str++;
1440         return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1441                 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1442                 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1443                 g_ascii_strncasecmp(str, "www.", 4) == 0);
1444 }
1445
1446 gchar *get_uri_path(const gchar *uri)
1447 {
1448         while (uri && *uri && g_ascii_isspace(*uri))
1449                 uri++;
1450         if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1451                 return (gchar *)(uri + 7);
1452         else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1453                 return (gchar *)(uri + 8);
1454         else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1455                 return (gchar *)(uri + 6);
1456         else
1457                 return (gchar *)uri;
1458 }
1459
1460 gint get_uri_len(const gchar *str)
1461 {
1462         const gchar *p;
1463
1464         if (is_uri_string(str)) {
1465                 for (p = str; *p != '\0'; p++) {
1466                         if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1467                                 break;
1468                 }
1469                 return p - str;
1470         }
1471
1472         return 0;
1473 }
1474
1475 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1476  * plusses, and escape characters are used)
1477  */
1478 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1479 {
1480         gchar *dec = decoded_uri;
1481         const gchar *enc = encoded_uri;
1482
1483         while (*enc) {
1484                 if (*enc == '%') {
1485                         enc++;
1486                         if (isxdigit((guchar)enc[0]) &&
1487                             isxdigit((guchar)enc[1])) {
1488                                 *dec = axtoi(enc);
1489                                 dec++;
1490                                 enc += 2;
1491                         }
1492                 } else {
1493                         if (with_plus && *enc == '+')
1494                                 *dec = ' ';
1495                         else
1496                                 *dec = *enc;
1497                         dec++;
1498                         enc++;
1499                 }
1500         }
1501
1502         *dec = '\0';
1503 }
1504
1505 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1506 {
1507         decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1508 }
1509
1510 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1511 {
1512     gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1513     decode_uri_with_plus(buffer, encoded_uri, FALSE);
1514     return buffer;
1515 }
1516
1517 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1518                      gchar **subject, gchar **body, gchar ***attach)
1519 {
1520         gchar *tmp_mailto;
1521         gchar *p;
1522         const gchar *forbidden_uris[] = { ".gnupg/",
1523                                           "/etc/passwd",
1524                                           "/etc/shadow",
1525                                           ".ssh/",
1526                                           "../",
1527                                           NULL };
1528         gint num_attach = 0;
1529         gchar **my_att = NULL;
1530
1531         Xstrdup_a(tmp_mailto, mailto, return -1);
1532
1533         if (!strncmp(tmp_mailto, "mailto:", 7))
1534                 tmp_mailto += 7;
1535
1536         p = strchr(tmp_mailto, '?');
1537         if (p) {
1538                 *p = '\0';
1539                 p++;
1540         }
1541
1542         if (to && !*to)
1543                 *to = decode_uri_gdup(tmp_mailto);
1544
1545         my_att = g_malloc(sizeof(char *));
1546         my_att[0] = NULL;
1547
1548         while (p) {
1549                 gchar *field, *value;
1550
1551                 field = p;
1552
1553                 p = strchr(p, '=');
1554                 if (!p) break;
1555                 *p = '\0';
1556                 p++;
1557
1558                 value = p;
1559
1560                 p = strchr(p, '&');
1561                 if (p) {
1562                         *p = '\0';
1563                         p++;
1564                 }
1565
1566                 if (*value == '\0') continue;
1567
1568                 if (from && !g_ascii_strcasecmp(field, "from")) {
1569                         if (!*from) {
1570                                 *from = decode_uri_gdup(value);
1571                         } else {
1572                                 gchar *tmp = decode_uri_gdup(value);
1573                                 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1574                                 g_free(*from);
1575                                 *from = new_from;
1576                         }
1577                 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1578                         if (!*cc) {
1579                                 *cc = decode_uri_gdup(value);
1580                         } else {
1581                                 gchar *tmp = decode_uri_gdup(value);
1582                                 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1583                                 g_free(*cc);
1584                                 *cc = new_cc;
1585                         }
1586                 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1587                         if (!*bcc) {
1588                                 *bcc = decode_uri_gdup(value);
1589                         } else {
1590                                 gchar *tmp = decode_uri_gdup(value);
1591                                 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1592                                 g_free(*bcc);
1593                                 *bcc = new_bcc;
1594                         }
1595                 } else if (subject && !*subject &&
1596                            !g_ascii_strcasecmp(field, "subject")) {
1597                         *subject = decode_uri_gdup(value);
1598                 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1599                         *body = decode_uri_gdup(value);
1600                 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1601                         gchar *tmp = decode_uri_gdup(value);
1602                         if (!g_file_get_contents(value, body, NULL, NULL)) {
1603                                 g_error("Error: couldn't set insert file '%s' in body\n", value);
1604                         }
1605                         g_free(tmp);
1606                         tmp = NULL;
1607                 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1608                         int i = 0;
1609                         gchar *tmp = decode_uri_gdup(value);
1610                         for (; forbidden_uris[i]; i++) {
1611                                 if (strstr(tmp, forbidden_uris[i])) {
1612                                         g_print("Refusing to attach '%s', potential private data leak\n",
1613                                                         tmp);
1614                                         g_free(tmp);
1615                                         tmp = NULL;
1616                                         break;
1617                                 }
1618                         }
1619                         if (tmp) {
1620                                 /* attach is correct */
1621                                 num_attach++;
1622                                 printf("realloc my_att %d\n", (num_attach+1));
1623                                 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1624                                 my_att[num_attach-1] = tmp;
1625                                 my_att[num_attach] = NULL;
1626                         }
1627                 }
1628         }
1629
1630         if (attach)
1631                 *attach = my_att;
1632         return 0;
1633 }
1634
1635
1636 #ifdef G_OS_WIN32
1637 #include <windows.h>
1638 #ifndef CSIDL_APPDATA
1639 #define CSIDL_APPDATA 0x001a
1640 #endif
1641 #ifndef CSIDL_LOCAL_APPDATA
1642 #define CSIDL_LOCAL_APPDATA 0x001c
1643 #endif
1644 #ifndef CSIDL_FLAG_CREATE
1645 #define CSIDL_FLAG_CREATE 0x8000
1646 #endif
1647 #define DIM(v)               (sizeof(v)/sizeof((v)[0]))
1648
1649 #define RTLD_LAZY 0
1650 const char *
1651 w32_strerror (int w32_errno)
1652 {
1653   static char strerr[256];
1654   int ec = (int)GetLastError ();
1655
1656   if (w32_errno == 0)
1657     w32_errno = ec;
1658   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1659                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1660                  strerr, DIM (strerr)-1, NULL);
1661   return strerr;
1662 }
1663
1664 static __inline__ void *
1665 dlopen (const char * name, int flag)
1666 {
1667   void * hd = LoadLibrary (name);
1668   return hd;
1669 }
1670
1671 static __inline__ void *
1672 dlsym (void * hd, const char * sym)
1673 {
1674   if (hd && sym)
1675     {
1676       void * fnc = GetProcAddress (hd, sym);
1677       if (!fnc)
1678         return NULL;
1679       return fnc;
1680     }
1681   return NULL;
1682 }
1683
1684
1685 static __inline__ const char *
1686 dlerror (void)
1687 {
1688   return w32_strerror (0);
1689 }
1690
1691
1692 static __inline__ int
1693 dlclose (void * hd)
1694 {
1695   if (hd)
1696     {
1697       FreeLibrary (hd);
1698       return 0;
1699     }
1700   return -1;
1701 }
1702
1703 static HRESULT
1704 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1705 {
1706   static int initialized;
1707   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1708
1709   if (!initialized)
1710     {
1711       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1712       void *handle;
1713       int i;
1714
1715       initialized = 1;
1716
1717       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1718         {
1719           handle = dlopen (dllnames[i], RTLD_LAZY);
1720           if (handle)
1721             {
1722               func = dlsym (handle, "SHGetFolderPathA");
1723               if (!func)
1724                 {
1725                   dlclose (handle);
1726                   handle = NULL;
1727                 }
1728             }
1729         }
1730     }
1731
1732   if (func)
1733     return func (a,b,c,d,e);
1734   else
1735     return -1;
1736 }
1737
1738 /* Returns a static string with the directroy from which the module
1739    has been loaded.  Returns an empty string on error. */
1740 static char *w32_get_module_dir(void)
1741 {
1742         static char *moddir;
1743
1744         if (!moddir) {
1745                 char name[MAX_PATH+10];
1746                 char *p;
1747
1748                 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1749                         *name = 0;
1750                 else {
1751                         p = strrchr (name, '\\');
1752                         if (p)
1753                                 *p = 0;
1754                         else
1755                                 *name = 0;
1756                 }
1757                 moddir = g_strdup (name);
1758         }
1759         return moddir;
1760 }
1761 #endif /* G_OS_WIN32 */
1762
1763 /* Return a static string with the locale dir. */
1764 const gchar *get_locale_dir(void)
1765 {
1766         static gchar *loc_dir;
1767
1768 #ifdef G_OS_WIN32
1769         if (!loc_dir)
1770                 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1771                                       "\\share\\locale", NULL);
1772 #endif
1773         if (!loc_dir)
1774                 loc_dir = LOCALEDIR;
1775         
1776         return loc_dir;
1777 }
1778
1779
1780 const gchar *get_home_dir(void)
1781 {
1782 #ifdef G_OS_WIN32
1783         static char home_dir[MAX_PATH] = "";
1784
1785         if (home_dir[0] == '\0') {
1786                 if (w32_shgetfolderpath
1787                             (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1788                              NULL, 0, home_dir) < 0)
1789                                 strcpy (home_dir, "C:\\Sylpheed");
1790         }
1791         return home_dir;
1792 #else
1793         static const gchar *homeenv = NULL;
1794
1795         if (homeenv)
1796                 return homeenv;
1797
1798         if (!homeenv && g_getenv("HOME") != NULL)
1799                 homeenv = g_strdup(g_getenv("HOME"));
1800         if (!homeenv)
1801                 homeenv = g_get_home_dir();
1802
1803         return homeenv;
1804 #endif
1805 }
1806
1807 static gchar *claws_rc_dir = NULL;
1808 static gboolean rc_dir_alt = FALSE;
1809 const gchar *get_rc_dir(void)
1810 {
1811
1812         if (!claws_rc_dir)
1813                 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1814                                      RC_DIR, NULL);
1815
1816         return claws_rc_dir;
1817 }
1818
1819 void set_rc_dir(const gchar *dir)
1820 {
1821         if (claws_rc_dir != NULL) {
1822                 g_print("Error: rc_dir already set\n");
1823         } else {
1824                 rc_dir_alt = TRUE;
1825                 if (g_path_is_absolute(dir))
1826                         claws_rc_dir = g_strdup(dir);
1827                 else {
1828                         claws_rc_dir = g_strconcat(g_get_current_dir(),
1829                                 G_DIR_SEPARATOR_S, dir, NULL);
1830                 }
1831                 debug_print("set rc_dir to %s\n", claws_rc_dir);
1832                 if (!is_dir_exist(claws_rc_dir)) {
1833                         if (make_dir_hier(claws_rc_dir) != 0) {
1834                                 g_print("Error: can't create %s\n",
1835                                 claws_rc_dir);
1836                         }
1837                 }
1838         }
1839 }
1840
1841 gboolean rc_dir_is_alt(void) {
1842         return rc_dir_alt;
1843 }
1844
1845 const gchar *get_mail_base_dir(void)
1846 {
1847 #ifdef G_OS_WIN32
1848         static gchar *mail_base_dir = NULL;
1849
1850         if (!mail_base_dir)
1851                 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1852                                             "Mailboxes", NULL);
1853
1854         return mail_base_dir;
1855 #else
1856         return get_home_dir();
1857 #endif
1858 }
1859
1860 #ifdef MAEMO
1861 const gchar *prefs_common_get_data_root(void);
1862 gchar *last_data_root = NULL;
1863 #endif
1864
1865 const gchar *get_news_cache_dir(void)
1866 {
1867         static gchar *news_cache_dir = NULL;
1868 #ifdef MAEMO
1869         const gchar *data_root = prefs_common_get_data_root();
1870         if (strcmp2(data_root, last_data_root)) {
1871                 g_free(news_cache_dir);
1872                 news_cache_dir = NULL;
1873         }
1874 #endif
1875         if (!news_cache_dir)
1876 #ifndef MAEMO
1877                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1878                                              NEWS_CACHE_DIR, NULL);
1879 #else
1880         {
1881                 if (data_root) {
1882                         news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1883                                              "Claws", G_DIR_SEPARATOR_S, 
1884                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1885                                              NEWS_CACHE_DIR, NULL);
1886                         g_free(last_data_root);
1887                         last_data_root = g_strdup(last_data_root);
1888                 } else {
1889                         news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1890                                              NEWS_CACHE_DIR, NULL);
1891                         g_free(last_data_root);
1892                         last_data_root = NULL;
1893                 }
1894         }
1895 #endif
1896         return news_cache_dir;
1897 }
1898
1899 const gchar *get_imap_cache_dir(void)
1900 {
1901         static gchar *imap_cache_dir = NULL;
1902 #ifdef MAEMO
1903         const gchar *data_root = prefs_common_get_data_root();
1904         if (strcmp2(data_root, last_data_root)) {
1905                 g_free(imap_cache_dir);
1906                 imap_cache_dir = NULL;
1907         }
1908 #endif
1909
1910         if (!imap_cache_dir)
1911 #ifndef MAEMO
1912                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1913                                              IMAP_CACHE_DIR, NULL);
1914 #else
1915         {
1916                 if (data_root) {
1917                         imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1918                                              "Claws", G_DIR_SEPARATOR_S, 
1919                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1920                                              IMAP_CACHE_DIR, NULL);
1921                         g_free(last_data_root);
1922                         last_data_root = g_strdup(last_data_root);
1923                 } else {
1924                         imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1925                                              IMAP_CACHE_DIR, NULL);
1926                         g_free(last_data_root);
1927                         last_data_root = NULL;
1928                 }
1929         }
1930 #endif
1931
1932         return imap_cache_dir;
1933 }
1934
1935 const gchar *get_mime_tmp_dir(void)
1936 {
1937         static gchar *mime_tmp_dir = NULL;
1938
1939         if (!mime_tmp_dir)
1940                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1941                                            MIME_TMP_DIR, NULL);
1942
1943         return mime_tmp_dir;
1944 }
1945
1946 const gchar *get_template_dir(void)
1947 {
1948         static gchar *template_dir = NULL;
1949
1950         if (!template_dir)
1951                 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1952                                            TEMPLATE_DIR, NULL);
1953
1954         return template_dir;
1955 }
1956
1957 /* Return the default directory for Plugins. */
1958 const gchar *get_plugin_dir(void)
1959 {
1960 #ifdef G_OS_WIN32
1961         static gchar *plugin_dir = NULL;
1962
1963         if (!plugin_dir)
1964                 plugin_dir = g_strconcat(w32_get_module_dir(),
1965                                          "\\lib\\claws-mail\\plugins\\",
1966                                          NULL);
1967         return plugin_dir;
1968 #else
1969         if (is_dir_exist(PLUGINDIR))
1970                 return PLUGINDIR;
1971         else {
1972                 static gchar *plugin_dir = NULL;
1973                 if (!plugin_dir)
1974                         plugin_dir = g_strconcat(get_rc_dir(), 
1975                                 G_DIR_SEPARATOR_S, "plugins", 
1976                                 G_DIR_SEPARATOR_S, NULL);
1977                 return plugin_dir;                      
1978         }
1979 #endif
1980 }
1981
1982 const gchar *get_tmp_dir(void)
1983 {
1984         static gchar *tmp_dir = NULL;
1985
1986         if (!tmp_dir)
1987                 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1988                                       TMP_DIR, NULL);
1989
1990         return tmp_dir;
1991 }
1992
1993 gchar *get_tmp_file(void)
1994 {
1995         gchar *tmp_file;
1996         static guint32 id = 0;
1997
1998         tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1999                                    get_tmp_dir(), G_DIR_SEPARATOR, id++);
2000
2001         return tmp_file;
2002 }
2003
2004 const gchar *get_domain_name(void)
2005 {
2006 #ifdef G_OS_UNIX
2007         static gchar *domain_name = NULL;
2008
2009         if (!domain_name) {
2010                 struct hostent *hp;
2011                 char hostname[256];
2012
2013                 if (gethostname(hostname, sizeof(hostname)) != 0) {
2014                         perror("gethostname");
2015                         domain_name = "unknown";
2016                 } else {
2017                         hostname[sizeof(hostname) - 1] = '\0';
2018                         if ((hp = my_gethostbyname(hostname)) == NULL) {
2019                                 perror("gethostbyname");
2020                                 domain_name = g_strdup(hostname);
2021                         } else {
2022                                 domain_name = g_strdup(hp->h_name);
2023                         }
2024                 }
2025                 debug_print("domain name = %s\n", domain_name);
2026         }
2027
2028         return domain_name;
2029 #else
2030         return "unknown";
2031 #endif
2032 }
2033
2034 off_t get_file_size(const gchar *file)
2035 {
2036         struct stat s;
2037
2038         if (g_stat(file, &s) < 0) {
2039                 FILE_OP_ERROR(file, "stat");
2040                 return -1;
2041         }
2042
2043         return s.st_size;
2044 }
2045
2046 time_t get_file_mtime(const gchar *file)
2047 {
2048         struct stat s;
2049
2050         if (g_stat(file, &s) < 0) {
2051                 FILE_OP_ERROR(file, "stat");
2052                 return -1;
2053         }
2054
2055         return s.st_mtime;
2056 }
2057
2058 off_t get_file_size_as_crlf(const gchar *file)
2059 {
2060         FILE *fp;
2061         off_t size = 0;
2062         gchar buf[BUFFSIZE];
2063
2064         if ((fp = g_fopen(file, "rb")) == NULL) {
2065                 FILE_OP_ERROR(file, "fopen");
2066                 return -1;
2067         }
2068
2069         while (fgets(buf, sizeof(buf), fp) != NULL) {
2070                 strretchomp(buf);
2071                 size += strlen(buf) + 2;
2072         }
2073
2074         if (ferror(fp)) {
2075                 FILE_OP_ERROR(file, "fgets");
2076                 size = -1;
2077         }
2078
2079         fclose(fp);
2080
2081         return size;
2082 }
2083
2084 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2085 {
2086         struct stat s;
2087
2088         if (file == NULL)
2089                 return FALSE;
2090
2091         if (g_stat(file, &s) < 0) {
2092                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2093                 return FALSE;
2094         }
2095
2096         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2097                 return TRUE;
2098
2099         return FALSE;
2100 }
2101
2102
2103 /* Test on whether FILE is a relative file name. This is
2104  * straightforward for Unix but more complex for Windows. */
2105 gboolean is_relative_filename(const gchar *file)
2106 {
2107         if (!file)
2108                 return TRUE;
2109 #ifdef G_OS_WIN32
2110         if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2111                 return FALSE; /* Prefixed with a hostname - this can't
2112                                * be a relative name. */
2113
2114         if ( ((*file >= 'a' && *file <= 'z')
2115               || (*file >= 'A' && *file <= 'Z'))
2116              && file[1] == ':')
2117                 file += 2;  /* Skip drive letter. */
2118
2119         return !(*file == '\\' || *file == '/');
2120 #else
2121         return !(*file == G_DIR_SEPARATOR);
2122 #endif
2123 }
2124
2125
2126 gboolean is_dir_exist(const gchar *dir)
2127 {
2128         if (dir == NULL)
2129                 return FALSE;
2130
2131         return g_file_test(dir, G_FILE_TEST_IS_DIR);
2132 }
2133
2134 gboolean is_file_entry_exist(const gchar *file)
2135 {
2136         if (file == NULL)
2137                 return FALSE;
2138
2139         return g_file_test(file, G_FILE_TEST_EXISTS);
2140 }
2141
2142 gboolean dirent_is_regular_file(struct dirent *d)
2143 {
2144 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2145         if (d->d_type == DT_REG)
2146                 return TRUE;
2147         else if (d->d_type != DT_UNKNOWN)
2148                 return FALSE;
2149 #endif
2150
2151         return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2152 }
2153
2154 gint change_dir(const gchar *dir)
2155 {
2156         gchar *prevdir = NULL;
2157
2158         if (debug_mode)
2159                 prevdir = g_get_current_dir();
2160
2161         if (g_chdir(dir) < 0) {
2162                 FILE_OP_ERROR(dir, "chdir");
2163                 if (debug_mode) g_free(prevdir);
2164                 return -1;
2165         } else if (debug_mode) {
2166                 gchar *cwd;
2167
2168                 cwd = g_get_current_dir();
2169                 if (strcmp(prevdir, cwd) != 0)
2170                         g_print("current dir: %s\n", cwd);
2171                 g_free(cwd);
2172                 g_free(prevdir);
2173         }
2174
2175         return 0;
2176 }
2177
2178 gint make_dir(const gchar *dir)
2179 {
2180         if (g_mkdir(dir, S_IRWXU) < 0) {
2181                 FILE_OP_ERROR(dir, "mkdir");
2182                 return -1;
2183         }
2184         if (g_chmod(dir, S_IRWXU) < 0)
2185                 FILE_OP_ERROR(dir, "chmod");
2186
2187         return 0;
2188 }
2189
2190 gint make_dir_hier(const gchar *dir)
2191 {
2192         gchar *parent_dir;
2193         const gchar *p;
2194
2195         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2196                 parent_dir = g_strndup(dir, p - dir);
2197                 if (*parent_dir != '\0') {
2198                         if (!is_dir_exist(parent_dir)) {
2199                                 if (make_dir(parent_dir) < 0) {
2200                                         g_free(parent_dir);
2201                                         return -1;
2202                                 }
2203                         }
2204                 }
2205                 g_free(parent_dir);
2206         }
2207
2208         if (!is_dir_exist(dir)) {
2209                 if (make_dir(dir) < 0)
2210                         return -1;
2211         }
2212
2213         return 0;
2214 }
2215
2216 gint remove_all_files(const gchar *dir)
2217 {
2218         GDir *dp;
2219         const gchar *dir_name;
2220         gchar *prev_dir;
2221
2222         prev_dir = g_get_current_dir();
2223
2224         if (g_chdir(dir) < 0) {
2225                 FILE_OP_ERROR(dir, "chdir");
2226                 g_free(prev_dir);
2227                 return -1;
2228         }
2229
2230         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2231                 g_warning("failed to open directory: %s\n", dir);
2232                 g_free(prev_dir);
2233                 return -1;
2234         }
2235
2236         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2237                 if (claws_unlink(dir_name) < 0)
2238                         FILE_OP_ERROR(dir_name, "unlink");
2239         }
2240
2241         g_dir_close(dp);
2242
2243         if (g_chdir(prev_dir) < 0) {
2244                 FILE_OP_ERROR(prev_dir, "chdir");
2245                 g_free(prev_dir);
2246                 return -1;
2247         }
2248
2249         g_free(prev_dir);
2250
2251         return 0;
2252 }
2253
2254 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2255 {
2256         GDir *dp;
2257         const gchar *dir_name;
2258         gchar *prev_dir;
2259         gint file_no;
2260
2261         prev_dir = g_get_current_dir();
2262
2263         if (g_chdir(dir) < 0) {
2264                 FILE_OP_ERROR(dir, "chdir");
2265                 g_free(prev_dir);
2266                 return -1;
2267         }
2268
2269         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2270                 g_warning("failed to open directory: %s\n", dir);
2271                 g_free(prev_dir);
2272                 return -1;
2273         }
2274
2275         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2276                 file_no = to_number(dir_name);
2277                 if (file_no > 0 && first <= file_no && file_no <= last) {
2278                         if (is_dir_exist(dir_name))
2279                                 continue;
2280                         if (claws_unlink(dir_name) < 0)
2281                                 FILE_OP_ERROR(dir_name, "unlink");
2282                 }
2283         }
2284
2285         g_dir_close(dp);
2286
2287         if (g_chdir(prev_dir) < 0) {
2288                 FILE_OP_ERROR(prev_dir, "chdir");
2289                 g_free(prev_dir);
2290                 return -1;
2291         }
2292
2293         g_free(prev_dir);
2294
2295         return 0;
2296 }
2297
2298 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2299 {
2300         GDir *dp;
2301         const gchar *dir_name;
2302         gchar *prev_dir;
2303         gint file_no;
2304
2305         prev_dir = g_get_current_dir();
2306
2307         if (g_chdir(dir) < 0) {
2308                 FILE_OP_ERROR(dir, "chdir");
2309                 g_free(prev_dir);
2310                 return -1;
2311         }
2312
2313         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2314                 FILE_OP_ERROR(dir, "opendir");
2315                 g_free(prev_dir);
2316                 return -1;
2317         }
2318
2319         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2320                 file_no = to_number(dir_name);
2321                 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2322                         debug_print("removing unwanted file %d from %s\n", file_no, dir);
2323                         if (is_dir_exist(dir_name))
2324                                 continue;
2325                         if (claws_unlink(dir_name) < 0)
2326                                 FILE_OP_ERROR(dir_name, "unlink");
2327                 }
2328         }
2329
2330         g_dir_close(dp);
2331
2332         if (g_chdir(prev_dir) < 0) {
2333                 FILE_OP_ERROR(prev_dir, "chdir");
2334                 g_free(prev_dir);
2335                 return -1;
2336         }
2337
2338         g_free(prev_dir);
2339
2340         return 0;
2341 }
2342
2343 gint remove_all_numbered_files(const gchar *dir)
2344 {
2345         return remove_numbered_files(dir, 0, UINT_MAX);
2346 }
2347
2348 gint remove_dir_recursive(const gchar *dir)
2349 {
2350         struct stat s;
2351         GDir *dp;
2352         const gchar *dir_name;
2353         gchar *prev_dir;
2354
2355         if (g_stat(dir, &s) < 0) {
2356                 FILE_OP_ERROR(dir, "stat");
2357                 if (ENOENT == errno) return 0;
2358                 return -1;
2359         }
2360
2361         if (!S_ISDIR(s.st_mode)) {
2362                 if (claws_unlink(dir) < 0) {
2363                         FILE_OP_ERROR(dir, "unlink");
2364                         return -1;
2365                 }
2366
2367                 return 0;
2368         }
2369
2370         prev_dir = g_get_current_dir();
2371         /* g_print("prev_dir = %s\n", prev_dir); */
2372
2373         if (!path_cmp(prev_dir, dir)) {
2374                 g_free(prev_dir);
2375                 if (g_chdir("..") < 0) {
2376                         FILE_OP_ERROR(dir, "chdir");
2377                         return -1;
2378                 }
2379                 prev_dir = g_get_current_dir();
2380         }
2381
2382         if (g_chdir(dir) < 0) {
2383                 FILE_OP_ERROR(dir, "chdir");
2384                 g_free(prev_dir);
2385                 return -1;
2386         }
2387
2388         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2389                 g_warning("failed to open directory: %s\n", dir);
2390                 g_chdir(prev_dir);
2391                 g_free(prev_dir);
2392                 return -1;
2393         }
2394
2395         /* remove all files in the directory */
2396         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2397                 /* g_print("removing %s\n", dir_name); */
2398
2399                 if (is_dir_exist(dir_name)) {
2400                         if (remove_dir_recursive(dir_name) < 0) {
2401                                 g_warning("can't remove directory\n");
2402                                 return -1;
2403                         }
2404                 } else {
2405                         if (claws_unlink(dir_name) < 0)
2406                                 FILE_OP_ERROR(dir_name, "unlink");
2407                 }
2408         }
2409
2410         g_dir_close(dp);
2411
2412         if (g_chdir(prev_dir) < 0) {
2413                 FILE_OP_ERROR(prev_dir, "chdir");
2414                 g_free(prev_dir);
2415                 return -1;
2416         }
2417
2418         g_free(prev_dir);
2419
2420         if (g_rmdir(dir) < 0) {
2421                 FILE_OP_ERROR(dir, "rmdir");
2422                 return -1;
2423         }
2424
2425         return 0;
2426 }
2427
2428 gint rename_force(const gchar *oldpath, const gchar *newpath)
2429 {
2430 #ifndef G_OS_UNIX
2431         if (!is_file_entry_exist(oldpath)) {
2432                 errno = ENOENT;
2433                 return -1;
2434         }
2435         if (is_file_exist(newpath)) {
2436                 if (claws_unlink(newpath) < 0)
2437                         FILE_OP_ERROR(newpath, "unlink");
2438         }
2439 #endif
2440         return g_rename(oldpath, newpath);
2441 }
2442
2443 /*
2444  * Append src file body to the tail of dest file.
2445  * Now keep_backup has no effects.
2446  */
2447 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2448 {
2449         FILE *src_fp, *dest_fp;
2450         gint n_read;
2451         gchar buf[BUFSIZ];
2452
2453         gboolean err = FALSE;
2454
2455         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2456                 FILE_OP_ERROR(src, "fopen");
2457                 return -1;
2458         }
2459
2460         if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2461                 FILE_OP_ERROR(dest, "fopen");
2462                 fclose(src_fp);
2463                 return -1;
2464         }
2465
2466         if (change_file_mode_rw(dest_fp, dest) < 0) {
2467                 FILE_OP_ERROR(dest, "chmod");
2468                 g_warning("can't change file mode\n");
2469         }
2470
2471         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2472                 if (n_read < sizeof(buf) && ferror(src_fp))
2473                         break;
2474                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2475                         g_warning("writing to %s failed.\n", dest);
2476                         fclose(dest_fp);
2477                         fclose(src_fp);
2478                         claws_unlink(dest);
2479                         return -1;
2480                 }
2481         }
2482
2483         if (ferror(src_fp)) {
2484                 FILE_OP_ERROR(src, "fread");
2485                 err = TRUE;
2486         }
2487         fclose(src_fp);
2488         if (fclose(dest_fp) == EOF) {
2489                 FILE_OP_ERROR(dest, "fclose");
2490                 err = TRUE;
2491         }
2492
2493         if (err) {
2494                 claws_unlink(dest);
2495                 return -1;
2496         }
2497
2498         return 0;
2499 }
2500
2501 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2502 {
2503         FILE *src_fp, *dest_fp;
2504         gint n_read;
2505         gchar buf[BUFSIZ];
2506         gchar *dest_bak = NULL;
2507         gboolean err = FALSE;
2508
2509         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2510                 FILE_OP_ERROR(src, "fopen");
2511                 return -1;
2512         }
2513         if (is_file_exist(dest)) {
2514                 dest_bak = g_strconcat(dest, ".bak", NULL);
2515                 if (rename_force(dest, dest_bak) < 0) {
2516                         FILE_OP_ERROR(dest, "rename");
2517                         fclose(src_fp);
2518                         g_free(dest_bak);
2519                         return -1;
2520                 }
2521         }
2522
2523         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2524                 FILE_OP_ERROR(dest, "fopen");
2525                 fclose(src_fp);
2526                 if (dest_bak) {
2527                         if (rename_force(dest_bak, dest) < 0)
2528                                 FILE_OP_ERROR(dest_bak, "rename");
2529                         g_free(dest_bak);
2530                 }
2531                 return -1;
2532         }
2533
2534         if (change_file_mode_rw(dest_fp, dest) < 0) {
2535                 FILE_OP_ERROR(dest, "chmod");
2536                 g_warning("can't change file mode\n");
2537         }
2538
2539         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2540                 if (n_read < sizeof(buf) && ferror(src_fp))
2541                         break;
2542                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2543                         g_warning("writing to %s failed.\n", dest);
2544                         fclose(dest_fp);
2545                         fclose(src_fp);
2546                         claws_unlink(dest);
2547                         if (dest_bak) {
2548                                 if (rename_force(dest_bak, dest) < 0)
2549                                         FILE_OP_ERROR(dest_bak, "rename");
2550                                 g_free(dest_bak);
2551                         }
2552                         return -1;
2553                 }
2554         }
2555
2556         if (ferror(src_fp)) {
2557                 FILE_OP_ERROR(src, "fread");
2558                 err = TRUE;
2559         }
2560         fclose(src_fp);
2561         if (fclose(dest_fp) == EOF) {
2562                 FILE_OP_ERROR(dest, "fclose");
2563                 err = TRUE;
2564         }
2565
2566         if (err) {
2567                 claws_unlink(dest);
2568                 if (dest_bak) {
2569                         if (rename_force(dest_bak, dest) < 0)
2570                                 FILE_OP_ERROR(dest_bak, "rename");
2571                         g_free(dest_bak);
2572                 }
2573                 return -1;
2574         }
2575
2576         if (keep_backup == FALSE && dest_bak)
2577                 claws_unlink(dest_bak);
2578
2579         g_free(dest_bak);
2580
2581         return 0;
2582 }
2583
2584 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2585 {
2586         if (overwrite == FALSE && is_file_exist(dest)) {
2587                 g_warning("move_file(): file %s already exists.", dest);
2588                 return -1;
2589         }
2590
2591         if (rename_force(src, dest) == 0) return 0;
2592
2593         if (EXDEV != errno) {
2594                 FILE_OP_ERROR(src, "rename");
2595                 return -1;
2596         }
2597
2598         if (copy_file(src, dest, FALSE) < 0) return -1;
2599
2600         claws_unlink(src);
2601
2602         return 0;
2603 }
2604
2605 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2606 {
2607         gint n_read;
2608         gint bytes_left, to_read;
2609         gchar buf[BUFSIZ];
2610
2611         if (fseek(fp, offset, SEEK_SET) < 0) {
2612                 perror("fseek");
2613                 return -1;
2614         }
2615
2616         bytes_left = length;
2617         to_read = MIN(bytes_left, sizeof(buf));
2618
2619         while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2620                 if (n_read < to_read && ferror(fp))
2621                         break;
2622                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2623                         return -1;
2624                 }
2625                 bytes_left -= n_read;
2626                 if (bytes_left == 0)
2627                         break;
2628                 to_read = MIN(bytes_left, sizeof(buf));
2629         }
2630
2631         if (ferror(fp)) {
2632                 perror("fread");
2633                 return -1;
2634         }
2635
2636         return 0;
2637 }
2638
2639 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2640 {
2641         FILE *dest_fp;
2642         gboolean err = FALSE;
2643
2644         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2645                 FILE_OP_ERROR(dest, "fopen");
2646                 return -1;
2647         }
2648
2649         if (change_file_mode_rw(dest_fp, dest) < 0) {
2650                 FILE_OP_ERROR(dest, "chmod");
2651                 g_warning("can't change file mode\n");
2652         }
2653
2654         if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2655                 err = TRUE;
2656
2657         if (!err && fclose(dest_fp) == EOF) {
2658                 FILE_OP_ERROR(dest, "fclose");
2659                 err = TRUE;
2660         }
2661
2662         if (err) {
2663                 g_warning("writing to %s failed.\n", dest);
2664                 claws_unlink(dest);
2665                 return -1;
2666         }
2667
2668         return 0;
2669 }
2670
2671 /* convert line endings into CRLF. If the last line doesn't end with
2672  * linebreak, add it.
2673  */
2674 gchar *canonicalize_str(const gchar *str)
2675 {
2676         const gchar *p;
2677         guint new_len = 0;
2678         gchar *out, *outp;
2679
2680         for (p = str; *p != '\0'; ++p) {
2681                 if (*p != '\r') {
2682                         ++new_len;
2683                         if (*p == '\n')
2684                                 ++new_len;
2685                 }
2686         }
2687         if (p == str || *(p - 1) != '\n')
2688                 new_len += 2;
2689
2690         out = outp = g_malloc(new_len + 1);
2691         for (p = str; *p != '\0'; ++p) {
2692                 if (*p != '\r') {
2693                         if (*p == '\n')
2694                                 *outp++ = '\r';
2695                         *outp++ = *p;
2696                 }
2697         }
2698         if (p == str || *(p - 1) != '\n') {
2699                 *outp++ = '\r';
2700                 *outp++ = '\n';
2701         }
2702         *outp = '\0';
2703
2704         return out;
2705 }
2706
2707 gint canonicalize_file(const gchar *src, const gchar *dest)
2708 {
2709         FILE *src_fp, *dest_fp;
2710         gchar buf[BUFFSIZE];
2711         gint len;
2712         gboolean err = FALSE;
2713         gboolean last_linebreak = FALSE;
2714
2715         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2716                 FILE_OP_ERROR(src, "fopen");
2717                 return -1;
2718         }
2719
2720         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2721                 FILE_OP_ERROR(dest, "fopen");
2722                 fclose(src_fp);
2723                 return -1;
2724         }
2725
2726         if (change_file_mode_rw(dest_fp, dest) < 0) {
2727                 FILE_OP_ERROR(dest, "chmod");
2728                 g_warning("can't change file mode\n");
2729         }
2730
2731         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2732                 gint r = 0;
2733
2734                 len = strlen(buf);
2735                 if (len == 0) break;
2736                 last_linebreak = FALSE;
2737
2738                 if (buf[len - 1] != '\n') {
2739                         last_linebreak = TRUE;
2740                         r = fputs(buf, dest_fp);
2741                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2742                         r = fputs(buf, dest_fp);
2743                 } else {
2744                         if (len > 1) {
2745                                 r = fwrite(buf, 1, len - 1, dest_fp);
2746                                 if (r != (len -1))
2747                                         r = EOF;
2748                         }
2749                         if (r != EOF)
2750                                 r = fputs("\r\n", dest_fp);
2751                 }
2752
2753                 if (r == EOF) {
2754                         g_warning("writing to %s failed.\n", dest);
2755                         fclose(dest_fp);
2756                         fclose(src_fp);
2757                         claws_unlink(dest);
2758                         return -1;
2759                 }
2760         }
2761
2762         if (last_linebreak == TRUE) {
2763                 if (fputs("\r\n", dest_fp) == EOF)
2764                         err = TRUE;
2765         }
2766
2767         if (ferror(src_fp)) {
2768                 FILE_OP_ERROR(src, "fgets");
2769                 err = TRUE;
2770         }
2771         fclose(src_fp);
2772         if (fclose(dest_fp) == EOF) {
2773                 FILE_OP_ERROR(dest, "fclose");
2774                 err = TRUE;
2775         }
2776
2777         if (err) {
2778                 claws_unlink(dest);
2779                 return -1;
2780         }
2781
2782         return 0;
2783 }
2784
2785 gint canonicalize_file_replace(const gchar *file)
2786 {
2787         gchar *tmp_file;
2788
2789         tmp_file = get_tmp_file();
2790
2791         if (canonicalize_file(file, tmp_file) < 0) {
2792                 g_free(tmp_file);
2793                 return -1;
2794         }
2795
2796         if (move_file(tmp_file, file, TRUE) < 0) {
2797                 g_warning("can't replace %s .\n", file);
2798                 claws_unlink(tmp_file);
2799                 g_free(tmp_file);
2800                 return -1;
2801         }
2802
2803         g_free(tmp_file);
2804         return 0;
2805 }
2806
2807 gchar *normalize_newlines(const gchar *str)
2808 {
2809         const gchar *p = str;
2810         gchar *out, *outp;
2811
2812         out = outp = g_malloc(strlen(str) + 1);
2813         for (p = str; *p != '\0'; ++p) {
2814                 if (*p == '\r') {
2815                         if (*(p + 1) != '\n')
2816                                 *outp++ = '\n';
2817                 } else
2818                         *outp++ = *p;
2819         }
2820
2821         *outp = '\0';
2822
2823         return out;
2824 }
2825
2826 gchar *get_outgoing_rfc2822_str(FILE *fp)
2827 {
2828         gchar buf[BUFFSIZE];
2829         GString *str;
2830         gchar *ret;
2831
2832         str = g_string_new(NULL);
2833
2834         /* output header part */
2835         while (fgets(buf, sizeof(buf), fp) != NULL) {
2836                 strretchomp(buf);
2837                 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2838                         gint next;
2839
2840                         for (;;) {
2841                                 next = fgetc(fp);
2842                                 if (next == EOF)
2843                                         break;
2844                                 else if (next != ' ' && next != '\t') {
2845                                         ungetc(next, fp);
2846                                         break;
2847                                 }
2848                                 if (fgets(buf, sizeof(buf), fp) == NULL)
2849                                         break;
2850                         }
2851                 } else {
2852                         g_string_append(str, buf);
2853                         g_string_append(str, "\r\n");
2854                         if (buf[0] == '\0')
2855                                 break;
2856                 }
2857         }
2858
2859         /* output body part */
2860         while (fgets(buf, sizeof(buf), fp) != NULL) {
2861                 strretchomp(buf);
2862                 if (buf[0] == '.')
2863                         g_string_append_c(str, '.');
2864                 g_string_append(str, buf);
2865                 g_string_append(str, "\r\n");
2866         }
2867
2868         ret = str->str;
2869         g_string_free(str, FALSE);
2870
2871         return ret;
2872 }
2873
2874 /*
2875  * Create a new boundary in a way that it is very unlikely that this
2876  * will occur in the following text.  It would be easy to ensure
2877  * uniqueness if everything is either quoted-printable or base64
2878  * encoded (note that conversion is allowed), but because MIME bodies
2879  * may be nested, it may happen that the same boundary has already
2880  * been used.
2881  *
2882  *   boundary := 0*69<bchars> bcharsnospace
2883  *   bchars := bcharsnospace / " "
2884  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2885  *                  "+" / "_" / "," / "-" / "." /
2886  *                  "/" / ":" / "=" / "?"
2887  *
2888  * some special characters removed because of buggy MTAs
2889  */
2890
2891 gchar *generate_mime_boundary(const gchar *prefix)
2892 {
2893         static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2894                              "abcdefghijklmnopqrstuvwxyz"
2895                              "1234567890+_./=";
2896         gchar buf_uniq[24];
2897         gint i;
2898
2899         for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2900                 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2901         buf_uniq[i] = '\0';
2902
2903         return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2904                                buf_uniq);
2905 }
2906
2907 gint change_file_mode_rw(FILE *fp, const gchar *file)
2908 {
2909 #if HAVE_FCHMOD
2910         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2911 #else
2912         return g_chmod(file, S_IRUSR|S_IWUSR);
2913 #endif
2914 }
2915
2916 FILE *my_tmpfile(void)
2917 {
2918 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2919         const gchar suffix[] = ".XXXXXX";
2920         const gchar *tmpdir;
2921         guint tmplen;
2922         const gchar *progname;
2923         guint proglen;
2924         gchar *fname;
2925         gint fd;
2926         FILE *fp;
2927         gchar buf[2]="\0";
2928
2929         tmpdir = get_tmp_dir();
2930         tmplen = strlen(tmpdir);
2931         progname = g_get_prgname();
2932         if (progname == NULL)
2933                 progname = "claws-mail";
2934         proglen = strlen(progname);
2935         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2936                 return tmpfile());
2937
2938         memcpy(fname, tmpdir, tmplen);
2939         fname[tmplen] = G_DIR_SEPARATOR;
2940         memcpy(fname + tmplen + 1, progname, proglen);
2941         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2942
2943         fd = mkstemp(fname);
2944         if (fd < 0)
2945                 return tmpfile();
2946
2947 #ifndef G_OS_WIN32
2948         claws_unlink(fname);
2949         
2950         /* verify that we can write in the file after unlinking */
2951         if (write(fd, buf, 1) < 0) {
2952                 close(fd);
2953                 return tmpfile();
2954         }
2955         
2956 #endif
2957
2958         fp = fdopen(fd, "w+b");
2959         if (!fp)
2960                 close(fd);
2961         else {
2962                 rewind(fp);
2963                 return fp;
2964         }
2965
2966 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2967
2968         return tmpfile();
2969 }
2970
2971 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2972 {
2973         int fd;
2974 #ifdef G_OS_WIN32
2975         char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2976                                           dir, G_DIR_SEPARATOR);
2977         fd = mkstemp_name(template, filename);
2978         g_free(template);
2979 #else
2980         *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2981         fd = mkstemp(*filename);
2982 #endif
2983         return fdopen(fd, "w+");
2984 }
2985
2986 FILE *str_open_as_stream(const gchar *str)
2987 {
2988         FILE *fp;
2989         size_t len;
2990
2991         g_return_val_if_fail(str != NULL, NULL);
2992
2993         fp = my_tmpfile();
2994         if (!fp) {
2995                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2996                 return NULL;
2997         }
2998
2999         len = strlen(str);
3000         if (len == 0) return fp;
3001
3002         if (fwrite(str, 1, len, fp) != len) {
3003                 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3004                 fclose(fp);
3005                 return NULL;
3006         }
3007
3008         rewind(fp);
3009         return fp;
3010 }
3011
3012 gint str_write_to_file(const gchar *str, const gchar *file)
3013 {
3014         FILE *fp;
3015         size_t len;
3016
3017         g_return_val_if_fail(str != NULL, -1);
3018         g_return_val_if_fail(file != NULL, -1);
3019
3020         if ((fp = g_fopen(file, "wb")) == NULL) {
3021                 FILE_OP_ERROR(file, "fopen");
3022                 return -1;
3023         }
3024
3025         len = strlen(str);
3026         if (len == 0) {
3027                 fclose(fp);
3028                 return 0;
3029         }
3030
3031         if (fwrite(str, 1, len, fp) != len) {
3032                 FILE_OP_ERROR(file, "fwrite");
3033                 fclose(fp);
3034                 claws_unlink(file);
3035                 return -1;
3036         }
3037
3038         if (fclose(fp) == EOF) {
3039                 FILE_OP_ERROR(file, "fclose");
3040                 claws_unlink(file);
3041                 return -1;
3042         }
3043
3044         return 0;
3045 }
3046
3047 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3048 {
3049         GByteArray *array;
3050         guchar buf[BUFSIZ];
3051         gint n_read;
3052         gchar *str;
3053
3054         g_return_val_if_fail(fp != NULL, NULL);
3055
3056         array = g_byte_array_new();
3057
3058         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3059                 if (n_read < sizeof(buf) && ferror(fp))
3060                         break;
3061                 g_byte_array_append(array, buf, n_read);
3062         }
3063
3064         if (ferror(fp)) {
3065                 FILE_OP_ERROR("file stream", "fread");
3066                 g_byte_array_free(array, TRUE);
3067                 return NULL;
3068         }
3069
3070         buf[0] = '\0';
3071         g_byte_array_append(array, buf, 1);
3072         str = (gchar *)array->data;
3073         g_byte_array_free(array, FALSE);
3074
3075         if (recode && !g_utf8_validate(str, -1, NULL)) {
3076                 const gchar *src_codeset, *dest_codeset;
3077                 gchar *tmp = NULL;
3078                 src_codeset = conv_get_locale_charset_str();
3079                 dest_codeset = CS_UTF_8;
3080                 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3081                 g_free(str);
3082                 str = tmp;
3083         }
3084
3085         return str;
3086 }
3087
3088 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3089 {
3090         FILE *fp;
3091         gchar *str;
3092
3093         g_return_val_if_fail(file != NULL, NULL);
3094
3095         if ((fp = g_fopen(file, "rb")) == NULL) {
3096                 FILE_OP_ERROR(file, "fopen");
3097                 return NULL;
3098         }
3099
3100         str = file_read_stream_to_str_full(fp, recode);
3101
3102         fclose(fp);
3103
3104         return str;
3105 }
3106
3107 gchar *file_read_to_str(const gchar *file)
3108 {
3109         return file_read_to_str_full(file, TRUE);
3110 }
3111 gchar *file_read_stream_to_str(FILE *fp)
3112 {
3113         return file_read_stream_to_str_full(fp, TRUE);
3114 }
3115
3116 gchar *file_read_to_str_no_recode(const gchar *file)
3117 {
3118         return file_read_to_str_full(file, FALSE);
3119 }
3120 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3121 {
3122         return file_read_stream_to_str_full(fp, FALSE);
3123 }
3124
3125 char *fgets_crlf(char *buf, int size, FILE *stream)
3126 {
3127         gboolean is_cr = FALSE;
3128         gboolean last_was_cr = FALSE;
3129         int c = 0;
3130         char *cs;
3131
3132         cs = buf;
3133         while (--size > 0 && (c = getc(stream)) != EOF)
3134         {
3135                 *cs++ = c;
3136                 is_cr = (c == '\r');
3137                 if (c == '\n') {
3138                         break;
3139                 }
3140                 if (last_was_cr) {
3141                         *(--cs) = '\n';
3142                         cs++;
3143                         ungetc(c, stream);
3144                         break;
3145                 }
3146                 last_was_cr = is_cr;
3147         }
3148         if (c == EOF && cs == buf)
3149                 return NULL;
3150
3151         *cs = '\0';
3152
3153         return buf;     
3154 }
3155
3156 static gint execute_async(gchar *const argv[])
3157 {
3158         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3159
3160         if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3161                           NULL, NULL, NULL, FALSE) == FALSE) {
3162                 g_warning("Couldn't execute command: %s\n", argv[0]);
3163                 return -1;
3164         }
3165
3166         return 0;
3167 }
3168
3169 static gint execute_sync(gchar *const argv[])
3170 {
3171         gint status;
3172
3173         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3174
3175         if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3176                          NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3177                 g_warning("Couldn't execute command: %s\n", argv[0]);
3178                 return -1;
3179         }
3180
3181 #ifdef G_OS_UNIX
3182         if (WIFEXITED(status))
3183                 return WEXITSTATUS(status);
3184         else
3185                 return -1;
3186 #else
3187         return status;
3188 #endif
3189 }
3190
3191 gint execute_command_line(const gchar *cmdline, gboolean async)
3192 {
3193         gchar **argv;
3194         gint ret;
3195
3196         debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3197
3198         argv = strsplit_with_quote(cmdline, " ", 0);
3199
3200         if (async)
3201                 ret = execute_async(argv);
3202         else
3203                 ret = execute_sync(argv);
3204
3205         g_strfreev(argv);
3206
3207         return ret;
3208 }
3209
3210 gchar *get_command_output(const gchar *cmdline)
3211 {
3212         gchar *child_stdout;
3213         gint status;
3214
3215         g_return_val_if_fail(cmdline != NULL, NULL);
3216
3217         debug_print("get_command_output(): executing: %s\n", cmdline);
3218
3219         if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3220                                       NULL) == FALSE) {
3221                 g_warning("Couldn't execute command: %s\n", cmdline);
3222                 return NULL;
3223         }
3224
3225         return child_stdout;
3226 }
3227 #ifndef MAEMO
3228 static gint is_unchanged_uri_char(char c)
3229 {
3230         switch (c) {
3231                 case '(':
3232                 case ')':
3233                         return 0;
3234                 default:
3235                         return 1;
3236         }
3237 }
3238
3239 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3240 {
3241         int i;
3242         int k;
3243
3244         k = 0;
3245         for(i = 0; i < strlen(uri) ; i++) {
3246                 if (is_unchanged_uri_char(uri[i])) {
3247                         if (k + 2 >= bufsize)
3248                                 break;
3249                         encoded_uri[k++] = uri[i];
3250                 }
3251                 else {
3252                         char * hexa = "0123456789ABCDEF";
3253
3254                         if (k + 4 >= bufsize)
3255                                 break;
3256                         encoded_uri[k++] = '%';
3257                         encoded_uri[k++] = hexa[uri[i] / 16];
3258                         encoded_uri[k++] = hexa[uri[i] % 16];
3259                 }
3260         }
3261         encoded_uri[k] = 0;
3262 }
3263 #endif
3264 gint open_uri(const gchar *uri, const gchar *cmdline)
3265 {
3266 #ifndef MAEMO
3267         gchar buf[BUFFSIZE];
3268         gchar *p;
3269         gchar encoded_uri[BUFFSIZE];
3270         g_return_val_if_fail(uri != NULL, -1);
3271
3272         /* an option to choose whether to use encode_uri or not ? */
3273         encode_uri(encoded_uri, BUFFSIZE, uri);
3274
3275         if (cmdline &&
3276             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3277             !strchr(p + 2, '%'))
3278                 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3279         else {
3280                 if (cmdline)
3281                         g_warning("Open URI command line is invalid "
3282                                   "(there must be only one '%%s'): %s",
3283                                   cmdline);
3284                 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3285         }
3286
3287         execute_command_line(buf, TRUE);
3288 #else
3289         extern osso_context_t *get_osso_context(void);
3290         osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3291                                         OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL, 
3292                                         DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3293 #endif
3294         return 0;
3295 }
3296
3297 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3298 {
3299         gchar buf[BUFFSIZE];
3300         gchar *p;
3301
3302         g_return_val_if_fail(filepath != NULL, -1);
3303
3304         if (cmdline &&
3305             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3306             !strchr(p + 2, '%'))
3307                 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3308         else {
3309                 if (cmdline)
3310                         g_warning("Open Text Editor command line is invalid "
3311                                   "(there must be only one '%%s'): %s",
3312                                   cmdline);
3313                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3314         }
3315
3316         execute_command_line(buf, TRUE);
3317
3318         return 0;
3319 }
3320
3321 time_t remote_tzoffset_sec(const gchar *zone)
3322 {
3323         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3324         gchar zone3[4];
3325         gchar *p;
3326         gchar c;
3327         gint iustz;
3328         gint offset;
3329         time_t remoteoffset;
3330
3331         strncpy(zone3, zone, 3);
3332         zone3[3] = '\0';
3333         remoteoffset = 0;
3334
3335         if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3336             (c == '+' || c == '-')) {
3337                 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3338                 if (c == '-')
3339                         remoteoffset = -remoteoffset;
3340         } else if (!strncmp(zone, "UT" , 2) ||
3341                    !strncmp(zone, "GMT", 2)) {
3342                 remoteoffset = 0;
3343         } else if (strlen(zone3) == 3) {
3344                 for (p = ustzstr; *p != '\0'; p += 3) {
3345                         if (!g_ascii_strncasecmp(p, zone3, 3)) {
3346                                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3347                                 remoteoffset = iustz * 3600;
3348                                 break;
3349                         }
3350                 }
3351                 if (*p == '\0')
3352                         return -1;
3353         } else if (strlen(zone3) == 1) {
3354                 switch (zone[0]) {
3355                 case 'Z': remoteoffset =   0; break;
3356                 case 'A': remoteoffset =  -1; break;
3357                 case 'B': remoteoffset =  -2; break;
3358                 case 'C': remoteoffset =  -3; break;
3359                 case 'D': remoteoffset =  -4; break;
3360                 case 'E': remoteoffset =  -5; break;
3361                 case 'F': remoteoffset =  -6; break;
3362                 case 'G': remoteoffset =  -7; break;
3363                 case 'H': remoteoffset =  -8; break;
3364                 case 'I': remoteoffset =  -9; break;
3365                 case 'K': remoteoffset = -10; break; /* J is not used */
3366                 case 'L': remoteoffset = -11; break;
3367                 case 'M': remoteoffset = -12; break;
3368                 case 'N': remoteoffset =   1; break;
3369                 case 'O': remoteoffset =   2; break;
3370                 case 'P': remoteoffset =   3; break;
3371                 case 'Q': remoteoffset =   4; break;
3372                 case 'R': remoteoffset =   5; break;
3373                 case 'S': remoteoffset =   6; break;
3374                 case 'T': remoteoffset =   7; break;
3375                 case 'U': remoteoffset =   8; break;
3376                 case 'V': remoteoffset =   9; break;
3377                 case 'W': remoteoffset =  10; break;
3378                 case 'X': remoteoffset =  11; break;
3379                 case 'Y': remoteoffset =  12; break;
3380                 default:  remoteoffset =   0; break;
3381                 }
3382                 remoteoffset = remoteoffset * 3600;
3383         } else
3384                 return -1;
3385
3386         return remoteoffset;
3387 }
3388
3389 time_t tzoffset_sec(time_t *now)
3390 {
3391         struct tm gmt, *lt;
3392         gint off;
3393         struct tm buf1, buf2;
3394         
3395         gmt = *gmtime_r(now, &buf1);
3396         lt = localtime_r(now, &buf2);
3397
3398         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3399
3400         if (lt->tm_year < gmt.tm_year)
3401                 off -= 24 * 60;
3402         else if (lt->tm_year > gmt.tm_year)
3403                 off += 24 * 60;
3404         else if (lt->tm_yday < gmt.tm_yday)
3405                 off -= 24 * 60;
3406         else if (lt->tm_yday > gmt.tm_yday)
3407                 off += 24 * 60;
3408
3409         if (off >= 24 * 60)             /* should be impossible */
3410                 off = 23 * 60 + 59;     /* if not, insert silly value */
3411         if (off <= -24 * 60)
3412                 off = -(23 * 60 + 59);
3413
3414         return off * 60;
3415 }
3416
3417 /* calculate timezone offset */
3418 gchar *tzoffset(time_t *now)
3419 {
3420         static gchar offset_string[6];
3421         struct tm gmt, *lt;
3422         gint off;
3423         gchar sign = '+';
3424         struct tm buf1, buf2;
3425
3426         gmt = *gmtime_r(now, &buf1);
3427         lt = localtime_r(now, &buf2);
3428
3429         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3430
3431         if (lt->tm_year < gmt.tm_year)
3432                 off -= 24 * 60;
3433         else if (lt->tm_year > gmt.tm_year)
3434                 off += 24 * 60;
3435         else if (lt->tm_yday < gmt.tm_yday)
3436                 off -= 24 * 60;
3437         else if (lt->tm_yday > gmt.tm_yday)
3438                 off += 24 * 60;
3439
3440         if (off < 0) {
3441                 sign = '-';
3442                 off = -off;
3443         }
3444
3445         if (off >= 24 * 60)             /* should be impossible */
3446                 off = 23 * 60 + 59;     /* if not, insert silly value */
3447
3448         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3449
3450         return offset_string;
3451 }
3452
3453 void get_rfc822_date(gchar *buf, gint len)
3454 {
3455         struct tm *lt;
3456         time_t t;
3457         gchar day[4], mon[4];
3458         gint dd, hh, mm, ss, yyyy;
3459         struct tm buf1;
3460         gchar buf2[BUFFSIZE];
3461
3462         t = time(NULL);
3463         lt = localtime_r(&t, &buf1);
3464
3465         sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3466                day, mon, &dd, &hh, &mm, &ss, &yyyy);
3467
3468         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3469                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3470 }
3471
3472 void debug_set_mode(gboolean mode)
3473 {
3474         debug_mode = mode;
3475 }
3476
3477 gboolean debug_get_mode(void)
3478 {
3479         return debug_mode;
3480 }
3481
3482 void debug_print_real(const gchar *format, ...)
3483 {
3484         va_list args;
3485         gchar buf[BUFFSIZE];
3486
3487         if (!debug_mode) return;
3488
3489         va_start(args, format);
3490         g_vsnprintf(buf, sizeof(buf), format, args);
3491         va_end(args);
3492
3493         g_print("%s", buf);
3494 }
3495
3496
3497 const char * debug_srcname(const char *file)
3498 {
3499         const char *s = strrchr (file, '/');
3500         return s? s+1:file;
3501 }
3502
3503
3504 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3505 {
3506         if (subject == NULL)
3507                 subject = "";
3508         else
3509                 subject += subject_get_prefix_length(subject);
3510
3511         return g_hash_table_lookup(subject_table, subject);
3512 }
3513
3514 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3515                           void * data)
3516 {
3517         if (subject == NULL || *subject == 0)
3518                 return;
3519         subject += subject_get_prefix_length(subject);
3520         g_hash_table_insert(subject_table, subject, data);
3521 }
3522
3523 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3524 {
3525         if (subject == NULL)
3526                 return;
3527
3528         subject += subject_get_prefix_length(subject);
3529         g_hash_table_remove(subject_table, subject);
3530 }
3531
3532 /*!
3533  *\brief        Check if a string is prefixed with known (combinations)
3534  *              of prefixes. The function assumes that each prefix
3535  *              is terminated by zero or exactly _one_ space.
3536  *
3537  *\param        str String to check for a prefixes
3538  *
3539  *\return       int Number of chars in the prefix that should be skipped
3540  *              for a "clean" subject line. If no prefix was found, 0
3541  *              is returned.
3542  */
3543 static regex_t u_regex;
3544 static gboolean u_init_;
3545
3546 void utils_free_regex(void)
3547 {
3548         if (u_init_) {
3549                 regfree(&u_regex);
3550                 u_init_ = FALSE;
3551         }
3552 }
3553
3554 int subject_get_prefix_length(const gchar *subject)
3555 {
3556         /*!< Array with allowable reply prefixes regexps. */
3557         static const gchar * const prefixes[] = {
3558                 "Re\\:",                        /* "Re:" */
3559                 "Re\\[[1-9][0-9]*\\]\\:",       /* "Re[XXX]:" (non-conforming news mail clients) */
3560                 "Antw\\:",                      /* "Antw:" (Dutch / German Outlook) */
3561                 "Aw\\:",                        /* "Aw:"   (German) */
3562                 "Antwort\\:",                   /* "Antwort:" (German Lotus Notes) */
3563                 "Res\\:",                       /* "Res:" (Brazilian Outlook) */
3564                 "Fw\\:",                        /* "Fw:" Forward */
3565                 "Enc\\:",                       /* "Enc:" Forward (Brazilian Outlook) */
3566                 "Odp\\:",                       /* "Odp:" Re (Polish Outlook) */
3567                 "Rif\\:",                       /* "Rif:" (Italian Outlook) */
3568                 "Sv\\:",                        /* "Sv" (Norwegian) */
3569                 "Vs\\:",                        /* "Vs" (Norwegian) */
3570                 "Ad\\:",                        /* "Ad" (Norwegian) */
3571                 "\347\255\224\345\244\215\\:"   /* "Re" (Chinese, UTF-8) */
3572                 /* add more */
3573         };
3574         const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3575         int n;
3576         regmatch_t pos;
3577
3578         if (!subject) return 0;
3579         if (!*subject) return 0;
3580
3581         if (!u_init_) {
3582                 GString *s = g_string_new("");
3583
3584                 for (n = 0; n < PREFIXES; n++)
3585                         /* Terminate each prefix regexpression by a
3586                          * "\ ?" (zero or ONE space), and OR them */
3587                         g_string_append_printf(s, "(%s\\ ?)%s",
3588                                           prefixes[n],
3589                                           n < PREFIXES - 1 ?
3590                                           "|" : "");
3591
3592                 g_string_prepend(s, "(");
3593                 g_string_append(s, ")+");       /* match at least once */
3594                 g_string_prepend(s, "^\\ *");   /* from beginning of line */
3595
3596
3597                 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3598                  * TODO: Should this be       "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3599                 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3600                         debug_print("Error compiling regexp %s\n", s->str);
3601                         g_string_free(s, TRUE);
3602                         return 0;
3603                 } else {
3604                         u_init_ = TRUE;
3605                         g_string_free(s, TRUE);
3606                 }
3607         }
3608
3609         if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3610                 return pos.rm_eo;
3611         else
3612                 return 0;
3613 }
3614
3615 static guint g_stricase_hash(gconstpointer gptr)
3616 {
3617         guint hash_result = 0;
3618         const char *str;
3619
3620         for (str = gptr; str && *str; str++) {
3621                 hash_result += toupper(*str);
3622         }
3623
3624         return hash_result;
3625 }
3626
3627 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3628 {
3629         const char *str1 = gptr1;
3630         const char *str2 = gptr2;
3631
3632         return !strcasecmp(str1, str2);
3633 }
3634
3635 gint g_int_compare(gconstpointer a, gconstpointer b)
3636 {
3637         return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3638 }
3639
3640 gchar *generate_msgid(gchar *buf, gint len)
3641 {
3642         struct tm *lt;
3643         time_t t;
3644         gchar *addr;
3645         struct tm buft;
3646
3647         t = time(NULL);
3648         lt = localtime_r(&t, &buft);
3649
3650         if (strcmp(buf, "") == 0) {
3651                 addr = g_strconcat("@", get_domain_name(), NULL);
3652         }
3653         else {
3654                 addr = g_strconcat("@", buf, NULL);
3655         }
3656
3657         g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3658                    lt->tm_year + 1900, lt->tm_mon + 1,
3659                    lt->tm_mday, lt->tm_hour,
3660                    lt->tm_min, lt->tm_sec,
3661                    (guint) rand(), addr);
3662
3663         g_free(addr);
3664         return buf;
3665 }
3666
3667 /*
3668    quote_cmd_argument()
3669
3670    return a quoted string safely usable in argument of a command.
3671
3672    code is extracted and adapted from etPan! project -- DINH V. Hoà.
3673 */
3674
3675 gint quote_cmd_argument(gchar * result, guint size,
3676                         const gchar * path)
3677 {
3678         const gchar * p;
3679         gchar * result_p;
3680         guint remaining;
3681
3682         result_p = result;
3683         remaining = size;
3684
3685         for(p = path ; * p != '\0' ; p ++) {
3686
3687                 if (isalnum((guchar)*p) || (* p == '/')) {
3688                         if (remaining > 0) {
3689                                 * result_p = * p;
3690                                 result_p ++;
3691                                 remaining --;
3692                         }
3693                         else {
3694                                 result[size - 1] = '\0';
3695                                 return -1;
3696                         }
3697                 }
3698                 else {
3699                         if (remaining >= 2) {
3700                                 * result_p = '\\';
3701                                 result_p ++;
3702                                 * result_p = * p;
3703                                 result_p ++;
3704                                 remaining -= 2;
3705                         }
3706                         else {
3707                                 result[size - 1] = '\0';
3708                                 return -1;
3709                         }
3710                 }
3711         }
3712         if (remaining > 0) {
3713                 * result_p = '\0';
3714         }
3715         else {
3716                 result[size - 1] = '\0';
3717                 return -1;
3718         }
3719
3720         return 0;
3721 }
3722
3723 typedef struct
3724 {
3725         GNode           *parent;
3726         GNodeMapFunc     func;
3727         gpointer         data;
3728 } GNodeMapData;
3729
3730 static void g_node_map_recursive(GNode *node, gpointer data)
3731 {
3732         GNodeMapData *mapdata = (GNodeMapData *) data;
3733         GNode *newnode;
3734         GNodeMapData newmapdata;
3735         gpointer newdata;
3736
3737         newdata = mapdata->func(node->data, mapdata->data);
3738         if (newdata != NULL) {
3739                 newnode = g_node_new(newdata);
3740                 g_node_append(mapdata->parent, newnode);
3741
3742                 newmapdata.parent = newnode;
3743                 newmapdata.func = mapdata->func;
3744                 newmapdata.data = mapdata->data;
3745
3746                 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3747         }
3748 }
3749
3750 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3751 {
3752         GNode *root;
3753         GNodeMapData mapdata;
3754
3755         g_return_val_if_fail(node != NULL, NULL);
3756         g_return_val_if_fail(func != NULL, NULL);
3757
3758         root = g_node_new(func(node->data, data));
3759
3760         mapdata.parent = root;
3761         mapdata.func = func;
3762         mapdata.data = data;
3763
3764         g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3765
3766         return root;
3767 }
3768
3769 #define HEX_TO_INT(val, hex)                    \
3770 {                                               \
3771         gchar c = hex;                          \
3772                                                 \
3773         if ('0' <= c && c <= '9') {             \
3774                 val = c - '0';                  \
3775         } else if ('a' <= c && c <= 'f') {      \
3776                 val = c - 'a' + 10;             \
3777         } else if ('A' <= c && c <= 'F') {      \
3778                 val = c - 'A' + 10;             \
3779         } else {                                \
3780                 val = -1;                       \
3781         }                                       \
3782 }
3783
3784 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3785 {
3786         gint hi, lo;
3787
3788         HEX_TO_INT(hi, c1);
3789         HEX_TO_INT(lo, c2);
3790
3791         if (hi == -1 || lo == -1)
3792                 return FALSE;
3793
3794         *out = (hi << 4) + lo;
3795         return TRUE;
3796 }
3797
3798 #define INT_TO_HEX(hex, val)            \
3799 {                                       \
3800         if ((val) < 10)                 \
3801                 hex = '0' + (val);      \
3802         else                            \
3803                 hex = 'A' + (val) - 10; \
3804 }
3805
3806 void get_hex_str(gchar *out, guchar ch)
3807 {
3808         gchar hex;
3809
3810         INT_TO_HEX(hex, ch >> 4);
3811         *out++ = hex;
3812         INT_TO_HEX(hex, ch & 0x0f);
3813         *out++ = hex;
3814 }
3815
3816 #undef REF_DEBUG
3817 #ifndef REF_DEBUG
3818 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3819 #else
3820 #define G_PRINT_REF g_print
3821 #endif
3822
3823 /*!
3824  *\brief        Register ref counted pointer. It is based on GBoxed, so should
3825  *              work with anything that uses the GType system. The semantics
3826  *              are similar to a C++ auto pointer, with the exception that
3827  *              C doesn't have automatic closure (calling destructors) when
3828  *              exiting a block scope.
3829  *              Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3830  *              function directly.
3831  *
3832  *\return       GType A GType type.
3833  */
3834 GType g_auto_pointer_register(void)
3835 {
3836         static GType auto_pointer_type;
3837         if (!auto_pointer_type)
3838                 auto_pointer_type =
3839                         g_boxed_type_register_static
3840                                 ("G_TYPE_AUTO_POINTER",
3841                                  (GBoxedCopyFunc) g_auto_pointer_copy,
3842                                  (GBoxedFreeFunc) g_auto_pointer_free);
3843         return auto_pointer_type;
3844 }
3845
3846 /*!
3847  *\brief        Structure with g_new() allocated pointer guarded by the
3848  *              auto pointer
3849  */
3850 typedef struct AutoPointerRef {
3851         void          (*free) (gpointer);
3852         gpointer        pointer;
3853         glong           cnt;
3854 } AutoPointerRef;
3855
3856 /*!
3857  *\brief        The auto pointer opaque structure that references the
3858  *              pointer guard block.
3859  */
3860 typedef struct AutoPointer {
3861         AutoPointerRef *ref;
3862         gpointer        ptr; /*!< access to protected pointer */
3863 } AutoPointer;
3864
3865 /*!
3866  *\brief        Creates an auto pointer for a g_new()ed pointer. Example:
3867  *
3868  *\code
3869  *
3870  *              ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3871  *              ... when assigning, copying and freeing storage elements
3872  *
3873  *              gtk_list_store_new(N_S_COLUMNS,
3874  *                                 G_TYPE_AUTO_POINTER,
3875  *                                 -1);
3876  *
3877  *
3878  *              Template *precious_data = g_new0(Template, 1);
3879  *              g_pointer protect = g_auto_pointer_new(precious_data);
3880  *
3881  *              gtk_list_store_set(container, &iter,
3882  *                                 S_DATA, protect,
3883  *                                 -1);
3884  *
3885  *              ... the gtk_list_store has copied the pointer and
3886  *              ... incremented its reference count, we should free
3887  *              ... the auto pointer (in C++ a destructor would do
3888  *              ... this for us when leaving block scope)
3889  *
3890  *              g_auto_pointer_free(protect);
3891  *
3892  *              ... gtk_list_store_set() now manages the data. When
3893  *              ... *explicitly* requesting a pointer from the list
3894  *              ... store, don't forget you get a copy that should be
3895  *              ... freed with g_auto_pointer_free() eventually.
3896  *
3897  *\endcode
3898  *
3899  *\param        pointer Pointer to be guarded.
3900  *
3901  *\return       GAuto * Pointer that should be used in containers with
3902  *              GType support.
3903  */
3904 GAuto *g_auto_pointer_new(gpointer p)
3905 {
3906         AutoPointerRef *ref;
3907         AutoPointer    *ptr;
3908
3909         if (p == NULL)
3910                 return NULL;
3911
3912         ref = g_new0(AutoPointerRef, 1);
3913         ptr = g_new0(AutoPointer, 1);
3914
3915         ref->pointer = p;
3916         ref->free = g_free;
3917         ref->cnt = 1;
3918
3919         ptr->ref = ref;
3920         ptr->ptr = p;
3921
3922 #ifdef REF_DEBUG
3923         G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
3924 #endif
3925         return ptr;
3926 }
3927
3928 /*!
3929  *\brief        Allocate an autopointer using the passed \a free function to
3930  *              free the guarded pointer
3931  */
3932 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
3933 {
3934         AutoPointer *aptr;
3935
3936         if (p == NULL)
3937                 return NULL;
3938
3939         aptr = g_auto_pointer_new(p);
3940         aptr->ref->free = free_;
3941         return aptr;
3942 }
3943
3944 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
3945 {
3946         if (auto_ptr == NULL)
3947                 return NULL;
3948         return ((AutoPointer *) auto_ptr)->ptr;
3949 }
3950
3951 /*!
3952  *\brief        Copies an auto pointer by. It's mostly not necessary
3953  *              to call this function directly, unless you copy/assign
3954  *              the guarded pointer.
3955  *
3956  *\param        auto_ptr Auto pointer returned by previous call to
3957  *              g_auto_pointer_new_XXX()
3958  *
3959  *\return       gpointer An auto pointer
3960  */
3961 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
3962 {
3963         AutoPointer     *ptr;
3964         AutoPointerRef  *ref;
3965         AutoPointer     *newp;
3966
3967         if (auto_ptr == NULL)
3968                 return NULL;
3969
3970         ptr = auto_ptr;
3971         ref = ptr->ref;
3972         newp = g_new0(AutoPointer, 1);
3973
3974         newp->ref = ref;
3975         newp->ptr = ref->pointer;
3976         ++(ref->cnt);
3977
3978 #ifdef REF_DEBUG
3979         G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
3980 #endif
3981         return newp;
3982 }
3983
3984 /*!
3985  *\brief        Free an auto pointer
3986  */
3987 void g_auto_pointer_free(GAuto *auto_ptr)
3988 {
3989         AutoPointer     *ptr;
3990         AutoPointerRef  *ref;
3991
3992         if (auto_ptr == NULL)
3993                 return;
3994
3995         ptr = auto_ptr;
3996         ref = ptr->ref;
3997
3998         if (--(ref->cnt) == 0) {
3999 #ifdef REF_DEBUG
4000                 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4001 #endif
4002                 ref->free(ref->pointer);
4003                 g_free(ref);
4004         }
4005 #ifdef REF_DEBUG
4006         else
4007                 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4008 #endif
4009         g_free(ptr);
4010 }
4011
4012 void replace_returns(gchar *str)
4013 {
4014         if (!str)
4015                 return;
4016
4017         while (strstr(str, "\n")) {
4018                 *strstr(str, "\n") = ' ';
4019         }
4020         while (strstr(str, "\r")) {
4021                 *strstr(str, "\r") = ' ';
4022         }
4023 }
4024
4025 /* get_uri_part() - retrieves a URI starting from scanpos.
4026                     Returns TRUE if succesful */
4027 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4028                              const gchar **bp, const gchar **ep, gboolean hdr)
4029 {
4030         const gchar *ep_;
4031         gint parenthese_cnt = 0;
4032
4033         g_return_val_if_fail(start != NULL, FALSE);
4034         g_return_val_if_fail(scanpos != NULL, FALSE);
4035         g_return_val_if_fail(bp != NULL, FALSE);
4036         g_return_val_if_fail(ep != NULL, FALSE);
4037
4038         *bp = scanpos;
4039
4040         /* find end point of URI */
4041         for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4042                 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4043                     !IS_ASCII(*(const guchar *)ep_) ||
4044                     strchr("[]{}<>\"", *ep_)) {
4045                         break;
4046                 } else if (strchr("(", *ep_)) {
4047                         parenthese_cnt++;
4048                 } else if (strchr(")", *ep_)) {
4049                         if (parenthese_cnt > 0)
4050                                 parenthese_cnt--;
4051                         else
4052                                 break;
4053                 }
4054         }
4055
4056         /* no punctuation at end of string */
4057
4058         /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4059          * should pass some URI type to this function and decide on that whether
4060          * to perform punctuation stripping */
4061
4062 #define IS_REAL_PUNCT(ch)       (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4063
4064         for (; ep_ - 1 > scanpos + 1 &&
4065                IS_REAL_PUNCT(*(ep_ - 1));
4066              ep_--)
4067                 ;
4068
4069 #undef IS_REAL_PUNCT
4070
4071         *ep = ep_;
4072
4073         return TRUE;
4074 }
4075
4076 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4077 {
4078         while (bp && *bp && g_ascii_isspace(*bp))
4079                 bp++;
4080         return g_strndup(bp, ep - bp);
4081 }
4082
4083 /* valid mail address characters */
4084 #define IS_RFC822_CHAR(ch) \
4085         (IS_ASCII(ch) && \
4086          (ch) > 32   && \
4087          (ch) != 127 && \
4088          !g_ascii_isspace(ch) && \
4089          !strchr("(),;<>\"", (ch)))
4090
4091 /* alphabet and number within 7bit ASCII */
4092 #define IS_ASCII_ALNUM(ch)      (IS_ASCII(ch) && g_ascii_isalnum(ch))
4093 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4094
4095 static GHashTable *create_domain_tab(void)
4096 {
4097         static const gchar *toplvl_domains [] = {
4098             "museum", "aero",
4099             "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4100             "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4101             "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4102             "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4103             "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4104             "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4105             "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4106             "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4107             "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4108             "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4109             "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4110             "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4111             "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4112             "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4113             "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4114             "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4115             "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4116             "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4117             "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4118             "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4119             "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4120             "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4121             "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4122             "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4123             "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4124             "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4125         };
4126         gint n;
4127         GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4128
4129         g_return_val_if_fail(htab, NULL);
4130         for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4131                 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4132         return htab;
4133 }
4134
4135 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4136 {
4137         const gint MAX_LVL_DOM_NAME_LEN = 6;
4138         gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4139         const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4140         register gchar *p;
4141
4142         if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4143                 return FALSE;
4144
4145         for (p = buf; p < m &&  first < last; *p++ = *first++)
4146                 ;
4147         *p = 0;
4148
4149         return g_hash_table_lookup(tab, buf) != NULL;
4150 }
4151
4152 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4153 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4154                                const gchar **bp, const gchar **ep, gboolean hdr)
4155 {
4156         /* more complex than the uri part because we need to scan back and forward starting from
4157          * the scan position. */
4158         gboolean result = FALSE;
4159         const gchar *bp_ = NULL;
4160         const gchar *ep_ = NULL;
4161         static GHashTable *dom_tab;
4162         const gchar *last_dot = NULL;
4163         const gchar *prelast_dot = NULL;
4164         const gchar *last_tld_char = NULL;
4165
4166         /* the informative part of the email address (describing the name
4167          * of the email address owner) may contain quoted parts. the
4168          * closure stack stores the last encountered quotes. */
4169         gchar closure_stack[128];
4170         gchar *ptr = closure_stack;
4171
4172         g_return_val_if_fail(start != NULL, FALSE);
4173         g_return_val_if_fail(scanpos != NULL, FALSE);
4174         g_return_val_if_fail(bp != NULL, FALSE);
4175         g_return_val_if_fail(ep != NULL, FALSE);
4176
4177         if (hdr) {
4178                 const gchar *start_quote = NULL;
4179                 const gchar *end_quote = NULL;
4180 search_again:
4181                 /* go to the real start */
4182                 if (start[0] == ',')
4183                         start++;
4184                 if (start[0] == ';')
4185                         start++;
4186                 while (start[0] == '\n' || start[0] == '\r')
4187                         start++;
4188                 while (start[0] == ' ' || start[0] == '\t')
4189                         start++;
4190
4191                 *bp = start;
4192                 
4193                 /* check if there are quotes (to skip , in them) */
4194                 if (*start == '"') {
4195                         start_quote = start;
4196                         start++;
4197                         end_quote = strstr(start, "\"");
4198                 } else {
4199                         start_quote = NULL;
4200                         end_quote = NULL;
4201                 }
4202                 
4203                 /* skip anything between quotes */
4204                 if (start_quote && end_quote) {
4205                         start = end_quote;
4206                         
4207                 } 
4208
4209                 /* find end (either , or ; or end of line) */
4210                 if (strstr(start, ",") && strstr(start, ";"))
4211                         *ep = strstr(start,",") < strstr(start, ";")
4212                                 ? strstr(start, ",") : strstr(start, ";");
4213                 else if (strstr(start, ","))
4214                         *ep = strstr(start, ",");
4215                 else if (strstr(start, ";"))
4216                         *ep = strstr(start, ";");
4217                 else
4218                         *ep = start+strlen(start);
4219
4220                 /* go back to real start */
4221                 if (start_quote && end_quote) {
4222                         start = start_quote;
4223                 }
4224
4225                 /* check there's still an @ in that, or search
4226                  * further if possible */
4227                 if (strstr(start, "@") && strstr(start, "@") < *ep)
4228                         return TRUE;
4229                 else if (*ep < start+strlen(start)) {
4230                         start = *ep;
4231                         goto search_again;
4232                 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4233                         *bp = start_quote;
4234                         return TRUE;
4235                 } else
4236                         return FALSE;
4237         }
4238
4239         if (!dom_tab)
4240                 dom_tab = create_domain_tab();
4241         g_return_val_if_fail(dom_tab, FALSE);
4242
4243         /* scan start of address */
4244         for (bp_ = scanpos - 1;
4245              bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4246                 ;
4247
4248         /* TODO: should start with an alnum? */
4249         bp_++;
4250         for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4251                 ;
4252
4253         if (bp_ != scanpos) {
4254                 /* scan end of address */
4255                 for (ep_ = scanpos + 1;
4256                      *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4257                         if (*ep_ == '.') {
4258                                 prelast_dot = last_dot;
4259                                 last_dot = ep_;
4260                                 if (*(last_dot + 1) == '.') {
4261                                         if (prelast_dot == NULL)
4262                                                 return FALSE;
4263                                         last_dot = prelast_dot;
4264                                         break;
4265                                 }
4266                         }
4267
4268                 /* TODO: really should terminate with an alnum? */
4269                 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4270                      --ep_)
4271                         ;
4272                 ep_++;
4273
4274                 if (last_dot == NULL)
4275                         return FALSE;
4276                 if (last_dot >= ep_)
4277                         last_dot = prelast_dot;
4278                 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4279                         return FALSE;
4280                 last_dot++;
4281
4282                 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4283                         if (*last_tld_char == '?')
4284                                 break;
4285
4286                 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4287                         result = TRUE;
4288
4289                 *ep = ep_;
4290                 *bp = bp_;
4291         }
4292
4293         if (!result) return FALSE;
4294
4295         if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4296         && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4297         && IS_RFC822_CHAR(*(ep_ + 3))) {
4298                 /* this informative part with an @ in it is
4299                  * followed by the email address */
4300                 ep_ += 3;
4301
4302                 /* go to matching '>' (or next non-rfc822 char, like \n) */
4303                 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4304                         ;
4305
4306                 /* include the bracket */
4307                 if (*ep_ == '>') ep_++;
4308
4309                 /* include the leading quote */
4310                 bp_--;
4311
4312                 *ep = ep_;
4313                 *bp = bp_;
4314                 return TRUE;
4315         }
4316
4317         /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4318         if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4319                 return FALSE;
4320
4321         /* see if this is <bracketed>; in this case we also scan for the informative part. */
4322         if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4323                 return TRUE;
4324
4325 #define FULL_STACK()    ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4326 #define IN_STACK()      (ptr > closure_stack)
4327 /* has underrun check */
4328 #define POP_STACK()     if(IN_STACK()) --ptr
4329 /* has overrun check */
4330 #define PUSH_STACK(c)   if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4331 /* has underrun check */
4332 #define PEEK_STACK()    (IN_STACK() ? *(ptr - 1) : 0)
4333
4334         ep_++;
4335
4336         /* scan for the informative part. */
4337         for (bp_ -= 2; bp_ >= start; bp_--) {
4338                 /* if closure on the stack keep scanning */
4339                 if (PEEK_STACK() == *bp_) {
4340                         POP_STACK();
4341                         continue;
4342                 }
4343                 if (*bp_ == '\'' || *bp_ == '"') {
4344                         PUSH_STACK(*bp_);
4345                         continue;
4346                 }
4347
4348                 /* if nothing in the closure stack, do the special conditions
4349                  * the following if..else expression simply checks whether
4350                  * a token is acceptable. if not acceptable, the clause
4351                  * should terminate the loop with a 'break' */
4352                 if (!PEEK_STACK()) {
4353                         if (*bp_ == '-'
4354                         && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4355                         && (((bp_ + 1) < ep_)    && isalnum(*(bp_ + 1)))) {
4356                                 /* hyphens are allowed, but only in
4357                                    between alnums */
4358                         } else if (strchr(" \"'", *bp_)) {
4359                                 /* but anything not being a punctiation
4360                                    is ok */
4361                         } else {
4362                                 break; /* anything else is rejected */
4363                         }
4364                 }
4365         }
4366
4367         bp_++;
4368
4369         /* scan forward (should start with an alnum) */
4370         for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4371                 ;
4372 #undef PEEK_STACK
4373 #undef PUSH_STACK
4374 #undef POP_STACK
4375 #undef IN_STACK
4376 #undef FULL_STACK
4377
4378
4379         *bp = bp_;
4380         *ep = ep_;
4381
4382         return result;
4383 }
4384
4385 #undef IS_QUOTE
4386 #undef IS_ASCII_ALNUM
4387 #undef IS_RFC822_CHAR
4388
4389 gchar *make_email_string(const gchar *bp, const gchar *ep)
4390 {
4391         /* returns a mailto: URI; mailto: is also used to detect the
4392          * uri type later on in the button_pressed signal handler */
4393         gchar *tmp;
4394         gchar *result;
4395
4396         tmp = g_strndup(bp, ep - bp);
4397         result = g_strconcat("mailto:", tmp, NULL);
4398         g_free(tmp);
4399
4400         return result;
4401 }
4402
4403 gchar *make_http_string(const gchar *bp, const gchar *ep)
4404 {
4405         /* returns an http: URI; */
4406         gchar *tmp;
4407         gchar *result;
4408
4409         while (bp && *bp && g_ascii_isspace(*bp))
4410                 bp++;
4411         tmp = g_strndup(bp, ep - bp);
4412         result = g_strconcat("http://", tmp, NULL);
4413         g_free(tmp);
4414
4415         return result;
4416 }
4417
4418 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4419 {
4420         FILE *fp = fopen(path, "rb");
4421         gchar buf[BUFFSIZE];
4422         gchar *result = NULL;
4423         if (!fp)
4424                 return NULL;
4425         while (fgets(buf, sizeof (buf), fp) != NULL) {
4426                 gchar **parts = g_strsplit(buf, ";", 3);
4427                 gchar *trimmed = parts[0];
4428                 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4429                         trimmed++;
4430                 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4431                         trimmed[strlen(trimmed)-1] = '\0';
4432
4433                 if (!strcmp(trimmed, type)) {
4434                         gboolean needsterminal = FALSE;
4435                         if (parts[2] && strstr(parts[2], "needsterminal")) {
4436                                 needsterminal = TRUE;
4437                         }
4438                         if (parts[2] && strstr(parts[2], "test=")) {
4439                                 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4440                                 gchar *testcmd = orig_testcmd;
4441                                 if (strstr(testcmd,";"))
4442                                         *(strstr(testcmd,";")) = '\0';
4443                                 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4444                                         testcmd++;
4445                                 while (testcmd[strlen(testcmd)-1] == '\n')
4446                                         testcmd[strlen(testcmd)-1] = '\0';
4447                                 while (testcmd[strlen(testcmd)-1] == '\r')
4448                                         testcmd[strlen(testcmd)-1] = '\0';
4449                                 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4450                                         testcmd[strlen(testcmd)-1] = '\0';
4451                                         
4452                                 if (strstr(testcmd, "%s")) {
4453                                         gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4454                                         gint res = system(tmp);
4455                                         g_free(tmp);
4456                                         g_free(orig_testcmd);
4457                                         
4458                                         if (res != 0) {
4459                                                 g_strfreev(parts);
4460                                                 continue;
4461                                         }
4462                                 } else {
4463                                         gint res = system(testcmd);
4464                                         g_free(orig_testcmd);
4465                                         
4466                                         if (res != 0) {
4467                                                 g_strfreev(parts);
4468                                                 continue;
4469                                         }
4470                                 }
4471                         }
4472                         
4473                         trimmed = parts[1];
4474                         while (trimmed[0] == ' ' || trimmed[0] == '\t')
4475                                 trimmed++;
4476                         while (trimmed[strlen(trimmed)-1] == '\n')
4477                                 trimmed[strlen(trimmed)-1] = '\0';
4478                         while (trimmed[strlen(trimmed)-1] == '\r')
4479                                 trimmed[strlen(trimmed)-1] = '\0';
4480                         while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4481                                 trimmed[strlen(trimmed)-1] = '\0';
4482                         result = g_strdup(trimmed);
4483                         g_strfreev(parts);
4484                         fclose(fp);
4485                         /* if there are no single quotes around %s, add them.
4486                          * '.*%s.*' is ok, as in display 'png:%s'
4487                          */
4488                         if (strstr(result, "%s") 
4489                         && !(strstr(result, "'") < strstr(result,"%s") &&
4490                              strstr(strstr(result,"%s"), "'"))) {
4491                                 gchar *start = g_strdup(result);
4492                                 gchar *end = g_strdup(strstr(result, "%s")+2);
4493                                 gchar *tmp;
4494                                 *strstr(start, "%s") = '\0';
4495                                 tmp = g_strconcat(start,"'%s'",end, NULL);
4496                                 g_free(start);
4497                                 g_free(end);
4498                                 g_free(result);
4499                                 result = tmp;
4500                         }
4501                         if (needsterminal) {
4502                                 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4503                                 g_free(result);
4504                                 result = tmp;
4505                         }
4506                         return result;
4507                 }
4508                 g_strfreev(parts);
4509         }
4510         fclose(fp);
4511         return NULL;
4512 }
4513 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4514 {
4515         gchar *result = NULL;
4516         gchar *path = NULL;
4517         if (type == NULL)
4518                 return NULL;
4519         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4520         result = mailcap_get_command_in_file(path, type, file_to_open);
4521         g_free(path);
4522         if (result)
4523                 return result;
4524         result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4525         return result;
4526 }
4527
4528 void mailcap_update_default(const gchar *type, const gchar *command)
4529 {
4530         gchar *path = NULL, *outpath = NULL;
4531         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4532         outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4533         FILE *fp = fopen(path, "rb");
4534         FILE *outfp = fopen(outpath, "wb");
4535         gchar buf[BUFFSIZE];
4536         gboolean err = FALSE;
4537
4538         if (!outfp) {
4539                 g_free(path);
4540                 g_free(outpath);
4541                 fclose(fp);
4542                 return;
4543         }
4544         while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4545                 gchar **parts = g_strsplit(buf, ";", 3);
4546                 gchar *trimmed = parts[0];
4547                 while (trimmed[0] == ' ')
4548                         trimmed++;
4549                 while (trimmed[strlen(trimmed)-1] == ' ')
4550                         trimmed[strlen(trimmed)-1] = '\0';
4551
4552                 if (!strcmp(trimmed, type)) {
4553                         g_strfreev(parts);
4554                         continue;
4555                 }
4556                 else {
4557                         if(fputs(buf, outfp) == EOF) {
4558                                 err = TRUE;
4559                                 break;
4560                         }
4561                 }
4562                 g_strfreev(parts);
4563         }
4564         if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4565                 err = TRUE;
4566
4567         if (fp)
4568                 fclose(fp);
4569
4570         if (fclose(outfp) == EOF)
4571                 err = TRUE;
4572                 
4573         if (!err)
4574                 g_rename(outpath, path);
4575
4576         g_free(path);
4577         g_free(outpath);
4578 }
4579
4580 gint copy_dir(const gchar *src, const gchar *dst)
4581 {
4582         GDir *dir;
4583         const gchar *name;
4584
4585         if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4586                 g_warning("failed to open directory: %s\n", src);
4587                 return -1;
4588         }
4589
4590         if (make_dir(dst) < 0)
4591                 return -1;
4592
4593         while ((name = g_dir_read_name(dir)) != NULL) {
4594                 gchar *old_file, *new_file;
4595                 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4596                 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4597                 debug_print("copying: %s -> %s\n", old_file, new_file);
4598                 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4599                         gint r = copy_file(old_file, new_file, TRUE);
4600                         if (r < 0) {
4601                                 g_dir_close(dir);
4602                                 return r;
4603                         }
4604                 }
4605 #ifndef G_OS_WIN32
4606                 /* Windows has no symlinks.  Or well, Vista seems to
4607                    have something like this but the semantics might be
4608                    different.  Thus we don't use it under Windows. */
4609                  else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4610                         GError *error;
4611                         gint r = 0;
4612                         gchar *target = g_file_read_link(old_file, &error);
4613                         if (target)
4614                                 r = symlink(target, new_file);
4615                         g_free(target);
4616                         if (r < 0) {
4617                                 g_dir_close(dir);
4618                                 return r;
4619                         }
4620                  }
4621 #endif /*G_OS_WIN32*/
4622                 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4623                         gint r = copy_dir(old_file, new_file);
4624                         if (r < 0) {
4625                                 g_dir_close(dir);
4626                                 return r;
4627                         }
4628                 }
4629         }
4630         g_dir_close(dir);
4631         return 0;
4632 }
4633
4634 /* crude test to see if a file is an email. */
4635 gboolean file_is_email (const gchar *filename)
4636 {
4637         FILE *fp = NULL;
4638         gchar buffer[2048];
4639         gint i = 0;
4640         gint score = 0;
4641         if (filename == NULL)
4642                 return FALSE;
4643         if ((fp = g_fopen(filename, "rb")) == NULL)
4644                 return FALSE;
4645         while (i < 60 && score < 3
4646                && fgets(buffer, sizeof (buffer), fp) > 0) {
4647                 if (!strncmp(buffer, "From:", strlen("From:")))
4648                         score++;
4649                 if (!strncmp(buffer, "To:", strlen("To:")))
4650                         score++;
4651                 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4652                         score++;
4653                 i++;
4654         }
4655         fclose(fp);
4656         return (score >= 3);
4657 }
4658
4659 gboolean sc_g_list_bigger(GList *list, gint max)
4660 {
4661         GList *cur = list;
4662         int i = 0;
4663         while (cur && i <= max+1) {
4664                 i++;
4665                 cur = cur->next;
4666         }
4667         return (i > max);
4668 }
4669
4670 gboolean sc_g_slist_bigger(GSList *list, gint max)
4671 {
4672         GSList *cur = list;
4673         int i = 0;
4674         while (cur && i <= max+1) {
4675                 i++;
4676                 cur = cur->next;
4677         }
4678         return (i > max);
4679 }
4680
4681 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4682 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
4683                              NULL, NULL, NULL, NULL, NULL, NULL};
4684 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4685 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
4686                              NULL, NULL, NULL, NULL, NULL, NULL};
4687 const gchar *s_am_up = NULL;
4688 const gchar *s_pm_up = NULL;
4689 const gchar *s_am_low = NULL;
4690 const gchar *s_pm_low = NULL;
4691 const gchar *def_loc_format = NULL;
4692 const gchar *date_loc_format = NULL;
4693 const gchar *time_loc_format = NULL;
4694 const gchar *time_am_pm = NULL;
4695
4696 static gboolean time_names_init_done = FALSE;
4697
4698 static void init_time_names(void)
4699 {
4700         daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4701         daynames[1] = Q_("Complete day name for use by strftime|Monday");
4702         daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4703         daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4704         daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4705         daynames[5] = Q_("Complete day name for use by strftime|Friday");
4706         daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4707         
4708         monthnames[0] = Q_("Complete month name for use by strftime|January");
4709         monthnames[1] = Q_("Complete month name for use by strftime|February");
4710         monthnames[2] = Q_("Complete month name for use by strftime|March");
4711         monthnames[3] = Q_("Complete month name for use by strftime|April");
4712         monthnames[4] = Q_("Complete month name for use by strftime|May");
4713         monthnames[5] = Q_("Complete month name for use by strftime|June");
4714         monthnames[6] = Q_("Complete month name for use by strftime|July");
4715         monthnames[7] = Q_("Complete month name for use by strftime|August");
4716         monthnames[8] = Q_("Complete month name for use by strftime|September");
4717         monthnames[9] = Q_("Complete month name for use by strftime|October");
4718         monthnames[10] = Q_("Complete month name for use by strftime|November");
4719         monthnames[11] = Q_("Complete month name for use by strftime|December");
4720
4721         s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4722         s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4723         s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4724         s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4725         s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4726         s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4727         s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4728         
4729         s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4730         s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4731         s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4732         s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4733         s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4734         s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4735         s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4736         s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4737         s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4738         s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4739         s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4740         s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4741
4742         s_am_up = Q_("For use by strftime (morning)|AM");
4743         s_pm_up = Q_("For use by strftime (afternoon)|PM");
4744         s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4745         s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4746         
4747         def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4748         date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4749         time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4750
4751         time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4752
4753         time_names_init_done = TRUE;
4754 }
4755
4756 #define CHECK_SIZE() {                  \
4757         total_done += len;              \
4758         if (total_done >= buflen) {     \
4759                 buf[buflen-1] = '\0';   \
4760                 return 0;               \
4761         }                               \
4762 }
4763
4764 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4765 {
4766         gchar *curpos = buf;
4767         gint total_done = 0;
4768         gchar subbuf[64], subfmt[64];
4769         static time_t last_tzset = (time_t)0;
4770         
4771         if (!time_names_init_done)
4772                 init_time_names();
4773         
4774         if (format == NULL || lt == NULL)
4775                 return 0;
4776                 
4777         if (last_tzset != time(NULL)) {
4778                 tzset();
4779                 last_tzset = time(NULL);
4780         }
4781         while(*format) {
4782                 if (*format == '%') {
4783                         gint len = 0, tmp = 0;
4784                         format++;
4785                         switch(*format) {
4786                         case '%':
4787                                 len = 1; CHECK_SIZE();
4788                                 *curpos = '%';
4789                                 break;
4790                         case 'a':
4791                                 len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
4792                                 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4793                                 break;
4794                         case 'A':
4795                                 len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
4796                                 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4797                                 break;
4798                         case 'b':
4799                         case 'h':
4800                                 len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
4801                                 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4802                                 break;
4803                         case 'B':
4804                                 len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
4805                                 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4806                                 break;
4807                         case 'c':
4808                                 fast_strftime(subbuf, 64, def_loc_format, lt);
4809                                 len = strlen(subbuf); CHECK_SIZE();
4810                                 strncpy2(curpos, subbuf, buflen - total_done);
4811                                 break;
4812                         case 'C':
4813                                 total_done += 2; CHECK_SIZE();
4814                                 tmp = (lt->tm_year + 1900)/100;
4815                                 *curpos++ = '0'+(tmp / 10);
4816                                 *curpos++ = '0'+(tmp % 10);
4817                                 break;
4818                         case 'd':
4819                                 total_done += 2; CHECK_SIZE();
4820                                 *curpos++ = '0'+(lt->tm_mday / 10);
4821                                 *curpos++ = '0'+(lt->tm_mday % 10);
4822                                 break;
4823                         case 'D':
4824                                 total_done += 8; CHECK_SIZE();
4825                                 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4826                                 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4827                                 *curpos++ = '/';
4828                                 *curpos++ = '0'+(lt->tm_mday / 10);
4829                                 *curpos++ = '0'+(lt->tm_mday % 10);
4830                                 *curpos++ = '/';
4831                                 tmp = lt->tm_year%100;
4832                                 *curpos++ = '0'+(tmp / 10);
4833                                 *curpos++ = '0'+(tmp % 10);
4834                                 break;
4835                         case 'e':
4836                                 len = 2; CHECK_SIZE();
4837                                 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4838                                 break;
4839                         case 'F':
4840                                 len = 10; CHECK_SIZE();
4841                                 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d", 
4842                                         lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4843                                 break;
4844                         case 'H':
4845                                 total_done += 2; CHECK_SIZE();
4846                                 *curpos++ = '0'+(lt->tm_hour / 10);
4847                                 *curpos++ = '0'+(lt->tm_hour % 10);
4848                                 break;
4849                         case 'I':
4850                                 total_done += 2; CHECK_SIZE();
4851                                 tmp = lt->tm_hour;
4852                                 if (tmp > 12)
4853                                         tmp -= 12;
4854                                 else if (tmp == 0)
4855                                         tmp = 12;
4856                                 *curpos++ = '0'+(tmp / 10);
4857                                 *curpos++ = '0'+(tmp % 10);
4858                                 break;
4859                         case 'j':
4860                                 len = 3; CHECK_SIZE();
4861                                 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
4862                                 break;
4863                         case 'k':
4864                                 len = 2; CHECK_SIZE();
4865                                 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
4866                                 break;
4867                         case 'l':
4868                                 len = 2; CHECK_SIZE();
4869                                 tmp = lt->tm_hour;
4870                                 if (tmp > 12)
4871                                         tmp -= 12;
4872                                 else if (tmp == 0)
4873                                         tmp = 12;
4874                                 snprintf(curpos, buflen - total_done, "%2d", tmp);
4875                                 break;
4876                         case 'm':
4877                                 total_done += 2; CHECK_SIZE();
4878                                 tmp = lt->tm_mon + 1;
4879                                 *curpos++ = '0'+(tmp / 10);
4880                                 *curpos++ = '0'+(tmp % 10);
4881                                 break;
4882                         case 'M':
4883                                 total_done += 2; CHECK_SIZE();
4884                                 *curpos++ = '0'+(lt->tm_min / 10);
4885                                 *curpos++ = '0'+(lt->tm_min % 10);
4886                                 break;
4887                         case 'n':
4888                                 len = 1; CHECK_SIZE();
4889                                 *curpos = '\n';
4890                                 break;
4891                         case 'p':
4892                                 if (lt->tm_hour >= 12) {
4893                                         len = strlen(s_pm_up); CHECK_SIZE();
4894                                         snprintf(curpos, buflen-total_done, s_pm_up);
4895                                 } else {
4896                                         len = strlen(s_am_up); CHECK_SIZE();
4897                                         snprintf(curpos, buflen-total_done, s_am_up);
4898                                 }
4899                                 break;
4900                         case 'P':
4901                                 if (lt->tm_hour >= 12) {
4902                                         len = strlen(s_pm_low); CHECK_SIZE();
4903                                         snprintf(curpos, buflen-total_done, s_pm_low);
4904                                 } else {
4905                                         len = strlen(s_am_low); CHECK_SIZE();
4906                                         snprintf(curpos, buflen-total_done, s_am_low);
4907                                 }
4908                                 break;
4909                         case 'r':
4910                                 fast_strftime(subbuf, 64, time_am_pm, lt);
4911                                 len = strlen(subbuf); CHECK_SIZE();
4912                                 strncpy2(curpos, subbuf, buflen - total_done);
4913                                 break;
4914                         case 'R':
4915                                 total_done += 5; CHECK_SIZE();
4916                                 *curpos++ = '0'+(lt->tm_hour / 10);
4917                                 *curpos++ = '0'+(lt->tm_hour % 10);
4918                                 *curpos++ = ':';
4919                                 *curpos++ = '0'+(lt->tm_min / 10);
4920                                 *curpos++ = '0'+(lt->tm_min % 10);
4921                                 break;
4922                         case 's':
4923                                 snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
4924                                 len = strlen(subbuf); CHECK_SIZE();
4925                                 strncpy2(curpos, subbuf, buflen - total_done);
4926                                 break;
4927                         case 'S':
4928                                 total_done += 2; CHECK_SIZE();
4929                                 *curpos++ = '0'+(lt->tm_sec / 10);
4930                                 *curpos++ = '0'+(lt->tm_sec % 10);
4931                                 break;
4932                         case 't':
4933                                 len = 1; CHECK_SIZE();
4934                                 *curpos = '\t';
4935                                 break;
4936                         case 'T':
4937                                 total_done += 8; CHECK_SIZE();
4938                                 *curpos++ = '0'+(lt->tm_hour / 10);
4939                                 *curpos++ = '0'+(lt->tm_hour % 10);
4940                                 *curpos++ = ':';
4941                                 *curpos++ = '0'+(lt->tm_min / 10);
4942                                 *curpos++ = '0'+(lt->tm_min % 10);
4943                                 *curpos++ = ':';
4944                                 *curpos++ = '0'+(lt->tm_sec / 10);
4945                                 *curpos++ = '0'+(lt->tm_sec % 10);
4946                                 break;
4947                         case 'u':
4948                                 len = 1; CHECK_SIZE();
4949                                 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
4950                                 break;
4951                         case 'w':
4952                                 len = 1; CHECK_SIZE();
4953                                 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
4954                                 break;
4955                         case 'x':
4956                                 fast_strftime(subbuf, 64, date_loc_format, lt);
4957                                 len = strlen(subbuf); CHECK_SIZE();
4958                                 strncpy2(curpos, subbuf, buflen - total_done);
4959                                 break;
4960                         case 'X':
4961                                 fast_strftime(subbuf, 64, time_loc_format, lt);
4962                                 len = strlen(subbuf); CHECK_SIZE();
4963                                 strncpy2(curpos, subbuf, buflen - total_done);
4964                                 break;
4965                         case 'y':
4966                                 total_done += 2; CHECK_SIZE();
4967                                 tmp = lt->tm_year%100;
4968                                 *curpos++ = '0'+(tmp / 10);
4969                                 *curpos++ = '0'+(tmp % 10);
4970                                 break;
4971                         case 'Y':
4972                                 len = 4; CHECK_SIZE();
4973                                 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
4974                                 break;
4975                         case 'G':
4976                         case 'g':
4977                         case 'U':
4978                         case 'V':
4979                         case 'W':
4980                         case 'z':
4981                         case 'Z':
4982                         case '+':
4983                                 /* let these complicated ones be done with the libc */
4984                                 snprintf(subfmt, 64, "%%%c", *format);
4985                                 strftime(subbuf, 64, subfmt, lt);
4986                                 len = strlen(subbuf); CHECK_SIZE();
4987                                 strncpy2(curpos, subbuf, buflen - total_done);
4988                                 break;
4989                         case 'E':
4990                         case 'O':
4991                                 /* let these complicated modifiers be done with the libc */
4992                                 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
4993                                 strftime(subbuf, 64, subfmt, lt);
4994                                 len = strlen(subbuf); CHECK_SIZE();
4995                                 strncpy2(curpos, subbuf, buflen - total_done);
4996                                 format++;
4997                                 break;
4998                         default:
4999                                 g_warning("format error (%c)", *format);
5000                                 *curpos = '\0';
5001                                 return total_done;
5002                         }
5003                         curpos += len;
5004                         format++;
5005                 } else {
5006                         int len = 1; CHECK_SIZE();
5007                         *curpos++ = *format++; 
5008                 }
5009         }
5010         *curpos++ = '\0';
5011         return total_done;
5012 }
5013
5014 gboolean prefs_common_get_use_shred(void);
5015
5016 int claws_unlink(const gchar *filename) 
5017 {
5018         struct stat s;
5019         static int found_shred = -1;
5020         static const gchar *args[4];
5021
5022         if (found_shred == -1) {
5023                 /* init */
5024                 args[0] = g_find_program_in_path("shred");
5025                 debug_print("found shred: %s\n", args[0]);
5026                 found_shred = (args[0] != NULL) ? 1:0;
5027                 args[1] = "-f";
5028                 args[3] = NULL;
5029         }
5030         if (found_shred == 1 && prefs_common_get_use_shred()) {
5031                 if (is_file_exist(filename) && g_stat(filename, &s) == 0) {
5032                         if (s.st_nlink == 1) {
5033                                 gint status=0;
5034                                 args[2] = filename;
5035                                 g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5036                                  NULL, NULL, NULL, NULL, &status, NULL);
5037                                 debug_print("%s %s exited with status %d\n",
5038                                         args[0], filename, WEXITSTATUS(status));
5039                                 truncate(filename, 0);
5040                         }
5041                 }
5042         }
5043         return g_unlink(filename);
5044 }