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