2d97ab6b49c22ed868172ece5ceb0f1804a948a0
[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, "SHGetFolderPathW");
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_utf16[MAX_PATH] = "";
1797         static gchar *home_dir_utf8 = NULL;
1798         if (home_dir_utf16[0] == '\0') {
1799                 if (w32_shgetfolderpath
1800                             (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1801                              NULL, 0, home_dir_utf16) < 0)
1802                                 strcpy (home_dir_utf16, "C:\\Sylpheed");
1803                 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1804         }
1805         return home_dir_utf8;
1806 #else
1807         static const gchar *homeenv = NULL;
1808
1809         if (homeenv)
1810                 return homeenv;
1811
1812         if (!homeenv && g_getenv("HOME") != NULL)
1813                 homeenv = g_strdup(g_getenv("HOME"));
1814         if (!homeenv)
1815                 homeenv = g_get_home_dir();
1816
1817         return homeenv;
1818 #endif
1819 }
1820
1821 static gchar *claws_rc_dir = NULL;
1822 static gboolean rc_dir_alt = FALSE;
1823 const gchar *get_rc_dir(void)
1824 {
1825
1826         if (!claws_rc_dir)
1827                 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1828                                      RC_DIR, NULL);
1829
1830         return claws_rc_dir;
1831 }
1832
1833 void set_rc_dir(const gchar *dir)
1834 {
1835         if (claws_rc_dir != NULL) {
1836                 g_print("Error: rc_dir already set\n");
1837         } else {
1838                 rc_dir_alt = TRUE;
1839                 if (g_path_is_absolute(dir))
1840                         claws_rc_dir = g_strdup(dir);
1841                 else {
1842                         claws_rc_dir = g_strconcat(g_get_current_dir(),
1843                                 G_DIR_SEPARATOR_S, dir, NULL);
1844                 }
1845                 debug_print("set rc_dir to %s\n", claws_rc_dir);
1846                 if (!is_dir_exist(claws_rc_dir)) {
1847                         if (make_dir_hier(claws_rc_dir) != 0) {
1848                                 g_print("Error: can't create %s\n",
1849                                 claws_rc_dir);
1850                         }
1851                 }
1852         }
1853 }
1854
1855 gboolean rc_dir_is_alt(void) {
1856         return rc_dir_alt;
1857 }
1858
1859 const gchar *get_mail_base_dir(void)
1860 {
1861         return get_home_dir();
1862 }
1863
1864 #ifdef MAEMO
1865 const gchar *prefs_common_get_data_root(void);
1866 gchar *last_data_root = NULL;
1867 #endif
1868
1869 const gchar *get_news_cache_dir(void)
1870 {
1871         static gchar *news_cache_dir = NULL;
1872 #ifdef MAEMO
1873         const gchar *data_root = prefs_common_get_data_root();
1874         if (strcmp2(data_root, last_data_root)) {
1875                 g_free(news_cache_dir);
1876                 news_cache_dir = NULL;
1877         }
1878 #endif
1879         if (!news_cache_dir)
1880 #ifndef MAEMO
1881                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1882                                              NEWS_CACHE_DIR, NULL);
1883 #else
1884         {
1885                 if (data_root) {
1886                         news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1887                                              "Claws", G_DIR_SEPARATOR_S, 
1888                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1889                                              NEWS_CACHE_DIR, NULL);
1890                         g_free(last_data_root);
1891                         last_data_root = g_strdup(last_data_root);
1892                 } else {
1893                         news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1894                                              NEWS_CACHE_DIR, NULL);
1895                         g_free(last_data_root);
1896                         last_data_root = NULL;
1897                 }
1898         }
1899 #endif
1900         return news_cache_dir;
1901 }
1902
1903 const gchar *get_imap_cache_dir(void)
1904 {
1905         static gchar *imap_cache_dir = NULL;
1906 #ifdef MAEMO
1907         const gchar *data_root = prefs_common_get_data_root();
1908         if (strcmp2(data_root, last_data_root)) {
1909                 g_free(imap_cache_dir);
1910                 imap_cache_dir = NULL;
1911         }
1912 #endif
1913
1914         if (!imap_cache_dir)
1915 #ifndef MAEMO
1916                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1917                                              IMAP_CACHE_DIR, NULL);
1918 #else
1919         {
1920                 if (data_root) {
1921                         imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1922                                              "Claws", G_DIR_SEPARATOR_S, 
1923                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1924                                              IMAP_CACHE_DIR, NULL);
1925                         g_free(last_data_root);
1926                         last_data_root = g_strdup(last_data_root);
1927                 } else {
1928                         imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1929                                              IMAP_CACHE_DIR, NULL);
1930                         g_free(last_data_root);
1931                         last_data_root = NULL;
1932                 }
1933         }
1934 #endif
1935
1936         return imap_cache_dir;
1937 }
1938
1939 const gchar *get_mime_tmp_dir(void)
1940 {
1941         static gchar *mime_tmp_dir = NULL;
1942
1943         if (!mime_tmp_dir)
1944                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1945                                            MIME_TMP_DIR, NULL);
1946
1947         return mime_tmp_dir;
1948 }
1949
1950 const gchar *get_template_dir(void)
1951 {
1952         static gchar *template_dir = NULL;
1953
1954         if (!template_dir)
1955                 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1956                                            TEMPLATE_DIR, NULL);
1957
1958         return template_dir;
1959 }
1960
1961 /* Return the default directory for Plugins. */
1962 const gchar *get_plugin_dir(void)
1963 {
1964 #ifdef G_OS_WIN32
1965         static gchar *plugin_dir = NULL;
1966
1967         if (!plugin_dir)
1968                 plugin_dir = g_strconcat(w32_get_module_dir(),
1969                                          "\\lib\\claws-mail\\plugins\\",
1970                                          NULL);
1971         return plugin_dir;
1972 #else
1973         if (is_dir_exist(PLUGINDIR))
1974                 return PLUGINDIR;
1975         else {
1976                 static gchar *plugin_dir = NULL;
1977                 if (!plugin_dir)
1978                         plugin_dir = g_strconcat(get_rc_dir(), 
1979                                 G_DIR_SEPARATOR_S, "plugins", 
1980                                 G_DIR_SEPARATOR_S, NULL);
1981                 return plugin_dir;                      
1982         }
1983 #endif
1984 }
1985
1986 const gchar *get_tmp_dir(void)
1987 {
1988         static gchar *tmp_dir = NULL;
1989
1990         if (!tmp_dir)
1991                 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1992                                       TMP_DIR, NULL);
1993
1994         return tmp_dir;
1995 }
1996
1997 gchar *get_tmp_file(void)
1998 {
1999         gchar *tmp_file;
2000         static guint32 id = 0;
2001
2002         tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2003                                    get_tmp_dir(), G_DIR_SEPARATOR, id++);
2004
2005         return tmp_file;
2006 }
2007
2008 const gchar *get_domain_name(void)
2009 {
2010 #ifdef G_OS_UNIX
2011         static gchar *domain_name = NULL;
2012
2013         if (!domain_name) {
2014                 struct hostent *hp;
2015                 char hostname[256];
2016
2017                 if (gethostname(hostname, sizeof(hostname)) != 0) {
2018                         perror("gethostname");
2019                         domain_name = "unknown";
2020                 } else {
2021                         hostname[sizeof(hostname) - 1] = '\0';
2022                         if ((hp = my_gethostbyname(hostname)) == NULL) {
2023                                 perror("gethostbyname");
2024                                 domain_name = g_strdup(hostname);
2025                         } else {
2026                                 domain_name = g_strdup(hp->h_name);
2027                         }
2028                 }
2029                 debug_print("domain name = %s\n", domain_name);
2030         }
2031
2032         return domain_name;
2033 #else
2034         return "unknown";
2035 #endif
2036 }
2037
2038 off_t get_file_size(const gchar *file)
2039 {
2040         struct stat s;
2041
2042         if (g_stat(file, &s) < 0) {
2043                 FILE_OP_ERROR(file, "stat");
2044                 return -1;
2045         }
2046
2047         return s.st_size;
2048 }
2049
2050 time_t get_file_mtime(const gchar *file)
2051 {
2052         struct stat s;
2053
2054         if (g_stat(file, &s) < 0) {
2055                 FILE_OP_ERROR(file, "stat");
2056                 return -1;
2057         }
2058
2059         return s.st_mtime;
2060 }
2061
2062 off_t get_file_size_as_crlf(const gchar *file)
2063 {
2064         FILE *fp;
2065         off_t size = 0;
2066         gchar buf[BUFFSIZE];
2067
2068         if ((fp = g_fopen(file, "rb")) == NULL) {
2069                 FILE_OP_ERROR(file, "fopen");
2070                 return -1;
2071         }
2072
2073         while (fgets(buf, sizeof(buf), fp) != NULL) {
2074                 strretchomp(buf);
2075                 size += strlen(buf) + 2;
2076         }
2077
2078         if (ferror(fp)) {
2079                 FILE_OP_ERROR(file, "fgets");
2080                 size = -1;
2081         }
2082
2083         fclose(fp);
2084
2085         return size;
2086 }
2087
2088 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2089 {
2090         struct stat s;
2091
2092         if (file == NULL)
2093                 return FALSE;
2094
2095         if (g_stat(file, &s) < 0) {
2096                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2097                 return FALSE;
2098         }
2099
2100         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2101                 return TRUE;
2102
2103         return FALSE;
2104 }
2105
2106
2107 /* Test on whether FILE is a relative file name. This is
2108  * straightforward for Unix but more complex for Windows. */
2109 gboolean is_relative_filename(const gchar *file)
2110 {
2111         if (!file)
2112                 return TRUE;
2113 #ifdef G_OS_WIN32
2114         if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2115                 return FALSE; /* Prefixed with a hostname - this can't
2116                                * be a relative name. */
2117
2118         if ( ((*file >= 'a' && *file <= 'z')
2119               || (*file >= 'A' && *file <= 'Z'))
2120              && file[1] == ':')
2121                 file += 2;  /* Skip drive letter. */
2122
2123         return !(*file == '\\' || *file == '/');
2124 #else
2125         return !(*file == G_DIR_SEPARATOR);
2126 #endif
2127 }
2128
2129
2130 gboolean is_dir_exist(const gchar *dir)
2131 {
2132         if (dir == NULL)
2133                 return FALSE;
2134
2135         return g_file_test(dir, G_FILE_TEST_IS_DIR);
2136 }
2137
2138 gboolean is_file_entry_exist(const gchar *file)
2139 {
2140         if (file == NULL)
2141                 return FALSE;
2142
2143         return g_file_test(file, G_FILE_TEST_EXISTS);
2144 }
2145
2146 gboolean dirent_is_regular_file(struct dirent *d)
2147 {
2148 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2149         if (d->d_type == DT_REG)
2150                 return TRUE;
2151         else if (d->d_type != DT_UNKNOWN)
2152                 return FALSE;
2153 #endif
2154
2155         return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2156 }
2157
2158 gint change_dir(const gchar *dir)
2159 {
2160         gchar *prevdir = NULL;
2161
2162         if (debug_mode)
2163                 prevdir = g_get_current_dir();
2164
2165         if (g_chdir(dir) < 0) {
2166                 FILE_OP_ERROR(dir, "chdir");
2167                 if (debug_mode) g_free(prevdir);
2168                 return -1;
2169         } else if (debug_mode) {
2170                 gchar *cwd;
2171
2172                 cwd = g_get_current_dir();
2173                 if (strcmp(prevdir, cwd) != 0)
2174                         g_print("current dir: %s\n", cwd);
2175                 g_free(cwd);
2176                 g_free(prevdir);
2177         }
2178
2179         return 0;
2180 }
2181
2182 gint make_dir(const gchar *dir)
2183 {
2184         if (g_mkdir(dir, S_IRWXU) < 0) {
2185                 FILE_OP_ERROR(dir, "mkdir");
2186                 return -1;
2187         }
2188         if (g_chmod(dir, S_IRWXU) < 0)
2189                 FILE_OP_ERROR(dir, "chmod");
2190
2191         return 0;
2192 }
2193
2194 gint make_dir_hier(const gchar *dir)
2195 {
2196         gchar *parent_dir;
2197         const gchar *p;
2198
2199         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2200                 parent_dir = g_strndup(dir, p - dir);
2201                 if (*parent_dir != '\0') {
2202                         if (!is_dir_exist(parent_dir)) {
2203                                 if (make_dir(parent_dir) < 0) {
2204                                         g_free(parent_dir);
2205                                         return -1;
2206                                 }
2207                         }
2208                 }
2209                 g_free(parent_dir);
2210         }
2211
2212         if (!is_dir_exist(dir)) {
2213                 if (make_dir(dir) < 0)
2214                         return -1;
2215         }
2216
2217         return 0;
2218 }
2219
2220 gint remove_all_files(const gchar *dir)
2221 {
2222         GDir *dp;
2223         const gchar *dir_name;
2224         gchar *prev_dir;
2225
2226         prev_dir = g_get_current_dir();
2227
2228         if (g_chdir(dir) < 0) {
2229                 FILE_OP_ERROR(dir, "chdir");
2230                 g_free(prev_dir);
2231                 return -1;
2232         }
2233
2234         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2235                 g_warning("failed to open directory: %s\n", dir);
2236                 g_free(prev_dir);
2237                 return -1;
2238         }
2239
2240         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2241                 if (claws_unlink(dir_name) < 0)
2242                         FILE_OP_ERROR(dir_name, "unlink");
2243         }
2244
2245         g_dir_close(dp);
2246
2247         if (g_chdir(prev_dir) < 0) {
2248                 FILE_OP_ERROR(prev_dir, "chdir");
2249                 g_free(prev_dir);
2250                 return -1;
2251         }
2252
2253         g_free(prev_dir);
2254
2255         return 0;
2256 }
2257
2258 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2259 {
2260         GDir *dp;
2261         const gchar *dir_name;
2262         gchar *prev_dir;
2263         gint file_no;
2264
2265         prev_dir = g_get_current_dir();
2266
2267         if (g_chdir(dir) < 0) {
2268                 FILE_OP_ERROR(dir, "chdir");
2269                 g_free(prev_dir);
2270                 return -1;
2271         }
2272
2273         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2274                 g_warning("failed to open directory: %s\n", dir);
2275                 g_free(prev_dir);
2276                 return -1;
2277         }
2278
2279         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2280                 file_no = to_number(dir_name);
2281                 if (file_no > 0 && first <= file_no && file_no <= last) {
2282                         if (is_dir_exist(dir_name))
2283                                 continue;
2284                         if (claws_unlink(dir_name) < 0)
2285                                 FILE_OP_ERROR(dir_name, "unlink");
2286                 }
2287         }
2288
2289         g_dir_close(dp);
2290
2291         if (g_chdir(prev_dir) < 0) {
2292                 FILE_OP_ERROR(prev_dir, "chdir");
2293                 g_free(prev_dir);
2294                 return -1;
2295         }
2296
2297         g_free(prev_dir);
2298
2299         return 0;
2300 }
2301
2302 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2303 {
2304         GDir *dp;
2305         const gchar *dir_name;
2306         gchar *prev_dir;
2307         gint file_no;
2308
2309         prev_dir = g_get_current_dir();
2310
2311         if (g_chdir(dir) < 0) {
2312                 FILE_OP_ERROR(dir, "chdir");
2313                 g_free(prev_dir);
2314                 return -1;
2315         }
2316
2317         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2318                 FILE_OP_ERROR(dir, "opendir");
2319                 g_free(prev_dir);
2320                 return -1;
2321         }
2322
2323         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2324                 file_no = to_number(dir_name);
2325                 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2326                         debug_print("removing unwanted file %d from %s\n", file_no, dir);
2327                         if (is_dir_exist(dir_name))
2328                                 continue;
2329                         if (claws_unlink(dir_name) < 0)
2330                                 FILE_OP_ERROR(dir_name, "unlink");
2331                 }
2332         }
2333
2334         g_dir_close(dp);
2335
2336         if (g_chdir(prev_dir) < 0) {
2337                 FILE_OP_ERROR(prev_dir, "chdir");
2338                 g_free(prev_dir);
2339                 return -1;
2340         }
2341
2342         g_free(prev_dir);
2343
2344         return 0;
2345 }
2346
2347 gint remove_all_numbered_files(const gchar *dir)
2348 {
2349         return remove_numbered_files(dir, 0, UINT_MAX);
2350 }
2351
2352 gint remove_dir_recursive(const gchar *dir)
2353 {
2354         struct stat s;
2355         GDir *dp;
2356         const gchar *dir_name;
2357         gchar *prev_dir;
2358
2359         if (g_stat(dir, &s) < 0) {
2360                 FILE_OP_ERROR(dir, "stat");
2361                 if (ENOENT == errno) return 0;
2362                 return -1;
2363         }
2364
2365         if (!S_ISDIR(s.st_mode)) {
2366                 if (claws_unlink(dir) < 0) {
2367                         FILE_OP_ERROR(dir, "unlink");
2368                         return -1;
2369                 }
2370
2371                 return 0;
2372         }
2373
2374         prev_dir = g_get_current_dir();
2375         /* g_print("prev_dir = %s\n", prev_dir); */
2376
2377         if (!path_cmp(prev_dir, dir)) {
2378                 g_free(prev_dir);
2379                 if (g_chdir("..") < 0) {
2380                         FILE_OP_ERROR(dir, "chdir");
2381                         return -1;
2382                 }
2383                 prev_dir = g_get_current_dir();
2384         }
2385
2386         if (g_chdir(dir) < 0) {
2387                 FILE_OP_ERROR(dir, "chdir");
2388                 g_free(prev_dir);
2389                 return -1;
2390         }
2391
2392         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2393                 g_warning("failed to open directory: %s\n", dir);
2394                 g_chdir(prev_dir);
2395                 g_free(prev_dir);
2396                 return -1;
2397         }
2398
2399         /* remove all files in the directory */
2400         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2401                 /* g_print("removing %s\n", dir_name); */
2402
2403                 if (is_dir_exist(dir_name)) {
2404                         if (remove_dir_recursive(dir_name) < 0) {
2405                                 g_warning("can't remove directory\n");
2406                                 return -1;
2407                         }
2408                 } else {
2409                         if (claws_unlink(dir_name) < 0)
2410                                 FILE_OP_ERROR(dir_name, "unlink");
2411                 }
2412         }
2413
2414         g_dir_close(dp);
2415
2416         if (g_chdir(prev_dir) < 0) {
2417                 FILE_OP_ERROR(prev_dir, "chdir");
2418                 g_free(prev_dir);
2419                 return -1;
2420         }
2421
2422         g_free(prev_dir);
2423
2424         if (g_rmdir(dir) < 0) {
2425                 FILE_OP_ERROR(dir, "rmdir");
2426                 return -1;
2427         }
2428
2429         return 0;
2430 }
2431
2432 gint rename_force(const gchar *oldpath, const gchar *newpath)
2433 {
2434 #ifndef G_OS_UNIX
2435         if (!is_file_entry_exist(oldpath)) {
2436                 errno = ENOENT;
2437                 return -1;
2438         }
2439         if (is_file_exist(newpath)) {
2440                 if (claws_unlink(newpath) < 0)
2441                         FILE_OP_ERROR(newpath, "unlink");
2442         }
2443 #endif
2444         return g_rename(oldpath, newpath);
2445 }
2446
2447 /*
2448  * Append src file body to the tail of dest file.
2449  * Now keep_backup has no effects.
2450  */
2451 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2452 {
2453         FILE *src_fp, *dest_fp;
2454         gint n_read;
2455         gchar buf[BUFSIZ];
2456
2457         gboolean err = FALSE;
2458
2459         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2460                 FILE_OP_ERROR(src, "fopen");
2461                 return -1;
2462         }
2463
2464         if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2465                 FILE_OP_ERROR(dest, "fopen");
2466                 fclose(src_fp);
2467                 return -1;
2468         }
2469
2470         if (change_file_mode_rw(dest_fp, dest) < 0) {
2471                 FILE_OP_ERROR(dest, "chmod");
2472                 g_warning("can't change file mode\n");
2473         }
2474
2475         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2476                 if (n_read < sizeof(buf) && ferror(src_fp))
2477                         break;
2478                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2479                         g_warning("writing to %s failed.\n", dest);
2480                         fclose(dest_fp);
2481                         fclose(src_fp);
2482                         claws_unlink(dest);
2483                         return -1;
2484                 }
2485         }
2486
2487         if (ferror(src_fp)) {
2488                 FILE_OP_ERROR(src, "fread");
2489                 err = TRUE;
2490         }
2491         fclose(src_fp);
2492         if (fclose(dest_fp) == EOF) {
2493                 FILE_OP_ERROR(dest, "fclose");
2494                 err = TRUE;
2495         }
2496
2497         if (err) {
2498                 claws_unlink(dest);
2499                 return -1;
2500         }
2501
2502         return 0;
2503 }
2504
2505 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2506 {
2507         FILE *src_fp, *dest_fp;
2508         gint n_read;
2509         gchar buf[BUFSIZ];
2510         gchar *dest_bak = NULL;
2511         gboolean err = FALSE;
2512
2513         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2514                 FILE_OP_ERROR(src, "fopen");
2515                 return -1;
2516         }
2517         if (is_file_exist(dest)) {
2518                 dest_bak = g_strconcat(dest, ".bak", NULL);
2519                 if (rename_force(dest, dest_bak) < 0) {
2520                         FILE_OP_ERROR(dest, "rename");
2521                         fclose(src_fp);
2522                         g_free(dest_bak);
2523                         return -1;
2524                 }
2525         }
2526
2527         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2528                 FILE_OP_ERROR(dest, "fopen");
2529                 fclose(src_fp);
2530                 if (dest_bak) {
2531                         if (rename_force(dest_bak, dest) < 0)
2532                                 FILE_OP_ERROR(dest_bak, "rename");
2533                         g_free(dest_bak);
2534                 }
2535                 return -1;
2536         }
2537
2538         if (change_file_mode_rw(dest_fp, dest) < 0) {
2539                 FILE_OP_ERROR(dest, "chmod");
2540                 g_warning("can't change file mode\n");
2541         }
2542
2543         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2544                 if (n_read < sizeof(buf) && ferror(src_fp))
2545                         break;
2546                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2547                         g_warning("writing to %s failed.\n", dest);
2548                         fclose(dest_fp);
2549                         fclose(src_fp);
2550                         claws_unlink(dest);
2551                         if (dest_bak) {
2552                                 if (rename_force(dest_bak, dest) < 0)
2553                                         FILE_OP_ERROR(dest_bak, "rename");
2554                                 g_free(dest_bak);
2555                         }
2556                         return -1;
2557                 }
2558         }
2559
2560         if (ferror(src_fp)) {
2561                 FILE_OP_ERROR(src, "fread");
2562                 err = TRUE;
2563         }
2564         fclose(src_fp);
2565         if (fclose(dest_fp) == EOF) {
2566                 FILE_OP_ERROR(dest, "fclose");
2567                 err = TRUE;
2568         }
2569
2570         if (err) {
2571                 claws_unlink(dest);
2572                 if (dest_bak) {
2573                         if (rename_force(dest_bak, dest) < 0)
2574                                 FILE_OP_ERROR(dest_bak, "rename");
2575                         g_free(dest_bak);
2576                 }
2577                 return -1;
2578         }
2579
2580         if (keep_backup == FALSE && dest_bak)
2581                 claws_unlink(dest_bak);
2582
2583         g_free(dest_bak);
2584
2585         return 0;
2586 }
2587
2588 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2589 {
2590         if (overwrite == FALSE && is_file_exist(dest)) {
2591                 g_warning("move_file(): file %s already exists.", dest);
2592                 return -1;
2593         }
2594
2595         if (rename_force(src, dest) == 0) return 0;
2596
2597         if (EXDEV != errno) {
2598                 FILE_OP_ERROR(src, "rename");
2599                 return -1;
2600         }
2601
2602         if (copy_file(src, dest, FALSE) < 0) return -1;
2603
2604         claws_unlink(src);
2605
2606         return 0;
2607 }
2608
2609 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2610 {
2611         gint n_read;
2612         gint bytes_left, to_read;
2613         gchar buf[BUFSIZ];
2614
2615         if (fseek(fp, offset, SEEK_SET) < 0) {
2616                 perror("fseek");
2617                 return -1;
2618         }
2619
2620         bytes_left = length;
2621         to_read = MIN(bytes_left, sizeof(buf));
2622
2623         while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2624                 if (n_read < to_read && ferror(fp))
2625                         break;
2626                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2627                         return -1;
2628                 }
2629                 bytes_left -= n_read;
2630                 if (bytes_left == 0)
2631                         break;
2632                 to_read = MIN(bytes_left, sizeof(buf));
2633         }
2634
2635         if (ferror(fp)) {
2636                 perror("fread");
2637                 return -1;
2638         }
2639
2640         return 0;
2641 }
2642
2643 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2644 {
2645         FILE *dest_fp;
2646         gboolean err = FALSE;
2647
2648         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2649                 FILE_OP_ERROR(dest, "fopen");
2650                 return -1;
2651         }
2652
2653         if (change_file_mode_rw(dest_fp, dest) < 0) {
2654                 FILE_OP_ERROR(dest, "chmod");
2655                 g_warning("can't change file mode\n");
2656         }
2657
2658         if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2659                 err = TRUE;
2660
2661         if (!err && fclose(dest_fp) == EOF) {
2662                 FILE_OP_ERROR(dest, "fclose");
2663                 err = TRUE;
2664         }
2665
2666         if (err) {
2667                 g_warning("writing to %s failed.\n", dest);
2668                 claws_unlink(dest);
2669                 return -1;
2670         }
2671
2672         return 0;
2673 }
2674
2675 /* convert line endings into CRLF. If the last line doesn't end with
2676  * linebreak, add it.
2677  */
2678 gchar *canonicalize_str(const gchar *str)
2679 {
2680         const gchar *p;
2681         guint new_len = 0;
2682         gchar *out, *outp;
2683
2684         for (p = str; *p != '\0'; ++p) {
2685                 if (*p != '\r') {
2686                         ++new_len;
2687                         if (*p == '\n')
2688                                 ++new_len;
2689                 }
2690         }
2691         if (p == str || *(p - 1) != '\n')
2692                 new_len += 2;
2693
2694         out = outp = g_malloc(new_len + 1);
2695         for (p = str; *p != '\0'; ++p) {
2696                 if (*p != '\r') {
2697                         if (*p == '\n')
2698                                 *outp++ = '\r';
2699                         *outp++ = *p;
2700                 }
2701         }
2702         if (p == str || *(p - 1) != '\n') {
2703                 *outp++ = '\r';
2704                 *outp++ = '\n';
2705         }
2706         *outp = '\0';
2707
2708         return out;
2709 }
2710
2711 gint canonicalize_file(const gchar *src, const gchar *dest)
2712 {
2713         FILE *src_fp, *dest_fp;
2714         gchar buf[BUFFSIZE];
2715         gint len;
2716         gboolean err = FALSE;
2717         gboolean last_linebreak = FALSE;
2718
2719         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2720                 FILE_OP_ERROR(src, "fopen");
2721                 return -1;
2722         }
2723
2724         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2725                 FILE_OP_ERROR(dest, "fopen");
2726                 fclose(src_fp);
2727                 return -1;
2728         }
2729
2730         if (change_file_mode_rw(dest_fp, dest) < 0) {
2731                 FILE_OP_ERROR(dest, "chmod");
2732                 g_warning("can't change file mode\n");
2733         }
2734
2735         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2736                 gint r = 0;
2737
2738                 len = strlen(buf);
2739                 if (len == 0) break;
2740                 last_linebreak = FALSE;
2741
2742                 if (buf[len - 1] != '\n') {
2743                         last_linebreak = TRUE;
2744                         r = fputs(buf, dest_fp);
2745                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2746                         r = fputs(buf, dest_fp);
2747                 } else {
2748                         if (len > 1) {
2749                                 r = fwrite(buf, 1, len - 1, dest_fp);
2750                                 if (r != (len -1))
2751                                         r = EOF;
2752                         }
2753                         if (r != EOF)
2754                                 r = fputs("\r\n", dest_fp);
2755                 }
2756
2757                 if (r == EOF) {
2758                         g_warning("writing to %s failed.\n", dest);
2759                         fclose(dest_fp);
2760                         fclose(src_fp);
2761                         claws_unlink(dest);
2762                         return -1;
2763                 }
2764         }
2765
2766         if (last_linebreak == TRUE) {
2767                 if (fputs("\r\n", dest_fp) == EOF)
2768                         err = TRUE;
2769         }
2770
2771         if (ferror(src_fp)) {
2772                 FILE_OP_ERROR(src, "fgets");
2773                 err = TRUE;
2774         }
2775         fclose(src_fp);
2776         if (fclose(dest_fp) == EOF) {
2777                 FILE_OP_ERROR(dest, "fclose");
2778                 err = TRUE;
2779         }
2780
2781         if (err) {
2782                 claws_unlink(dest);
2783                 return -1;
2784         }
2785
2786         return 0;
2787 }
2788
2789 gint canonicalize_file_replace(const gchar *file)
2790 {
2791         gchar *tmp_file;
2792
2793         tmp_file = get_tmp_file();
2794
2795         if (canonicalize_file(file, tmp_file) < 0) {
2796                 g_free(tmp_file);
2797                 return -1;
2798         }
2799
2800         if (move_file(tmp_file, file, TRUE) < 0) {
2801                 g_warning("can't replace %s .\n", file);
2802                 claws_unlink(tmp_file);
2803                 g_free(tmp_file);
2804                 return -1;
2805         }
2806
2807         g_free(tmp_file);
2808         return 0;
2809 }
2810
2811 gchar *normalize_newlines(const gchar *str)
2812 {
2813         const gchar *p = str;
2814         gchar *out, *outp;
2815
2816         out = outp = g_malloc(strlen(str) + 1);
2817         for (p = str; *p != '\0'; ++p) {
2818                 if (*p == '\r') {
2819                         if (*(p + 1) != '\n')
2820                                 *outp++ = '\n';
2821                 } else
2822                         *outp++ = *p;
2823         }
2824
2825         *outp = '\0';
2826
2827         return out;
2828 }
2829
2830 gchar *get_outgoing_rfc2822_str(FILE *fp)
2831 {
2832         gchar buf[BUFFSIZE];
2833         GString *str;
2834         gchar *ret;
2835
2836         str = g_string_new(NULL);
2837
2838         /* output header part */
2839         while (fgets(buf, sizeof(buf), fp) != NULL) {
2840                 strretchomp(buf);
2841                 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2842                         gint next;
2843
2844                         for (;;) {
2845                                 next = fgetc(fp);
2846                                 if (next == EOF)
2847                                         break;
2848                                 else if (next != ' ' && next != '\t') {
2849                                         ungetc(next, fp);
2850                                         break;
2851                                 }
2852                                 if (fgets(buf, sizeof(buf), fp) == NULL)
2853                                         break;
2854                         }
2855                 } else {
2856                         g_string_append(str, buf);
2857                         g_string_append(str, "\r\n");
2858                         if (buf[0] == '\0')
2859                                 break;
2860                 }
2861         }
2862
2863         /* output body part */
2864         while (fgets(buf, sizeof(buf), fp) != NULL) {
2865                 strretchomp(buf);
2866                 if (buf[0] == '.')
2867                         g_string_append_c(str, '.');
2868                 g_string_append(str, buf);
2869                 g_string_append(str, "\r\n");
2870         }
2871
2872         ret = str->str;
2873         g_string_free(str, FALSE);
2874
2875         return ret;
2876 }
2877
2878 /*
2879  * Create a new boundary in a way that it is very unlikely that this
2880  * will occur in the following text.  It would be easy to ensure
2881  * uniqueness if everything is either quoted-printable or base64
2882  * encoded (note that conversion is allowed), but because MIME bodies
2883  * may be nested, it may happen that the same boundary has already
2884  * been used.
2885  *
2886  *   boundary := 0*69<bchars> bcharsnospace
2887  *   bchars := bcharsnospace / " "
2888  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2889  *                  "+" / "_" / "," / "-" / "." /
2890  *                  "/" / ":" / "=" / "?"
2891  *
2892  * some special characters removed because of buggy MTAs
2893  */
2894
2895 gchar *generate_mime_boundary(const gchar *prefix)
2896 {
2897         static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2898                              "abcdefghijklmnopqrstuvwxyz"
2899                              "1234567890+_./=";
2900         gchar buf_uniq[24];
2901         gint i;
2902
2903         for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2904                 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2905         buf_uniq[i] = '\0';
2906
2907         return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2908                                buf_uniq);
2909 }
2910
2911 gint change_file_mode_rw(FILE *fp, const gchar *file)
2912 {
2913 #if HAVE_FCHMOD
2914         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2915 #else
2916         return g_chmod(file, S_IRUSR|S_IWUSR);
2917 #endif
2918 }
2919
2920 FILE *my_tmpfile(void)
2921 {
2922 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2923         const gchar suffix[] = ".XXXXXX";
2924         const gchar *tmpdir;
2925         guint tmplen;
2926         const gchar *progname;
2927         guint proglen;
2928         gchar *fname;
2929         gint fd;
2930         FILE *fp;
2931 #ifndef G_OS_WIN32
2932         gchar buf[2]="\0";
2933 #endif
2934
2935         tmpdir = get_tmp_dir();
2936         tmplen = strlen(tmpdir);
2937         progname = g_get_prgname();
2938         if (progname == NULL)
2939                 progname = "claws-mail";
2940         proglen = strlen(progname);
2941         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2942                 return tmpfile());
2943
2944         memcpy(fname, tmpdir, tmplen);
2945         fname[tmplen] = G_DIR_SEPARATOR;
2946         memcpy(fname + tmplen + 1, progname, proglen);
2947         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2948
2949         fd = mkstemp(fname);
2950         if (fd < 0)
2951                 return tmpfile();
2952
2953 #ifndef G_OS_WIN32
2954         claws_unlink(fname);
2955         
2956         /* verify that we can write in the file after unlinking */
2957         if (write(fd, buf, 1) < 0) {
2958                 close(fd);
2959                 return tmpfile();
2960         }
2961         
2962 #endif
2963
2964         fp = fdopen(fd, "w+b");
2965         if (!fp)
2966                 close(fd);
2967         else {
2968                 rewind(fp);
2969                 return fp;
2970         }
2971
2972 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2973
2974         return tmpfile();
2975 }
2976
2977 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2978 {
2979         int fd;
2980 #ifdef G_OS_WIN32
2981         char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2982                                           dir, G_DIR_SEPARATOR);
2983         fd = mkstemp_name(template, filename);
2984         g_free(template);
2985 #else
2986         *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2987         fd = mkstemp(*filename);
2988 #endif
2989         return fdopen(fd, "w+");
2990 }
2991
2992 FILE *str_open_as_stream(const gchar *str)
2993 {
2994         FILE *fp;
2995         size_t len;
2996
2997         g_return_val_if_fail(str != NULL, NULL);
2998
2999         fp = my_tmpfile();
3000         if (!fp) {
3001                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3002                 return NULL;
3003         }
3004
3005         len = strlen(str);
3006         if (len == 0) return fp;
3007
3008         if (fwrite(str, 1, len, fp) != len) {
3009                 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3010                 fclose(fp);
3011                 return NULL;
3012         }
3013
3014         rewind(fp);
3015         return fp;
3016 }
3017
3018 gint str_write_to_file(const gchar *str, const gchar *file)
3019 {
3020         FILE *fp;
3021         size_t len;
3022
3023         g_return_val_if_fail(str != NULL, -1);
3024         g_return_val_if_fail(file != NULL, -1);
3025
3026         if ((fp = g_fopen(file, "wb")) == NULL) {
3027                 FILE_OP_ERROR(file, "fopen");
3028                 return -1;
3029         }
3030
3031         len = strlen(str);
3032         if (len == 0) {
3033                 fclose(fp);
3034                 return 0;
3035         }
3036
3037         if (fwrite(str, 1, len, fp) != len) {
3038                 FILE_OP_ERROR(file, "fwrite");
3039                 fclose(fp);
3040                 claws_unlink(file);
3041                 return -1;
3042         }
3043
3044         if (fclose(fp) == EOF) {
3045                 FILE_OP_ERROR(file, "fclose");
3046                 claws_unlink(file);
3047                 return -1;
3048         }
3049
3050         return 0;
3051 }
3052
3053 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3054 {
3055         GByteArray *array;
3056         guchar buf[BUFSIZ];
3057         gint n_read;
3058         gchar *str;
3059
3060         g_return_val_if_fail(fp != NULL, NULL);
3061
3062         array = g_byte_array_new();
3063
3064         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3065                 if (n_read < sizeof(buf) && ferror(fp))
3066                         break;
3067                 g_byte_array_append(array, buf, n_read);
3068         }
3069
3070         if (ferror(fp)) {
3071                 FILE_OP_ERROR("file stream", "fread");
3072                 g_byte_array_free(array, TRUE);
3073                 return NULL;
3074         }
3075
3076         buf[0] = '\0';
3077         g_byte_array_append(array, buf, 1);
3078         str = (gchar *)array->data;
3079         g_byte_array_free(array, FALSE);
3080
3081         if (recode && !g_utf8_validate(str, -1, NULL)) {
3082                 const gchar *src_codeset, *dest_codeset;
3083                 gchar *tmp = NULL;
3084                 src_codeset = conv_get_locale_charset_str();
3085                 dest_codeset = CS_UTF_8;
3086                 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3087                 g_free(str);
3088                 str = tmp;
3089         }
3090
3091         return str;
3092 }
3093
3094 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3095 {
3096         FILE *fp;
3097         gchar *str;
3098         struct stat s;
3099 #ifndef G_OS_WIN32
3100         gint fd, err;
3101         struct timeval timeout = {1, 0};
3102         fd_set fds;
3103         int fflags = 0;
3104 #endif
3105
3106         g_return_val_if_fail(file != NULL, NULL);
3107
3108         if (g_stat(file, &s) != 0) {
3109                 FILE_OP_ERROR(file, "stat");
3110                 return NULL;
3111         }
3112         if (S_ISDIR(s.st_mode)) {
3113                 g_warning("%s: is a directory\n", file);
3114                 return NULL;
3115         }
3116
3117 #ifdef G_OS_WIN32
3118         fp = fopen (file, "rb");
3119         if (fp == NULL) {
3120                 FILE_OP_ERROR(file, "open");
3121                 return NULL;
3122         }
3123 #else     
3124         /* test whether the file is readable without blocking */
3125         fd = open(file, O_RDONLY | O_NONBLOCK);
3126         if (fd == -1) {
3127                 FILE_OP_ERROR(file, "open");
3128                 return NULL;
3129         }
3130
3131         FD_ZERO(&fds);
3132         FD_SET(fd, &fds);
3133
3134         /* allow for one second */
3135         err = select(fd+1, &fds, NULL, NULL, &timeout);
3136         if (err <= 0 || !FD_ISSET(fd, &fds)) {
3137                 if (err < 0) {
3138                         FILE_OP_ERROR(file, "select");
3139                 } else {
3140                         g_warning("%s: doesn't seem readable\n", file);
3141                 }
3142                 close(fd);
3143                 return NULL;
3144         }
3145         
3146         /* Now clear O_NONBLOCK */
3147         if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3148                 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3149                 close(fd);
3150                 return NULL;
3151         }
3152         if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3153                 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3154                 close(fd);
3155                 return NULL;
3156         }
3157         
3158         /* get the FILE pointer */
3159         fp = fdopen(fd, "rb");
3160
3161         if (fp == NULL) {
3162                 FILE_OP_ERROR(file, "fdopen");
3163                 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3164                 return NULL;
3165         }
3166 #endif
3167
3168         str = file_read_stream_to_str_full(fp, recode);
3169
3170         fclose(fp);
3171
3172         return str;
3173 }
3174
3175 gchar *file_read_to_str(const gchar *file)
3176 {
3177         return file_read_to_str_full(file, TRUE);
3178 }
3179 gchar *file_read_stream_to_str(FILE *fp)
3180 {
3181         return file_read_stream_to_str_full(fp, TRUE);
3182 }
3183
3184 gchar *file_read_to_str_no_recode(const gchar *file)
3185 {
3186         return file_read_to_str_full(file, FALSE);
3187 }
3188 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3189 {
3190         return file_read_stream_to_str_full(fp, FALSE);
3191 }
3192
3193 char *fgets_crlf(char *buf, int size, FILE *stream)
3194 {
3195         gboolean is_cr = FALSE;
3196         gboolean last_was_cr = FALSE;
3197         int c = 0;
3198         char *cs;
3199
3200         cs = buf;
3201         while (--size > 0 && (c = getc(stream)) != EOF)
3202         {
3203                 *cs++ = c;
3204                 is_cr = (c == '\r');
3205                 if (c == '\n') {
3206                         break;
3207                 }
3208                 if (last_was_cr) {
3209                         *(--cs) = '\n';
3210                         cs++;
3211                         ungetc(c, stream);
3212                         break;
3213                 }
3214                 last_was_cr = is_cr;
3215         }
3216         if (c == EOF && cs == buf)
3217                 return NULL;
3218
3219         *cs = '\0';
3220
3221         return buf;     
3222 }
3223
3224 static gint execute_async(gchar *const argv[])
3225 {
3226         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3227
3228         if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3229                           NULL, NULL, NULL, FALSE) == FALSE) {
3230                 g_warning("Couldn't execute command: %s\n", argv[0]);
3231                 return -1;
3232         }
3233
3234         return 0;
3235 }
3236
3237 static gint execute_sync(gchar *const argv[])
3238 {
3239         gint status;
3240
3241         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3242
3243         if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3244                          NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3245                 g_warning("Couldn't execute command: %s\n", argv[0]);
3246                 return -1;
3247         }
3248
3249 #ifdef G_OS_UNIX
3250         if (WIFEXITED(status))
3251                 return WEXITSTATUS(status);
3252         else
3253                 return -1;
3254 #else
3255         return status;
3256 #endif
3257 }
3258
3259 gint execute_command_line(const gchar *cmdline, gboolean async)
3260 {
3261         gchar **argv;
3262         gint ret;
3263
3264         debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3265
3266         argv = strsplit_with_quote(cmdline, " ", 0);
3267
3268         if (async)
3269                 ret = execute_async(argv);
3270         else
3271                 ret = execute_sync(argv);
3272
3273         g_strfreev(argv);
3274
3275         return ret;
3276 }
3277
3278 gchar *get_command_output(const gchar *cmdline)
3279 {
3280         gchar *child_stdout;
3281         gint status;
3282
3283         g_return_val_if_fail(cmdline != NULL, NULL);
3284
3285         debug_print("get_command_output(): executing: %s\n", cmdline);
3286
3287         if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3288                                       NULL) == FALSE) {
3289                 g_warning("Couldn't execute command: %s\n", cmdline);
3290                 return NULL;
3291         }
3292
3293         return child_stdout;
3294 }
3295 #ifndef MAEMO
3296 static gint is_unchanged_uri_char(char c)
3297 {
3298         switch (c) {
3299                 case '(':
3300                 case ')':
3301                         return 0;
3302                 default:
3303                         return 1;
3304         }
3305 }
3306
3307 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3308 {
3309         int i;
3310         int k;
3311
3312         k = 0;
3313         for(i = 0; i < strlen(uri) ; i++) {
3314                 if (is_unchanged_uri_char(uri[i])) {
3315                         if (k + 2 >= bufsize)
3316                                 break;
3317                         encoded_uri[k++] = uri[i];
3318                 }
3319                 else {
3320                         char * hexa = "0123456789ABCDEF";
3321
3322                         if (k + 4 >= bufsize)
3323                                 break;
3324                         encoded_uri[k++] = '%';
3325                         encoded_uri[k++] = hexa[uri[i] / 16];
3326                         encoded_uri[k++] = hexa[uri[i] % 16];
3327                 }
3328         }
3329         encoded_uri[k] = 0;
3330 }
3331 #endif
3332 gint open_uri(const gchar *uri, const gchar *cmdline)
3333 {
3334 #ifndef MAEMO
3335 #ifndef G_OS_WIN32
3336         gchar buf[BUFFSIZE];
3337         gchar *p;
3338         gchar encoded_uri[BUFFSIZE];
3339         g_return_val_if_fail(uri != NULL, -1);
3340
3341         /* an option to choose whether to use encode_uri or not ? */
3342         encode_uri(encoded_uri, BUFFSIZE, uri);
3343
3344         if (cmdline &&
3345             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3346             !strchr(p + 2, '%'))
3347                 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3348         else {
3349                 if (cmdline)
3350                         g_warning("Open URI command-line is invalid "
3351                                   "(there must be only one '%%s'): %s",
3352                                   cmdline);
3353                 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3354         }
3355
3356         execute_command_line(buf, TRUE);
3357 #else
3358         ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3359 #endif
3360 #else
3361         extern osso_context_t *get_osso_context(void);
3362         osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3363                                         OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL, 
3364                                         DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3365 #endif
3366         return 0;
3367 }
3368
3369 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3370 {
3371         gchar buf[BUFFSIZE];
3372         gchar *p;
3373
3374         g_return_val_if_fail(filepath != NULL, -1);
3375
3376         if (cmdline &&
3377             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3378             !strchr(p + 2, '%'))
3379                 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3380         else {
3381                 if (cmdline)
3382                         g_warning("Open Text Editor command-line is invalid "
3383                                   "(there must be only one '%%s'): %s",
3384                                   cmdline);
3385                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3386         }
3387
3388         execute_command_line(buf, TRUE);
3389
3390         return 0;
3391 }
3392
3393 time_t remote_tzoffset_sec(const gchar *zone)
3394 {
3395         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3396         gchar zone3[4];
3397         gchar *p;
3398         gchar c;
3399         gint iustz;
3400         gint offset;
3401         time_t remoteoffset;
3402
3403         strncpy(zone3, zone, 3);
3404         zone3[3] = '\0';
3405         remoteoffset = 0;
3406
3407         if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3408             (c == '+' || c == '-')) {
3409                 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3410                 if (c == '-')
3411                         remoteoffset = -remoteoffset;
3412         } else if (!strncmp(zone, "UT" , 2) ||
3413                    !strncmp(zone, "GMT", 2)) {
3414                 remoteoffset = 0;
3415         } else if (strlen(zone3) == 3) {
3416                 for (p = ustzstr; *p != '\0'; p += 3) {
3417                         if (!g_ascii_strncasecmp(p, zone3, 3)) {
3418                                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3419                                 remoteoffset = iustz * 3600;
3420                                 break;
3421                         }
3422                 }
3423                 if (*p == '\0')
3424                         return -1;
3425         } else if (strlen(zone3) == 1) {
3426                 switch (zone[0]) {
3427                 case 'Z': remoteoffset =   0; break;
3428                 case 'A': remoteoffset =  -1; break;
3429                 case 'B': remoteoffset =  -2; break;
3430                 case 'C': remoteoffset =  -3; break;
3431                 case 'D': remoteoffset =  -4; break;
3432                 case 'E': remoteoffset =  -5; break;
3433                 case 'F': remoteoffset =  -6; break;
3434                 case 'G': remoteoffset =  -7; break;
3435                 case 'H': remoteoffset =  -8; break;
3436                 case 'I': remoteoffset =  -9; break;
3437                 case 'K': remoteoffset = -10; break; /* J is not used */
3438                 case 'L': remoteoffset = -11; break;
3439                 case 'M': remoteoffset = -12; break;
3440                 case 'N': remoteoffset =   1; break;
3441                 case 'O': remoteoffset =   2; break;
3442                 case 'P': remoteoffset =   3; break;
3443                 case 'Q': remoteoffset =   4; break;
3444                 case 'R': remoteoffset =   5; break;
3445                 case 'S': remoteoffset =   6; break;
3446                 case 'T': remoteoffset =   7; break;
3447                 case 'U': remoteoffset =   8; break;
3448                 case 'V': remoteoffset =   9; break;
3449                 case 'W': remoteoffset =  10; break;
3450                 case 'X': remoteoffset =  11; break;
3451                 case 'Y': remoteoffset =  12; break;
3452                 default:  remoteoffset =   0; break;
3453                 }
3454                 remoteoffset = remoteoffset * 3600;
3455         } else
3456                 return -1;
3457
3458         return remoteoffset;
3459 }
3460
3461 time_t tzoffset_sec(time_t *now)
3462 {
3463         struct tm gmt, *lt;
3464         gint off;
3465 #ifndef G_OS_WIN32
3466         struct tm buf1, buf2;
3467 #endif
3468 #ifdef G_OS_WIN32
3469         if (now && *now < 0)
3470                 return 0;
3471 #endif  
3472         gmt = *gmtime_r(now, &buf1);
3473         lt = localtime_r(now, &buf2);
3474
3475         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3476
3477         if (lt->tm_year < gmt.tm_year)
3478                 off -= 24 * 60;
3479         else if (lt->tm_year > gmt.tm_year)
3480                 off += 24 * 60;
3481         else if (lt->tm_yday < gmt.tm_yday)
3482                 off -= 24 * 60;
3483         else if (lt->tm_yday > gmt.tm_yday)
3484                 off += 24 * 60;
3485
3486         if (off >= 24 * 60)             /* should be impossible */
3487                 off = 23 * 60 + 59;     /* if not, insert silly value */
3488         if (off <= -24 * 60)
3489                 off = -(23 * 60 + 59);
3490
3491         return off * 60;
3492 }
3493
3494 /* calculate timezone offset */
3495 gchar *tzoffset(time_t *now)
3496 {
3497         static gchar offset_string[6];
3498         struct tm gmt, *lt;
3499         gint off;
3500         gchar sign = '+';
3501 #ifndef G_OS_WIN32
3502         struct tm buf1, buf2;
3503 #endif
3504 #ifdef G_OS_WIN32
3505         if (now && *now < 0)
3506                 return 0;
3507 #endif
3508         gmt = *gmtime_r(now, &buf1);
3509         lt = localtime_r(now, &buf2);
3510
3511         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3512
3513         if (lt->tm_year < gmt.tm_year)
3514                 off -= 24 * 60;
3515         else if (lt->tm_year > gmt.tm_year)
3516                 off += 24 * 60;
3517         else if (lt->tm_yday < gmt.tm_yday)
3518                 off -= 24 * 60;
3519         else if (lt->tm_yday > gmt.tm_yday)
3520                 off += 24 * 60;
3521
3522         if (off < 0) {
3523                 sign = '-';
3524                 off = -off;
3525         }
3526
3527         if (off >= 24 * 60)             /* should be impossible */
3528                 off = 23 * 60 + 59;     /* if not, insert silly value */
3529
3530         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3531
3532         return offset_string;
3533 }
3534
3535 void get_rfc822_date(gchar *buf, gint len)
3536 {
3537         struct tm *lt;
3538         time_t t;
3539         gchar day[4], mon[4];
3540         gint dd, hh, mm, ss, yyyy;
3541 #ifndef G_OS_WIN32
3542         struct tm buf1;
3543         gchar buf2[BUFFSIZE];
3544 #endif
3545
3546         t = time(NULL);
3547         lt = localtime_r(&t, &buf1);
3548
3549         sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3550                day, mon, &dd, &hh, &mm, &ss, &yyyy);
3551
3552         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3553                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3554 }
3555
3556 void debug_set_mode(gboolean mode)
3557 {
3558         debug_mode = mode;
3559 }
3560
3561 gboolean debug_get_mode(void)
3562 {
3563         return debug_mode;
3564 }
3565
3566 void debug_print_real(const gchar *format, ...)
3567 {
3568         va_list args;
3569         gchar buf[BUFFSIZE];
3570
3571         if (!debug_mode) return;
3572
3573         va_start(args, format);
3574         g_vsnprintf(buf, sizeof(buf), format, args);
3575         va_end(args);
3576
3577         g_print("%s", buf);
3578 }
3579
3580
3581 const char * debug_srcname(const char *file)
3582 {
3583         const char *s = strrchr (file, '/');
3584         return s? s+1:file;
3585 }
3586
3587
3588 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3589 {
3590         if (subject == NULL)
3591                 subject = "";
3592         else
3593                 subject += subject_get_prefix_length(subject);
3594
3595         return g_hash_table_lookup(subject_table, subject);
3596 }
3597
3598 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3599                           void * data)
3600 {
3601         if (subject == NULL || *subject == 0)
3602                 return;
3603         subject += subject_get_prefix_length(subject);
3604         g_hash_table_insert(subject_table, subject, data);
3605 }
3606
3607 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3608 {
3609         if (subject == NULL)
3610                 return;
3611
3612         subject += subject_get_prefix_length(subject);
3613         g_hash_table_remove(subject_table, subject);
3614 }
3615
3616 /*!
3617  *\brief        Check if a string is prefixed with known (combinations)
3618  *              of prefixes. The function assumes that each prefix
3619  *              is terminated by zero or exactly _one_ space.
3620  *
3621  *\param        str String to check for a prefixes
3622  *
3623  *\return       int Number of chars in the prefix that should be skipped
3624  *              for a "clean" subject line. If no prefix was found, 0
3625  *              is returned.
3626  */
3627  
3628 #ifndef G_OS_WIN32
3629 static regex_t u_regex;
3630 static gboolean u_init_;
3631 #endif
3632
3633 void utils_free_regex(void)
3634 {
3635 #ifndef G_OS_WIN32
3636         if (u_init_) {
3637                 regfree(&u_regex);
3638                 u_init_ = FALSE;
3639         }
3640 #endif
3641 }
3642
3643 int subject_get_prefix_length(const gchar *subject)
3644 {
3645 #ifndef G_OS_WIN32
3646         /*!< Array with allowable reply prefixes regexps. */
3647         static const gchar * const prefixes[] = {
3648                 "Re\\:",                        /* "Re:" */
3649                 "Re\\[[1-9][0-9]*\\]\\:",       /* "Re[XXX]:" (non-conforming news mail clients) */
3650                 "Antw\\:",                      /* "Antw:" (Dutch / German Outlook) */
3651                 "Aw\\:",                        /* "Aw:"   (German) */
3652                 "Antwort\\:",                   /* "Antwort:" (German Lotus Notes) */
3653                 "Res\\:",                       /* "Res:" (Brazilian Outlook) */
3654                 "Fw\\:",                        /* "Fw:" Forward */
3655                 "Fwd\\:",                       /* "Fwd:" Forward */
3656                 "Enc\\:",                       /* "Enc:" Forward (Brazilian Outlook) */
3657                 "Odp\\:",                       /* "Odp:" Re (Polish Outlook) */
3658                 "Rif\\:",                       /* "Rif:" (Italian Outlook) */
3659                 "Sv\\:",                        /* "Sv" (Norwegian) */
3660                 "Vs\\:",                        /* "Vs" (Norwegian) */
3661                 "Ad\\:",                        /* "Ad" (Norwegian) */
3662                 "\347\255\224\345\244\215\\:"   /* "Re" (Chinese, UTF-8) */
3663                 /* add more */
3664         };
3665         const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3666         int n;
3667         regmatch_t pos;
3668
3669         if (!subject) return 0;
3670         if (!*subject) return 0;
3671
3672         if (!u_init_) {
3673                 GString *s = g_string_new("");
3674
3675                 for (n = 0; n < PREFIXES; n++)
3676                         /* Terminate each prefix regexpression by a
3677                          * "\ ?" (zero or ONE space), and OR them */
3678                         g_string_append_printf(s, "(%s\\ ?)%s",
3679                                           prefixes[n],
3680                                           n < PREFIXES - 1 ?
3681                                           "|" : "");
3682
3683                 g_string_prepend(s, "(");
3684                 g_string_append(s, ")+");       /* match at least once */
3685                 g_string_prepend(s, "^\\ *");   /* from beginning of line */
3686
3687
3688                 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3689                  * TODO: Should this be       "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3690                 if (regcomp(&u_regex, s->str, REG_EXTENDED | REG_ICASE)) {
3691                         debug_print("Error compiling regexp %s\n", s->str);
3692                         g_string_free(s, TRUE);
3693                         return 0;
3694                 } else {
3695                         u_init_ = TRUE;
3696                         g_string_free(s, TRUE);
3697                 }
3698         }
3699
3700         if (!regexec(&u_regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3701                 return pos.rm_eo;
3702         else
3703                 return 0;
3704 #else
3705         /*!< Array with allowable reply prefixes regexps. */
3706         static const gchar * const prefixes[] = {
3707                 "re:",                  /* "Re:" */
3708                 "antw:",                        /* "Antw:" (Dutch / German Outlook) */
3709                 "aw:",                  /* "Aw:"   (German) */
3710                 "antwort:",                     /* "Antwort:" (German Lotus Notes) */
3711                 "res:",                 /* "Res:" (Brazilian Outlook) */
3712                 "fw:",                  /* "Fw:" Forward */
3713                 "fwd:",                 /* "Fwd:" Forward */
3714                 "enc:",                 /* "Enc:" Forward (Brazilian Outlook) */
3715                 "odp:",                 /* "Odp:" Re (Polish Outlook) */
3716                 "rif:",                 /* "Rif:" (Italian Outlook) */
3717                 "sv:",                  /* "Sv" (Norwegian) */
3718                 "vs:",                  /* "Vs" (Norwegian) */
3719                 "ad:",                  /* "Ad" (Norwegian) */
3720                 /* add more */
3721         };
3722         const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3723         int n;
3724
3725         if (!subject) return 0;
3726         if (!*subject) return 0;
3727
3728         for (n = 0; n < PREFIXES; n++) {
3729                 int len = strlen(prefixes[n]);
3730                 if (!strncasecmp(subject, prefixes[n], len))
3731                         return len;
3732         }
3733         return 0;
3734 #endif
3735 }
3736 static guint g_stricase_hash(gconstpointer gptr)
3737 {
3738         guint hash_result = 0;
3739         const char *str;
3740
3741         for (str = gptr; str && *str; str++) {
3742                 hash_result += toupper(*str);
3743         }
3744
3745         return hash_result;
3746 }
3747
3748 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3749 {
3750         const char *str1 = gptr1;
3751         const char *str2 = gptr2;
3752
3753         return !strcasecmp(str1, str2);
3754 }
3755
3756 gint g_int_compare(gconstpointer a, gconstpointer b)
3757 {
3758         return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3759 }
3760
3761 gchar *generate_msgid(gchar *buf, gint len)
3762 {
3763         struct tm *lt;
3764         time_t t;
3765         gchar *addr;
3766 #ifndef G_OS_WIN32
3767         struct tm buft;
3768 #endif
3769
3770         t = time(NULL);
3771         lt = localtime_r(&t, &buft);
3772
3773         if (strcmp(buf, "") == 0) {
3774                 addr = g_strconcat("@", get_domain_name(), NULL);
3775         }
3776         else {
3777                 addr = g_strconcat("@", buf, NULL);
3778         }
3779
3780         g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
3781                    lt->tm_year + 1900, lt->tm_mon + 1,
3782                    lt->tm_mday, lt->tm_hour,
3783                    lt->tm_min, lt->tm_sec,
3784                    (guint) rand(), addr);
3785
3786         g_free(addr);
3787         return buf;
3788 }
3789
3790 /*
3791    quote_cmd_argument()
3792
3793    return a quoted string safely usable in argument of a command.
3794
3795    code is extracted and adapted from etPan! project -- DINH V. Hoà.
3796 */
3797
3798 gint quote_cmd_argument(gchar * result, guint size,
3799                         const gchar * path)
3800 {
3801         const gchar * p;
3802         gchar * result_p;
3803         guint remaining;
3804
3805         result_p = result;
3806         remaining = size;
3807
3808         for(p = path ; * p != '\0' ; p ++) {
3809
3810                 if (isalnum((guchar)*p) || (* p == '/')) {
3811                         if (remaining > 0) {
3812                                 * result_p = * p;
3813                                 result_p ++;
3814                                 remaining --;
3815                         }
3816                         else {
3817                                 result[size - 1] = '\0';
3818                                 return -1;
3819                         }
3820                 }
3821                 else {
3822                         if (remaining >= 2) {
3823                                 * result_p = '\\';
3824                                 result_p ++;
3825                                 * result_p = * p;
3826                                 result_p ++;
3827                                 remaining -= 2;
3828                         }
3829                         else {
3830                                 result[size - 1] = '\0';
3831                                 return -1;
3832                         }
3833                 }
3834         }
3835         if (remaining > 0) {
3836                 * result_p = '\0';
3837         }
3838         else {
3839                 result[size - 1] = '\0';
3840                 return -1;
3841         }
3842
3843         return 0;
3844 }
3845
3846 typedef struct
3847 {
3848         GNode           *parent;
3849         GNodeMapFunc     func;
3850         gpointer         data;
3851 } GNodeMapData;
3852
3853 static void g_node_map_recursive(GNode *node, gpointer data)
3854 {
3855         GNodeMapData *mapdata = (GNodeMapData *) data;
3856         GNode *newnode;
3857         GNodeMapData newmapdata;
3858         gpointer newdata;
3859
3860         newdata = mapdata->func(node->data, mapdata->data);
3861         if (newdata != NULL) {
3862                 newnode = g_node_new(newdata);
3863                 g_node_append(mapdata->parent, newnode);
3864
3865                 newmapdata.parent = newnode;
3866                 newmapdata.func = mapdata->func;
3867                 newmapdata.data = mapdata->data;
3868
3869                 g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
3870         }
3871 }
3872
3873 GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
3874 {
3875         GNode *root;
3876         GNodeMapData mapdata;
3877
3878         g_return_val_if_fail(node != NULL, NULL);
3879         g_return_val_if_fail(func != NULL, NULL);
3880
3881         root = g_node_new(func(node->data, data));
3882
3883         mapdata.parent = root;
3884         mapdata.func = func;
3885         mapdata.data = data;
3886
3887         g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
3888
3889         return root;
3890 }
3891
3892 #define HEX_TO_INT(val, hex)                    \
3893 {                                               \
3894         gchar c = hex;                          \
3895                                                 \
3896         if ('0' <= c && c <= '9') {             \
3897                 val = c - '0';                  \
3898         } else if ('a' <= c && c <= 'f') {      \
3899                 val = c - 'a' + 10;             \
3900         } else if ('A' <= c && c <= 'F') {      \
3901                 val = c - 'A' + 10;             \
3902         } else {                                \
3903                 val = -1;                       \
3904         }                                       \
3905 }
3906
3907 gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
3908 {
3909         gint hi, lo;
3910
3911         HEX_TO_INT(hi, c1);
3912         HEX_TO_INT(lo, c2);
3913
3914         if (hi == -1 || lo == -1)
3915                 return FALSE;
3916
3917         *out = (hi << 4) + lo;
3918         return TRUE;
3919 }
3920
3921 #define INT_TO_HEX(hex, val)            \
3922 {                                       \
3923         if ((val) < 10)                 \
3924                 hex = '0' + (val);      \
3925         else                            \
3926                 hex = 'A' + (val) - 10; \
3927 }
3928
3929 void get_hex_str(gchar *out, guchar ch)
3930 {
3931         gchar hex;
3932
3933         INT_TO_HEX(hex, ch >> 4);
3934         *out++ = hex;
3935         INT_TO_HEX(hex, ch & 0x0f);
3936         *out++ = hex;
3937 }
3938
3939 #undef REF_DEBUG
3940 #ifndef REF_DEBUG
3941 #define G_PRINT_REF 1 == 1 ? (void) 0 : (void)
3942 #else
3943 #define G_PRINT_REF g_print
3944 #endif
3945
3946 /*!
3947  *\brief        Register ref counted pointer. It is based on GBoxed, so should
3948  *              work with anything that uses the GType system. The semantics
3949  *              are similar to a C++ auto pointer, with the exception that
3950  *              C doesn't have automatic closure (calling destructors) when
3951  *              exiting a block scope.
3952  *              Use the \ref G_TYPE_AUTO_POINTER macro instead of calling this
3953  *              function directly.
3954  *
3955  *\return       GType A GType type.
3956  */
3957 GType g_auto_pointer_register(void)
3958 {
3959         static GType auto_pointer_type;
3960         if (!auto_pointer_type)
3961                 auto_pointer_type =
3962                         g_boxed_type_register_static
3963                                 ("G_TYPE_AUTO_POINTER",
3964                                  (GBoxedCopyFunc) g_auto_pointer_copy,
3965                                  (GBoxedFreeFunc) g_auto_pointer_free);
3966         return auto_pointer_type;
3967 }
3968
3969 /*!
3970  *\brief        Structure with g_new() allocated pointer guarded by the
3971  *              auto pointer
3972  */
3973 typedef struct AutoPointerRef {
3974         void          (*free) (gpointer);
3975         gpointer        pointer;
3976         glong           cnt;
3977 } AutoPointerRef;
3978
3979 /*!
3980  *\brief        The auto pointer opaque structure that references the
3981  *              pointer guard block.
3982  */
3983 typedef struct AutoPointer {
3984         AutoPointerRef *ref;
3985         gpointer        ptr; /*!< access to protected pointer */
3986 } AutoPointer;
3987
3988 /*!
3989  *\brief        Creates an auto pointer for a g_new()ed pointer. Example:
3990  *
3991  *\code
3992  *
3993  *              ... tell gtk_list_store it should use a G_TYPE_AUTO_POINTER
3994  *              ... when assigning, copying and freeing storage elements
3995  *
3996  *              gtk_list_store_new(N_S_COLUMNS,
3997  *                                 G_TYPE_AUTO_POINTER,
3998  *                                 -1);
3999  *
4000  *
4001  *              Template *precious_data = g_new0(Template, 1);
4002  *              g_pointer protect = g_auto_pointer_new(precious_data);
4003  *
4004  *              gtk_list_store_set(container, &iter,
4005  *                                 S_DATA, protect,
4006  *                                 -1);
4007  *
4008  *              ... the gtk_list_store has copied the pointer and
4009  *              ... incremented its reference count, we should free
4010  *              ... the auto pointer (in C++ a destructor would do
4011  *              ... this for us when leaving block scope)
4012  *
4013  *              g_auto_pointer_free(protect);
4014  *
4015  *              ... gtk_list_store_set() now manages the data. When
4016  *              ... *explicitly* requesting a pointer from the list
4017  *              ... store, don't forget you get a copy that should be
4018  *              ... freed with g_auto_pointer_free() eventually.
4019  *
4020  *\endcode
4021  *
4022  *\param        pointer Pointer to be guarded.
4023  *
4024  *\return       GAuto * Pointer that should be used in containers with
4025  *              GType support.
4026  */
4027 GAuto *g_auto_pointer_new(gpointer p)
4028 {
4029         AutoPointerRef *ref;
4030         AutoPointer    *ptr;
4031
4032         if (p == NULL)
4033                 return NULL;
4034
4035         ref = g_new0(AutoPointerRef, 1);
4036         ptr = g_new0(AutoPointer, 1);
4037
4038         ref->pointer = p;
4039         ref->free = g_free;
4040         ref->cnt = 1;
4041
4042         ptr->ref = ref;
4043         ptr->ptr = p;
4044
4045 #ifdef REF_DEBUG
4046         G_PRINT_REF ("XXXX ALLOC(%lx)\n", p);
4047 #endif
4048         return ptr;
4049 }
4050
4051 /*!
4052  *\brief        Allocate an autopointer using the passed \a free function to
4053  *              free the guarded pointer
4054  */
4055 GAuto *g_auto_pointer_new_with_free(gpointer p, GFreeFunc free_)
4056 {
4057         AutoPointer *aptr;
4058
4059         if (p == NULL)
4060                 return NULL;
4061
4062         aptr = g_auto_pointer_new(p);
4063         aptr->ref->free = free_;
4064         return aptr;
4065 }
4066
4067 gpointer g_auto_pointer_get_ptr(GAuto *auto_ptr)
4068 {
4069         if (auto_ptr == NULL)
4070                 return NULL;
4071         return ((AutoPointer *) auto_ptr)->ptr;
4072 }
4073
4074 /*!
4075  *\brief        Copies an auto pointer by. It's mostly not necessary
4076  *              to call this function directly, unless you copy/assign
4077  *              the guarded pointer.
4078  *
4079  *\param        auto_ptr Auto pointer returned by previous call to
4080  *              g_auto_pointer_new_XXX()
4081  *
4082  *\return       gpointer An auto pointer
4083  */
4084 GAuto *g_auto_pointer_copy(GAuto *auto_ptr)
4085 {
4086         AutoPointer     *ptr;
4087         AutoPointerRef  *ref;
4088         AutoPointer     *newp;
4089
4090         if (auto_ptr == NULL)
4091                 return NULL;
4092
4093         ptr = auto_ptr;
4094         ref = ptr->ref;
4095         newp = g_new0(AutoPointer, 1);
4096
4097         newp->ref = ref;
4098         newp->ptr = ref->pointer;
4099         ++(ref->cnt);
4100
4101 #ifdef REF_DEBUG
4102         G_PRINT_REF ("XXXX COPY(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4103 #endif
4104         return newp;
4105 }
4106
4107 /*!
4108  *\brief        Free an auto pointer
4109  */
4110 void g_auto_pointer_free(GAuto *auto_ptr)
4111 {
4112         AutoPointer     *ptr;
4113         AutoPointerRef  *ref;
4114
4115         if (auto_ptr == NULL)
4116                 return;
4117
4118         ptr = auto_ptr;
4119         ref = ptr->ref;
4120
4121         if (--(ref->cnt) == 0) {
4122 #ifdef REF_DEBUG
4123                 G_PRINT_REF ("XXXX FREE(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4124 #endif
4125                 ref->free(ref->pointer);
4126                 g_free(ref);
4127         }
4128 #ifdef REF_DEBUG
4129         else
4130                 G_PRINT_REF ("XXXX DEREF(%lx) -- REF (%d)\n", ref->pointer, ref->cnt);
4131 #endif
4132         g_free(ptr);
4133 }
4134
4135 void replace_returns(gchar *str)
4136 {
4137         if (!str)
4138                 return;
4139
4140         while (strstr(str, "\n")) {
4141                 *strstr(str, "\n") = ' ';
4142         }
4143         while (strstr(str, "\r")) {
4144                 *strstr(str, "\r") = ' ';
4145         }
4146 }
4147
4148 /* get_uri_part() - retrieves a URI starting from scanpos.
4149                     Returns TRUE if succesful */
4150 gboolean get_uri_part(const gchar *start, const gchar *scanpos,
4151                              const gchar **bp, const gchar **ep, gboolean hdr)
4152 {
4153         const gchar *ep_;
4154         gint parenthese_cnt = 0;
4155
4156         g_return_val_if_fail(start != NULL, FALSE);
4157         g_return_val_if_fail(scanpos != NULL, FALSE);
4158         g_return_val_if_fail(bp != NULL, FALSE);
4159         g_return_val_if_fail(ep != NULL, FALSE);
4160
4161         *bp = scanpos;
4162
4163         /* find end point of URI */
4164         for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
4165                 if (!g_ascii_isgraph(*(const guchar *)ep_) ||
4166                     !IS_ASCII(*(const guchar *)ep_) ||
4167                     strchr("[]{}<>\"", *ep_)) {
4168                         break;
4169                 } else if (strchr("(", *ep_)) {
4170                         parenthese_cnt++;
4171                 } else if (strchr(")", *ep_)) {
4172                         if (parenthese_cnt > 0)
4173                                 parenthese_cnt--;
4174                         else
4175                                 break;
4176                 }
4177         }
4178
4179         /* no punctuation at end of string */
4180
4181         /* FIXME: this stripping of trailing punctuations may bite with other URIs.
4182          * should pass some URI type to this function and decide on that whether
4183          * to perform punctuation stripping */
4184
4185 #define IS_REAL_PUNCT(ch)       (g_ascii_ispunct(ch) && !strchr("/?=-)", ch))
4186
4187         for (; ep_ - 1 > scanpos + 1 &&
4188                IS_REAL_PUNCT(*(ep_ - 1));
4189              ep_--)
4190                 ;
4191
4192 #undef IS_REAL_PUNCT
4193
4194         *ep = ep_;
4195
4196         return TRUE;
4197 }
4198
4199 gchar *make_uri_string(const gchar *bp, const gchar *ep)
4200 {
4201         while (bp && *bp && g_ascii_isspace(*bp))
4202                 bp++;
4203         return g_strndup(bp, ep - bp);
4204 }
4205
4206 /* valid mail address characters */
4207 #define IS_RFC822_CHAR(ch) \
4208         (IS_ASCII(ch) && \
4209          (ch) > 32   && \
4210          (ch) != 127 && \
4211          !g_ascii_isspace(ch) && \
4212          !strchr("(),;<>\"", (ch)))
4213
4214 /* alphabet and number within 7bit ASCII */
4215 #define IS_ASCII_ALNUM(ch)      (IS_ASCII(ch) && g_ascii_isalnum(ch))
4216 #define IS_QUOTE(ch) ((ch) == '\'' || (ch) == '"')
4217
4218 static GHashTable *create_domain_tab(void)
4219 {
4220         static const gchar *toplvl_domains [] = {
4221             "museum", "aero",
4222             "arpa", "coop", "info", "name", "biz", "com", "edu", "gov",
4223             "int", "mil", "net", "org", "ac", "ad", "ae", "af", "ag",
4224             "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au",
4225             "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi",
4226             "bj", "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by",
4227             "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl",
4228             "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
4229             "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er",
4230             "es", "et", "eu", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gd",
4231             "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq",
4232             "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
4233             "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir",
4234             "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki",
4235             "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc",
4236             "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
4237             "md", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq",
4238             "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na",
4239             "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
4240             "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
4241             "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru",
4242             "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj",
4243             "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz",
4244             "tc", "td", "tf", "tg", "th", "tj", "tk", "tm", "tn", "to",
4245             "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um",
4246             "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu",
4247             "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
4248         };
4249         gint n;
4250         GHashTable *htab = g_hash_table_new(g_stricase_hash, g_stricase_equal);
4251
4252         g_return_val_if_fail(htab, NULL);
4253         for (n = 0; n < sizeof toplvl_domains / sizeof toplvl_domains[0]; n++)
4254                 g_hash_table_insert(htab, (gpointer) toplvl_domains[n], (gpointer) toplvl_domains[n]);
4255         return htab;
4256 }
4257
4258 static gboolean is_toplvl_domain(GHashTable *tab, const gchar *first, const gchar *last)
4259 {
4260         const gint MAX_LVL_DOM_NAME_LEN = 6;
4261         gchar buf[MAX_LVL_DOM_NAME_LEN + 1];
4262         const gchar *m = buf + MAX_LVL_DOM_NAME_LEN + 1;
4263         register gchar *p;
4264
4265         if (last - first > MAX_LVL_DOM_NAME_LEN || first > last)
4266                 return FALSE;
4267
4268         for (p = buf; p < m &&  first < last; *p++ = *first++)
4269                 ;
4270         *p = 0;
4271
4272         return g_hash_table_lookup(tab, buf) != NULL;
4273 }
4274
4275 /* get_email_part() - retrieves an email address. Returns TRUE if succesful */
4276 gboolean get_email_part(const gchar *start, const gchar *scanpos,
4277                                const gchar **bp, const gchar **ep, gboolean hdr)
4278 {
4279         /* more complex than the uri part because we need to scan back and forward starting from
4280          * the scan position. */
4281         gboolean result = FALSE;
4282         const gchar *bp_ = NULL;
4283         const gchar *ep_ = NULL;
4284         static GHashTable *dom_tab;
4285         const gchar *last_dot = NULL;
4286         const gchar *prelast_dot = NULL;
4287         const gchar *last_tld_char = NULL;
4288
4289         /* the informative part of the email address (describing the name
4290          * of the email address owner) may contain quoted parts. the
4291          * closure stack stores the last encountered quotes. */
4292         gchar closure_stack[128];
4293         gchar *ptr = closure_stack;
4294
4295         g_return_val_if_fail(start != NULL, FALSE);
4296         g_return_val_if_fail(scanpos != NULL, FALSE);
4297         g_return_val_if_fail(bp != NULL, FALSE);
4298         g_return_val_if_fail(ep != NULL, FALSE);
4299
4300         if (hdr) {
4301                 const gchar *start_quote = NULL;
4302                 const gchar *end_quote = NULL;
4303 search_again:
4304                 /* go to the real start */
4305                 if (start[0] == ',')
4306                         start++;
4307                 if (start[0] == ';')
4308                         start++;
4309                 while (start[0] == '\n' || start[0] == '\r')
4310                         start++;
4311                 while (start[0] == ' ' || start[0] == '\t')
4312                         start++;
4313
4314                 *bp = start;
4315                 
4316                 /* check if there are quotes (to skip , in them) */
4317                 if (*start == '"') {
4318                         start_quote = start;
4319                         start++;
4320                         end_quote = strstr(start, "\"");
4321                 } else {
4322                         start_quote = NULL;
4323                         end_quote = NULL;
4324                 }
4325                 
4326                 /* skip anything between quotes */
4327                 if (start_quote && end_quote) {
4328                         start = end_quote;
4329                         
4330                 } 
4331
4332                 /* find end (either , or ; or end of line) */
4333                 if (strstr(start, ",") && strstr(start, ";"))
4334                         *ep = strstr(start,",") < strstr(start, ";")
4335                                 ? strstr(start, ",") : strstr(start, ";");
4336                 else if (strstr(start, ","))
4337                         *ep = strstr(start, ",");
4338                 else if (strstr(start, ";"))
4339                         *ep = strstr(start, ";");
4340                 else
4341                         *ep = start+strlen(start);
4342
4343                 /* go back to real start */
4344                 if (start_quote && end_quote) {
4345                         start = start_quote;
4346                 }
4347
4348                 /* check there's still an @ in that, or search
4349                  * further if possible */
4350                 if (strstr(start, "@") && strstr(start, "@") < *ep)
4351                         return TRUE;
4352                 else if (*ep < start+strlen(start)) {
4353                         start = *ep;
4354                         goto search_again;
4355                 } else if (start_quote && strstr(start, "\"") && strstr(start, "\"") < *ep) {
4356                         *bp = start_quote;
4357                         return TRUE;
4358                 } else
4359                         return FALSE;
4360         }
4361
4362         if (!dom_tab)
4363                 dom_tab = create_domain_tab();
4364         g_return_val_if_fail(dom_tab, FALSE);
4365
4366         /* scan start of address */
4367         for (bp_ = scanpos - 1;
4368              bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
4369                 ;
4370
4371         /* TODO: should start with an alnum? */
4372         bp_++;
4373         for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
4374                 ;
4375
4376         if (bp_ != scanpos) {
4377                 /* scan end of address */
4378                 for (ep_ = scanpos + 1;
4379                      *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
4380                         if (*ep_ == '.') {
4381                                 prelast_dot = last_dot;
4382                                 last_dot = ep_;
4383                                 if (*(last_dot + 1) == '.') {
4384                                         if (prelast_dot == NULL)
4385                                                 return FALSE;
4386                                         last_dot = prelast_dot;
4387                                         break;
4388                                 }
4389                         }
4390
4391                 /* TODO: really should terminate with an alnum? */
4392                 for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
4393                      --ep_)
4394                         ;
4395                 ep_++;
4396
4397                 if (last_dot == NULL)
4398                         return FALSE;
4399                 if (last_dot >= ep_)
4400                         last_dot = prelast_dot;
4401                 if (last_dot == NULL || (scanpos + 1 >= last_dot))
4402                         return FALSE;
4403                 last_dot++;
4404
4405                 for (last_tld_char = last_dot; last_tld_char < ep_; last_tld_char++)
4406                         if (*last_tld_char == '?')
4407                                 break;
4408
4409                 if (is_toplvl_domain(dom_tab, last_dot, last_tld_char))
4410                         result = TRUE;
4411
4412                 *ep = ep_;
4413                 *bp = bp_;
4414         }
4415
4416         if (!result) return FALSE;
4417
4418         if (*ep_ && bp_ != start && *(bp_ - 1) == '"' && *(ep_) == '"'
4419         && *(ep_ + 1) == ' ' && *(ep_ + 2) == '<'
4420         && IS_RFC822_CHAR(*(ep_ + 3))) {
4421                 /* this informative part with an @ in it is
4422                  * followed by the email address */
4423                 ep_ += 3;
4424
4425                 /* go to matching '>' (or next non-rfc822 char, like \n) */
4426                 for (; *ep_ != '>' && *ep != '\0' && IS_RFC822_CHAR(*ep_); ep_++)
4427                         ;
4428
4429                 /* include the bracket */
4430                 if (*ep_ == '>') ep_++;
4431
4432                 /* include the leading quote */
4433                 bp_--;
4434
4435                 *ep = ep_;
4436                 *bp = bp_;
4437                 return TRUE;
4438         }
4439
4440         /* skip if it's between quotes "'alfons@proteus.demon.nl'" <alfons@proteus.demon.nl> */
4441         if (bp_ - 1 > start && IS_QUOTE(*(bp_ - 1)) && IS_QUOTE(*ep_))
4442                 return FALSE;
4443
4444         /* see if this is <bracketed>; in this case we also scan for the informative part. */
4445         if (bp_ - 1 <= start || *(bp_ - 1) != '<' || *ep_ != '>')
4446                 return TRUE;
4447
4448 #define FULL_STACK()    ((size_t) (ptr - closure_stack) >= sizeof closure_stack)
4449 #define IN_STACK()      (ptr > closure_stack)
4450 /* has underrun check */
4451 #define POP_STACK()     if(IN_STACK()) --ptr
4452 /* has overrun check */
4453 #define PUSH_STACK(c)   if(!FULL_STACK()) *ptr++ = (c); else return TRUE
4454 /* has underrun check */
4455 #define PEEK_STACK()    (IN_STACK() ? *(ptr - 1) : 0)
4456
4457         ep_++;
4458
4459         /* scan for the informative part. */
4460         for (bp_ -= 2; bp_ >= start; bp_--) {
4461                 /* if closure on the stack keep scanning */
4462                 if (PEEK_STACK() == *bp_) {
4463                         POP_STACK();
4464                         continue;
4465                 }
4466                 if (*bp_ == '\'' || *bp_ == '"') {
4467                         PUSH_STACK(*bp_);
4468                         continue;
4469                 }
4470
4471                 /* if nothing in the closure stack, do the special conditions
4472                  * the following if..else expression simply checks whether
4473                  * a token is acceptable. if not acceptable, the clause
4474                  * should terminate the loop with a 'break' */
4475                 if (!PEEK_STACK()) {
4476                         if (*bp_ == '-'
4477                         && (((bp_ - 1) >= start) && isalnum(*(bp_ - 1)))
4478                         && (((bp_ + 1) < ep_)    && isalnum(*(bp_ + 1)))) {
4479                                 /* hyphens are allowed, but only in
4480                                    between alnums */
4481                         } else if (strchr(" \"'", *bp_)) {
4482                                 /* but anything not being a punctiation
4483                                    is ok */
4484                         } else {
4485                                 break; /* anything else is rejected */
4486                         }
4487                 }
4488         }
4489
4490         bp_++;
4491
4492         /* scan forward (should start with an alnum) */
4493         for (; *bp_ != '<' && isspace(*bp_) && *bp_ != '"'; bp_++)
4494                 ;
4495 #undef PEEK_STACK
4496 #undef PUSH_STACK
4497 #undef POP_STACK
4498 #undef IN_STACK
4499 #undef FULL_STACK
4500
4501
4502         *bp = bp_;
4503         *ep = ep_;
4504
4505         return result;
4506 }
4507
4508 #undef IS_QUOTE
4509 #undef IS_ASCII_ALNUM
4510 #undef IS_RFC822_CHAR
4511
4512 gchar *make_email_string(const gchar *bp, const gchar *ep)
4513 {
4514         /* returns a mailto: URI; mailto: is also used to detect the
4515          * uri type later on in the button_pressed signal handler */
4516         gchar *tmp;
4517         gchar *result;
4518
4519         tmp = g_strndup(bp, ep - bp);
4520         result = g_strconcat("mailto:", tmp, NULL);
4521         g_free(tmp);
4522
4523         return result;
4524 }
4525
4526 gchar *make_http_string(const gchar *bp, const gchar *ep)
4527 {
4528         /* returns an http: URI; */
4529         gchar *tmp;
4530         gchar *result;
4531
4532         while (bp && *bp && g_ascii_isspace(*bp))
4533                 bp++;
4534         tmp = g_strndup(bp, ep - bp);
4535         result = g_strconcat("http://", tmp, NULL);
4536         g_free(tmp);
4537
4538         return result;
4539 }
4540
4541 static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type, const gchar *file_to_open)
4542 {
4543         FILE *fp = fopen(path, "rb");
4544         gchar buf[BUFFSIZE];
4545         gchar *result = NULL;
4546         if (!fp)
4547                 return NULL;
4548         while (fgets(buf, sizeof (buf), fp) != NULL) {
4549                 gchar **parts = g_strsplit(buf, ";", 3);
4550                 gchar *trimmed = parts[0];
4551                 while (trimmed[0] == ' ' || trimmed[0] == '\t')
4552                         trimmed++;
4553                 while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4554                         trimmed[strlen(trimmed)-1] = '\0';
4555
4556                 if (!strcmp(trimmed, type)) {
4557                         gboolean needsterminal = FALSE;
4558                         if (parts[2] && strstr(parts[2], "needsterminal")) {
4559                                 needsterminal = TRUE;
4560                         }
4561                         if (parts[2] && strstr(parts[2], "test=")) {
4562                                 gchar *orig_testcmd = g_strdup(strstr(parts[2], "test=")+5);
4563                                 gchar *testcmd = orig_testcmd;
4564                                 if (strstr(testcmd,";"))
4565                                         *(strstr(testcmd,";")) = '\0';
4566                                 while (testcmd[0] == ' ' || testcmd[0] == '\t')
4567                                         testcmd++;
4568                                 while (testcmd[strlen(testcmd)-1] == '\n')
4569                                         testcmd[strlen(testcmd)-1] = '\0';
4570                                 while (testcmd[strlen(testcmd)-1] == '\r')
4571                                         testcmd[strlen(testcmd)-1] = '\0';
4572                                 while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
4573                                         testcmd[strlen(testcmd)-1] = '\0';
4574                                         
4575                                 if (strstr(testcmd, "%s")) {
4576                                         gchar *tmp = g_strdup_printf(testcmd, file_to_open);
4577                                         gint res = system(tmp);
4578                                         g_free(tmp);
4579                                         g_free(orig_testcmd);
4580                                         
4581                                         if (res != 0) {
4582                                                 g_strfreev(parts);
4583                                                 continue;
4584                                         }
4585                                 } else {
4586                                         gint res = system(testcmd);
4587                                         g_free(orig_testcmd);
4588                                         
4589                                         if (res != 0) {
4590                                                 g_strfreev(parts);
4591                                                 continue;
4592                                         }
4593                                 }
4594                         }
4595                         
4596                         trimmed = parts[1];
4597                         while (trimmed[0] == ' ' || trimmed[0] == '\t')
4598                                 trimmed++;
4599                         while (trimmed[strlen(trimmed)-1] == '\n')
4600                                 trimmed[strlen(trimmed)-1] = '\0';
4601                         while (trimmed[strlen(trimmed)-1] == '\r')
4602                                 trimmed[strlen(trimmed)-1] = '\0';
4603                         while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
4604                                 trimmed[strlen(trimmed)-1] = '\0';
4605                         result = g_strdup(trimmed);
4606                         g_strfreev(parts);
4607                         fclose(fp);
4608                         /* if there are no single quotes around %s, add them.
4609                          * '.*%s.*' is ok, as in display 'png:%s'
4610                          */
4611                         if (strstr(result, "%s") 
4612                         && !(strstr(result, "'") < strstr(result,"%s") &&
4613                              strstr(strstr(result,"%s"), "'"))) {
4614                                 gchar *start = g_strdup(result);
4615                                 gchar *end = g_strdup(strstr(result, "%s")+2);
4616                                 gchar *tmp;
4617                                 *strstr(start, "%s") = '\0';
4618                                 tmp = g_strconcat(start,"'%s'",end, NULL);
4619                                 g_free(start);
4620                                 g_free(end);
4621                                 g_free(result);
4622                                 result = tmp;
4623                         }
4624                         if (needsterminal) {
4625                                 gchar *tmp = g_strdup_printf("xterm -e %s", result);
4626                                 g_free(result);
4627                                 result = tmp;
4628                         }
4629                         return result;
4630                 }
4631                 g_strfreev(parts);
4632         }
4633         fclose(fp);
4634         return NULL;
4635 }
4636 gchar *mailcap_get_command_for_type(const gchar *type, const gchar *file_to_open)
4637 {
4638         gchar *result = NULL;
4639         gchar *path = NULL;
4640         if (type == NULL)
4641                 return NULL;
4642         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4643         result = mailcap_get_command_in_file(path, type, file_to_open);
4644         g_free(path);
4645         if (result)
4646                 return result;
4647         result = mailcap_get_command_in_file("/etc/mailcap", type, file_to_open);
4648         return result;
4649 }
4650
4651 void mailcap_update_default(const gchar *type, const gchar *command)
4652 {
4653         gchar *path = NULL, *outpath = NULL;
4654         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap", NULL);
4655         outpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".mailcap.new", NULL);
4656         FILE *fp = fopen(path, "rb");
4657         FILE *outfp = fopen(outpath, "wb");
4658         gchar buf[BUFFSIZE];
4659         gboolean err = FALSE;
4660
4661         if (!outfp) {
4662                 g_free(path);
4663                 g_free(outpath);
4664                 fclose(fp);
4665                 return;
4666         }
4667         while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
4668                 gchar **parts = g_strsplit(buf, ";", 3);
4669                 gchar *trimmed = parts[0];
4670                 while (trimmed[0] == ' ')
4671                         trimmed++;
4672                 while (trimmed[strlen(trimmed)-1] == ' ')
4673                         trimmed[strlen(trimmed)-1] = '\0';
4674
4675                 if (!strcmp(trimmed, type)) {
4676                         g_strfreev(parts);
4677                         continue;
4678                 }
4679                 else {
4680                         if(fputs(buf, outfp) == EOF) {
4681                                 err = TRUE;
4682                                 break;
4683                         }
4684                 }
4685                 g_strfreev(parts);
4686         }
4687         if (fprintf(outfp, "%s; %s\n", type, command) < 0)
4688                 err = TRUE;
4689
4690         if (fp)
4691                 fclose(fp);
4692
4693         if (fclose(outfp) == EOF)
4694                 err = TRUE;
4695                 
4696         if (!err)
4697                 g_rename(outpath, path);
4698
4699         g_free(path);
4700         g_free(outpath);
4701 }
4702
4703 gint copy_dir(const gchar *src, const gchar *dst)
4704 {
4705         GDir *dir;
4706         const gchar *name;
4707
4708         if ((dir = g_dir_open(src, 0, NULL)) == NULL) {
4709                 g_warning("failed to open directory: %s\n", src);
4710                 return -1;
4711         }
4712
4713         if (make_dir(dst) < 0)
4714                 return -1;
4715
4716         while ((name = g_dir_read_name(dir)) != NULL) {
4717                 gchar *old_file, *new_file;
4718                 old_file = g_strconcat(src, G_DIR_SEPARATOR_S, name, NULL);
4719                 new_file = g_strconcat(dst, G_DIR_SEPARATOR_S, name, NULL);
4720                 debug_print("copying: %s -> %s\n", old_file, new_file);
4721                 if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
4722                         gint r = copy_file(old_file, new_file, TRUE);
4723                         if (r < 0) {
4724                                 g_dir_close(dir);
4725                                 return r;
4726                         }
4727                 }
4728 #ifndef G_OS_WIN32
4729                 /* Windows has no symlinks.  Or well, Vista seems to
4730                    have something like this but the semantics might be
4731                    different.  Thus we don't use it under Windows. */
4732                  else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
4733                         GError *error;
4734                         gint r = 0;
4735                         gchar *target = g_file_read_link(old_file, &error);
4736                         if (target)
4737                                 r = symlink(target, new_file);
4738                         g_free(target);
4739                         if (r < 0) {
4740                                 g_dir_close(dir);
4741                                 return r;
4742                         }
4743                  }
4744 #endif /*G_OS_WIN32*/
4745                 else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
4746                         gint r = copy_dir(old_file, new_file);
4747                         if (r < 0) {
4748                                 g_dir_close(dir);
4749                                 return r;
4750                         }
4751                 }
4752         }
4753         g_dir_close(dir);
4754         return 0;
4755 }
4756
4757 /* crude test to see if a file is an email. */
4758 gboolean file_is_email (const gchar *filename)
4759 {
4760         FILE *fp = NULL;
4761         gchar buffer[2048];
4762         gint i = 0;
4763         gint score = 0;
4764         if (filename == NULL)
4765                 return FALSE;
4766         if ((fp = g_fopen(filename, "rb")) == NULL)
4767                 return FALSE;
4768         while (i < 60 && score < 3
4769                && fgets(buffer, sizeof (buffer), fp) > 0) {
4770                 if (!strncmp(buffer, "From:", strlen("From:")))
4771                         score++;
4772                 if (!strncmp(buffer, "To:", strlen("To:")))
4773                         score++;
4774                 if (!strncmp(buffer, "Subject:", strlen("Subject:")))
4775                         score++;
4776                 i++;
4777         }
4778         fclose(fp);
4779         return (score >= 3);
4780 }
4781
4782 gboolean sc_g_list_bigger(GList *list, gint max)
4783 {
4784         GList *cur = list;
4785         int i = 0;
4786         while (cur && i <= max+1) {
4787                 i++;
4788                 cur = cur->next;
4789         }
4790         return (i > max);
4791 }
4792
4793 gboolean sc_g_slist_bigger(GSList *list, gint max)
4794 {
4795         GSList *cur = list;
4796         int i = 0;
4797         while (cur && i <= max+1) {
4798                 i++;
4799                 cur = cur->next;
4800         }
4801         return (i > max);
4802 }
4803
4804 const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4805 const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
4806                              NULL, NULL, NULL, NULL, NULL, NULL};
4807 const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
4808 const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
4809                              NULL, NULL, NULL, NULL, NULL, NULL};
4810
4811 gint daynames_len[] =     {0,0,0,0,0,0,0};
4812 gint monthnames_len[] =   {0,0,0,0,0,0,
4813                                  0,0,0,0,0,0};
4814 gint s_daynames_len[] =   {0,0,0,0,0,0,0};
4815 gint s_monthnames_len[] = {0,0,0,0,0,0,
4816                                  0,0,0,0,0,0};
4817 const gchar *s_am_up = NULL;
4818 const gchar *s_pm_up = NULL;
4819 const gchar *s_am_low = NULL;
4820 const gchar *s_pm_low = NULL;
4821
4822 gint s_am_up_len = 0;
4823 gint s_pm_up_len = 0;
4824 gint s_am_low_len = 0;
4825 gint s_pm_low_len = 0;
4826
4827 const gchar *def_loc_format = NULL;
4828 const gchar *date_loc_format = NULL;
4829 const gchar *time_loc_format = NULL;
4830 const gchar *time_am_pm = NULL;
4831
4832 static gboolean time_names_init_done = FALSE;
4833
4834 static void init_time_names(void)
4835 {
4836         int i = 0;
4837
4838         daynames[0] = Q_("Complete day name for use by strftime|Sunday");
4839         daynames[1] = Q_("Complete day name for use by strftime|Monday");
4840         daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
4841         daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
4842         daynames[4] = Q_("Complete day name for use by strftime|Thursday");
4843         daynames[5] = Q_("Complete day name for use by strftime|Friday");
4844         daynames[6] = Q_("Complete day name for use by strftime|Saturday");
4845
4846         monthnames[0] = Q_("Complete month name for use by strftime|January");
4847         monthnames[1] = Q_("Complete month name for use by strftime|February");
4848         monthnames[2] = Q_("Complete month name for use by strftime|March");
4849         monthnames[3] = Q_("Complete month name for use by strftime|April");
4850         monthnames[4] = Q_("Complete month name for use by strftime|May");
4851         monthnames[5] = Q_("Complete month name for use by strftime|June");
4852         monthnames[6] = Q_("Complete month name for use by strftime|July");
4853         monthnames[7] = Q_("Complete month name for use by strftime|August");
4854         monthnames[8] = Q_("Complete month name for use by strftime|September");
4855         monthnames[9] = Q_("Complete month name for use by strftime|October");
4856         monthnames[10] = Q_("Complete month name for use by strftime|November");
4857         monthnames[11] = Q_("Complete month name for use by strftime|December");
4858
4859         s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
4860         s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
4861         s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
4862         s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
4863         s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
4864         s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
4865         s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
4866         
4867         s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
4868         s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
4869         s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
4870         s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
4871         s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
4872         s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
4873         s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
4874         s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
4875         s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
4876         s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
4877         s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
4878         s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
4879
4880         for (i = 0; i < 7; i++) {
4881                 daynames_len[i] = strlen(daynames[i]);
4882                 s_daynames_len[i] = strlen(s_daynames[i]);
4883         }
4884         for (i = 0; i < 12; i++) {
4885                 monthnames_len[i] = strlen(monthnames[i]);
4886                 s_monthnames_len[i] = strlen(s_monthnames[i]);
4887         }
4888
4889         s_am_up = Q_("For use by strftime (morning)|AM");
4890         s_pm_up = Q_("For use by strftime (afternoon)|PM");
4891         s_am_low = Q_("For use by strftime (morning, lowercase)|am");
4892         s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
4893         
4894         s_am_up_len = strlen(s_am_up);
4895         s_pm_up_len = strlen(s_pm_up);
4896         s_am_low_len = strlen(s_am_low);
4897         s_pm_low_len = strlen(s_pm_low);
4898         
4899         def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
4900         date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
4901         time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
4902
4903         time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
4904
4905         time_names_init_done = TRUE;
4906 }
4907
4908 #define CHECK_SIZE() {                  \
4909         total_done += len;              \
4910         if (total_done >= buflen) {     \
4911                 buf[buflen-1] = '\0';   \
4912                 return 0;               \
4913         }                               \
4914 }
4915
4916 size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
4917 {
4918         gchar *curpos = buf;
4919         gint total_done = 0;
4920         gchar subbuf[64], subfmt[64];
4921         static time_t last_tzset = (time_t)0;
4922         
4923         if (!time_names_init_done)
4924                 init_time_names();
4925         
4926         if (format == NULL || lt == NULL)
4927                 return 0;
4928                 
4929         if (last_tzset != time(NULL)) {
4930                 tzset();
4931                 last_tzset = time(NULL);
4932         }
4933         while(*format) {
4934                 if (*format == '%') {
4935                         gint len = 0, tmp = 0;
4936                         format++;
4937                         switch(*format) {
4938                         case '%':
4939                                 len = 1; CHECK_SIZE();
4940                                 *curpos = '%';
4941                                 break;
4942                         case 'a':
4943                                 len = s_daynames_len[lt->tm_wday]; CHECK_SIZE();
4944                                 strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
4945                                 break;
4946                         case 'A':
4947                                 len = daynames_len[lt->tm_wday]; CHECK_SIZE();
4948                                 strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
4949                                 break;
4950                         case 'b':
4951                         case 'h':
4952                                 len = s_monthnames_len[lt->tm_mon]; CHECK_SIZE();
4953                                 strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
4954                                 break;
4955                         case 'B':
4956                                 len = monthnames_len[lt->tm_mon]; CHECK_SIZE();
4957                                 strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
4958                                 break;
4959                         case 'c':
4960                                 fast_strftime(subbuf, 64, def_loc_format, lt);
4961                                 len = strlen(subbuf); CHECK_SIZE();
4962                                 strncpy2(curpos, subbuf, buflen - total_done);
4963                                 break;
4964                         case 'C':
4965                                 total_done += 2; CHECK_SIZE();
4966                                 tmp = (lt->tm_year + 1900)/100;
4967                                 *curpos++ = '0'+(tmp / 10);
4968                                 *curpos++ = '0'+(tmp % 10);
4969                                 break;
4970                         case 'd':
4971                                 total_done += 2; CHECK_SIZE();
4972                                 *curpos++ = '0'+(lt->tm_mday / 10);
4973                                 *curpos++ = '0'+(lt->tm_mday % 10);
4974                                 break;
4975                         case 'D':
4976                                 total_done += 8; CHECK_SIZE();
4977                                 *curpos++ = '0'+((lt->tm_mon+1) / 10);
4978                                 *curpos++ = '0'+((lt->tm_mon+1) % 10);
4979                                 *curpos++ = '/';
4980                                 *curpos++ = '0'+(lt->tm_mday / 10);
4981                                 *curpos++ = '0'+(lt->tm_mday % 10);
4982                                 *curpos++ = '/';
4983                                 tmp = lt->tm_year%100;
4984                                 *curpos++ = '0'+(tmp / 10);
4985                                 *curpos++ = '0'+(tmp % 10);
4986                                 break;
4987                         case 'e':
4988                                 len = 2; CHECK_SIZE();
4989                                 snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
4990                                 break;
4991                         case 'F':
4992                                 len = 10; CHECK_SIZE();
4993                                 snprintf(curpos, buflen - total_done, "%4d-%02d-%02d", 
4994                                         lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
4995                                 break;
4996                         case 'H':
4997                                 total_done += 2; CHECK_SIZE();
4998                                 *curpos++ = '0'+(lt->tm_hour / 10);
4999                                 *curpos++ = '0'+(lt->tm_hour % 10);
5000                                 break;
5001                         case 'I':
5002                                 total_done += 2; CHECK_SIZE();
5003                                 tmp = lt->tm_hour;
5004                                 if (tmp > 12)
5005                                         tmp -= 12;
5006                                 else if (tmp == 0)
5007                                         tmp = 12;
5008                                 *curpos++ = '0'+(tmp / 10);
5009                                 *curpos++ = '0'+(tmp % 10);
5010                                 break;
5011                         case 'j':
5012                                 len = 3; CHECK_SIZE();
5013                                 snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
5014                                 break;
5015                         case 'k':
5016                                 len = 2; CHECK_SIZE();
5017                                 snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
5018                                 break;
5019                         case 'l':
5020                                 len = 2; CHECK_SIZE();
5021                                 tmp = lt->tm_hour;
5022                                 if (tmp > 12)
5023                                         tmp -= 12;
5024                                 else if (tmp == 0)
5025                                         tmp = 12;
5026                                 snprintf(curpos, buflen - total_done, "%2d", tmp);
5027                                 break;
5028                         case 'm':
5029                                 total_done += 2; CHECK_SIZE();
5030                                 tmp = lt->tm_mon + 1;
5031                                 *curpos++ = '0'+(tmp / 10);
5032                                 *curpos++ = '0'+(tmp % 10);
5033                                 break;
5034                         case 'M':
5035                                 total_done += 2; CHECK_SIZE();
5036                                 *curpos++ = '0'+(lt->tm_min / 10);
5037                                 *curpos++ = '0'+(lt->tm_min % 10);
5038                                 break;
5039                         case 'n':
5040                                 len = 1; CHECK_SIZE();
5041                                 *curpos = '\n';
5042                                 break;
5043                         case 'p':
5044                                 if (lt->tm_hour >= 12) {
5045                                         len = s_pm_up_len; CHECK_SIZE();
5046                                         snprintf(curpos, buflen-total_done, "%s", s_pm_up);
5047                                 } else {
5048                                         len = s_am_up_len; CHECK_SIZE();
5049                                         snprintf(curpos, buflen-total_done, "%s", s_am_up);
5050                                 }
5051                                 break;
5052                         case 'P':
5053                                 if (lt->tm_hour >= 12) {
5054                                         len = s_pm_low_len; CHECK_SIZE();
5055                                         snprintf(curpos, buflen-total_done, "%s", s_pm_low);
5056                                 } else {
5057                                         len = s_am_low_len; CHECK_SIZE();
5058                                         snprintf(curpos, buflen-total_done, "%s", s_am_low);
5059                                 }
5060                                 break;
5061                         case 'r':
5062                                 fast_strftime(subbuf, 64, time_am_pm, lt);
5063                                 len = strlen(subbuf); CHECK_SIZE();
5064                                 strncpy2(curpos, subbuf, buflen - total_done);
5065                                 break;
5066                         case 'R':
5067                                 total_done += 5; CHECK_SIZE();
5068                                 *curpos++ = '0'+(lt->tm_hour / 10);
5069                                 *curpos++ = '0'+(lt->tm_hour % 10);
5070                                 *curpos++ = ':';
5071                                 *curpos++ = '0'+(lt->tm_min / 10);
5072                                 *curpos++ = '0'+(lt->tm_min % 10);
5073                                 break;
5074                         case 's':
5075                                 snprintf(subbuf, 64, "%ld", mktime(lt));
5076                                 len = strlen(subbuf); CHECK_SIZE();
5077                                 strncpy2(curpos, subbuf, buflen - total_done);
5078                                 break;
5079                         case 'S':
5080                                 total_done += 2; CHECK_SIZE();
5081                                 *curpos++ = '0'+(lt->tm_sec / 10);
5082                                 *curpos++ = '0'+(lt->tm_sec % 10);
5083                                 break;
5084                         case 't':
5085                                 len = 1; CHECK_SIZE();
5086                                 *curpos = '\t';
5087                                 break;
5088                         case 'T':
5089                                 total_done += 8; CHECK_SIZE();
5090                                 *curpos++ = '0'+(lt->tm_hour / 10);
5091                                 *curpos++ = '0'+(lt->tm_hour % 10);
5092                                 *curpos++ = ':';
5093                                 *curpos++ = '0'+(lt->tm_min / 10);
5094                                 *curpos++ = '0'+(lt->tm_min % 10);
5095                                 *curpos++ = ':';
5096                                 *curpos++ = '0'+(lt->tm_sec / 10);
5097                                 *curpos++ = '0'+(lt->tm_sec % 10);
5098                                 break;
5099                         case 'u':
5100                                 len = 1; CHECK_SIZE();
5101                                 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
5102                                 break;
5103                         case 'w':
5104                                 len = 1; CHECK_SIZE();
5105                                 snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
5106                                 break;
5107                         case 'x':
5108                                 fast_strftime(subbuf, 64, date_loc_format, lt);
5109                                 len = strlen(subbuf); CHECK_SIZE();
5110                                 strncpy2(curpos, subbuf, buflen - total_done);
5111                                 break;
5112                         case 'X':
5113                                 fast_strftime(subbuf, 64, time_loc_format, lt);
5114                                 len = strlen(subbuf); CHECK_SIZE();
5115                                 strncpy2(curpos, subbuf, buflen - total_done);
5116                                 break;
5117                         case 'y':
5118                                 total_done += 2; CHECK_SIZE();
5119                                 tmp = lt->tm_year%100;
5120                                 *curpos++ = '0'+(tmp / 10);
5121                                 *curpos++ = '0'+(tmp % 10);
5122                                 break;
5123                         case 'Y':
5124                                 len = 4; CHECK_SIZE();
5125                                 snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
5126                                 break;
5127                         case 'G':
5128                         case 'g':
5129                         case 'U':
5130                         case 'V':
5131                         case 'W':
5132                         case 'z':
5133                         case 'Z':
5134                         case '+':
5135                                 /* let these complicated ones be done with the libc */
5136                                 snprintf(subfmt, 64, "%%%c", *format);
5137                                 strftime(subbuf, 64, subfmt, lt);
5138                                 len = strlen(subbuf); CHECK_SIZE();
5139                                 strncpy2(curpos, subbuf, buflen - total_done);
5140                                 break;
5141                         case 'E':
5142                         case 'O':
5143                                 /* let these complicated modifiers be done with the libc */
5144                                 snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
5145                                 strftime(subbuf, 64, subfmt, lt);
5146                                 len = strlen(subbuf); CHECK_SIZE();
5147                                 strncpy2(curpos, subbuf, buflen - total_done);
5148                                 format++;
5149                                 break;
5150                         default:
5151                                 if (format && *format)
5152                                         g_warning("format error (%c)", *format);
5153                                 *curpos = '\0';
5154                                 return total_done;
5155                         }
5156                         curpos += len;
5157                         format++;
5158                 } else {
5159                         int len = 1; CHECK_SIZE();
5160                         *curpos++ = *format++; 
5161                 }
5162         }
5163         *curpos++ = '\0';
5164         return total_done;
5165 }
5166
5167 gboolean prefs_common_get_use_shred(void);
5168
5169
5170 #ifdef G_OS_WIN32
5171 #define WEXITSTATUS(x) (x)
5172 #endif
5173
5174 int claws_unlink(const gchar *filename) 
5175 {
5176         struct stat s;
5177         static int found_shred = -1;
5178         static const gchar *args[4];
5179
5180         if (filename == NULL)
5181                 return 0;
5182
5183         if (prefs_common_get_use_shred()) {
5184                 if (found_shred == -1) {
5185                         /* init */
5186                         args[0] = g_find_program_in_path("shred");
5187                         debug_print("found shred: %s\n", args[0]);
5188                         found_shred = (args[0] != NULL) ? 1:0;
5189                         args[1] = "-f";
5190                         args[3] = NULL;
5191                 }
5192                 if (found_shred == 1) {
5193                         if (g_stat(filename, &s) == 0 && S_ISREG(s.st_mode)) {
5194                                 if (s.st_nlink == 1) {
5195                                         gint status=0;
5196                                         args[2] = filename;
5197                                         g_spawn_sync(NULL, (gchar **)args, NULL, 0,
5198                                          NULL, NULL, NULL, NULL, &status, NULL);
5199                                         debug_print("%s %s exited with status %d\n",
5200                                                 args[0], filename, WEXITSTATUS(status));
5201                                         if (truncate(filename, 0) < 0)
5202                                                 g_warning("couln't truncate");
5203                                 }
5204                         }
5205                 }
5206         }
5207         return g_unlink(filename);
5208 }