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