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