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