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