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