2010-02-08 [pawel] 3.7.5cvs8
[claws.git] / src / common / utils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2009 Hiroyuki Yamamoto & The Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27
28 #include <glib/gi18n.h>
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <sys/param.h>
35
36 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
37 #  include <wchar.h>
38 #  include <wctype.h>
39 #endif
40 #include <stdlib.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <stdarg.h>
44 #include <sys/types.h>
45 #if HAVE_SYS_WAIT_H
46 #  include <sys/wait.h>
47 #endif
48 #include <dirent.h>
49 #include <time.h>
50 #include <regex.h>
51
52 #ifdef G_OS_UNIX
53 #include <sys/utsname.h>
54 #endif
55
56 #include <fcntl.h>
57
58 #ifdef G_OS_WIN32
59 #  include <direct.h>
60 #  include <io.h>
61 #  include <w32lib.h>
62 #endif
63
64 #ifdef MAEMO
65 #include <libosso.h>
66 #ifdef CHINOOK
67 # include <tablet-browser-interface.h>
68 #else
69 # include <osso-browser-interface.h>
70 #endif
71 #endif
72
73 #include "utils.h"
74 #include "socket.h"
75 #include "../codeconv.h"
76
77 #define BUFFSIZE        8192
78
79 static gboolean debug_mode = FALSE;
80 #ifdef G_OS_WIN32
81 static GSList *tempfiles=NULL;
82 #endif
83
84 /* Return true if we are running as root.  This function should beused
85    instead of getuid () == 0.  */
86 gboolean superuser_p (void)
87 {
88 #ifdef G_OS_WIN32
89   return w32_is_administrator ();
90 #else
91   return !getuid();
92 #endif  
93 }
94
95
96
97 #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX)
98 gint g_chdir(const gchar *path)
99 {
100 #ifdef G_OS_WIN32
101         if (G_WIN32_HAVE_WIDECHAR_API()) {
102                 wchar_t *wpath;
103                 gint retval;
104                 gint save_errno;
105
106                 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
107                 if (wpath == NULL) {
108                         errno = EINVAL;
109                         return -1;
110                 }
111
112                 retval = _wchdir(wpath);
113                 save_errno = errno;
114
115                 g_free(wpath);
116
117                 errno = save_errno;
118                 return retval;
119         } else {
120                 gchar *cp_path;
121                 gint retval;
122                 gint save_errno;
123
124                 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
125                 if (cp_path == NULL) {
126                         errno = EINVAL;
127                         return -1;
128                 }
129
130                 retval = chdir(cp_path);
131                 save_errno = errno;
132
133                 g_free(cp_path);
134
135                 errno = save_errno;
136                 return retval;
137         }
138 #else
139         return chdir(path);
140 #endif
141 }
142
143 gint g_chmod(const gchar *path, gint mode)
144 {
145 #ifdef G_OS_WIN32
146         if (G_WIN32_HAVE_WIDECHAR_API()) {
147                 wchar_t *wpath;
148                 gint retval;
149                 gint save_errno;
150
151                 wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
152                 if (wpath == NULL) {
153                         errno = EINVAL;
154                         return -1;
155                 }
156
157                 retval = _wchmod(wpath, mode);
158                 save_errno = errno;
159
160                 g_free(wpath);
161
162                 errno = save_errno;
163                 return retval;
164         } else {
165                 gchar *cp_path;
166                 gint retval;
167                 gint save_errno;
168
169                 cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL);
170                 if (cp_path == NULL) {
171                         errno = EINVAL;
172                         return -1;
173                 }
174
175                 retval = chmod(cp_path, mode);
176                 save_errno = errno;
177
178                 g_free(cp_path);
179
180                 errno = save_errno;
181                 return retval;
182         }
183 #else
184         return chmod(path, mode);
185 #endif
186 }
187
188 FILE* g_fopen(const gchar *filename, const gchar *mode)
189 {
190 #ifdef G_OS_WIN32
191         char *name = g_win32_locale_filename_from_utf8(filename);
192         FILE* fp = fopen(name, mode);
193         g_free(name);
194         return fp;
195 #else
196         return fopen(filename, mode);
197 #endif
198 }
199 int g_open(const gchar *filename, int flags, int mode)
200 {
201 #ifdef G_OS_WIN32
202         char *name = g_win32_locale_filename_from_utf8(filename);
203         int fd = open(name, flags, mode);
204         g_free(name);
205         return fp;
206 #else
207         return open(filename, flags, mode);
208 #endif
209 }
210 #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */
211
212
213 #ifdef G_OS_WIN32
214 gint mkstemp_name(gchar *template, gchar **name_used)
215 {
216         static gulong count=0; /* W32-_mktemp only supports up to 27
217                                   tempfiles... */
218         int tmpfd;
219
220         *name_used = g_strdup_printf("%s.%ld",_mktemp(template),count++);
221         tmpfd = g_open (*name_used, (O_CREAT | O_RDWR | O_BINARY),
222                                     (S_IRUSR | S_IWUSR));
223
224         tempfiles=g_slist_append(tempfiles, g_strdup(*name_used));
225         if (tmpfd<0) {
226                 perror(g_strdup_printf("cant create %s",*name_used));
227                 return -1;
228         }
229         else
230                 return tmpfd;
231 }
232 #endif /* G_OS_WIN32 */
233
234 #ifdef G_OS_WIN32
235 gint mkstemp(gchar *template)
236 {
237         gchar *dummyname;
238         gint res = mkstemp_name(template, &dummyname);
239         g_free(dummyname);
240         return res;
241 }
242 #endif /* G_OS_WIN32 */
243
244 void list_free_strings(GList *list)
245 {
246         list = g_list_first(list);
247
248         while (list != NULL) {
249                 g_free(list->data);
250                 list = list->next;
251         }
252 }
253
254 void slist_free_strings(GSList *list)
255 {
256         while (list != NULL) {
257                 g_free(list->data);
258                 list = list->next;
259         }
260 }
261
262 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
263 {
264         g_free(key);
265 }
266
267 void hash_free_strings(GHashTable *table)
268 {
269         g_hash_table_foreach(table, hash_free_strings_func, NULL);
270 }
271
272 gint str_case_equal(gconstpointer v, gconstpointer v2)
273 {
274         return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
275 }
276
277 guint str_case_hash(gconstpointer key)
278 {
279         const gchar *p = key;
280         guint h = *p;
281
282         if (h) {
283                 h = g_ascii_tolower(h);
284                 for (p += 1; *p != '\0'; p++)
285                         h = (h << 5) - h + g_ascii_tolower(*p);
286         }
287
288         return h;
289 }
290
291 void ptr_array_free_strings(GPtrArray *array)
292 {
293         gint i;
294         gchar *str;
295
296         cm_return_if_fail(array != NULL);
297
298         for (i = 0; i < array->len; i++) {
299                 str = g_ptr_array_index(array, i);
300                 g_free(str);
301         }
302 }
303
304 gboolean str_find(const gchar *haystack, const gchar *needle)
305 {
306         return strstr(haystack, needle) != NULL ? TRUE : FALSE;
307 }
308
309 gboolean str_case_find(const gchar *haystack, const gchar *needle)
310 {
311         return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
312 }
313
314 gint to_number(const gchar *nstr)
315 {
316         register const gchar *p;
317
318         if (*nstr == '\0') return -1;
319
320         for (p = nstr; *p != '\0'; p++)
321                 if (!g_ascii_isdigit(*p)) return -1;
322
323         return atoi(nstr);
324 }
325
326 /* convert integer into string,
327    nstr must be not lower than 11 characters length */
328 gchar *itos_buf(gchar *nstr, gint n)
329 {
330         g_snprintf(nstr, 11, "%d", n);
331         return nstr;
332 }
333
334 /* convert integer into string */
335 gchar *itos(gint n)
336 {
337         static gchar nstr[11];
338
339         return itos_buf(nstr, n);
340 }
341
342 #define divide(num,divisor,i,d)         \
343 {                                       \
344         i = num >> divisor;             \
345         d = num & ((1<<divisor)-1);     \
346         d = (d*100) >> divisor;         \
347 }
348
349
350 /*!
351  * \brief Convert a given size in bytes in a human-readable string
352  *
353  * \param size  The size expressed in bytes to convert in string
354  * \return      The string that respresents the size in an human-readable way
355  */
356 gchar *to_human_readable(goffset size)
357 {
358         static gchar str[14];
359         static gchar *b_format = NULL, *kb_format = NULL, 
360                      *mb_format = NULL, *gb_format = NULL;
361         register int t = 0, r = 0;
362         if (b_format == NULL) {
363                 b_format  = _("%dB");
364                 kb_format = _("%d.%02dKB");
365                 mb_format = _("%d.%02dMB");
366                 gb_format = _("%.2fGB");
367         }
368         
369         if (size < (goffset)1024) {
370                 g_snprintf(str, sizeof(str), b_format, (gint)size);
371                 return str;
372         } else if (size >> 10 < (goffset)1024) {
373                 divide(size, 10, t, r);
374                 g_snprintf(str, sizeof(str), kb_format, t, r);
375                 return str;
376         } else if (size >> 20 < (goffset)1024) {
377                 divide(size, 20, t, r);
378                 g_snprintf(str, sizeof(str), mb_format, t, r);
379                 return str;
380         } else {
381                 g_snprintf(str, sizeof(str), gb_format, (gfloat)(size >> 30));
382                 return str;
383         }
384 }
385
386 /* strcmp with NULL-checking */
387 gint strcmp2(const gchar *s1, const gchar *s2)
388 {
389         if (s1 == NULL || s2 == NULL)
390                 return -1;
391         else
392                 return strcmp(s1, s2);
393 }
394 /* strstr with NULL-checking */
395 gchar *strstr2(const gchar *s1, const gchar *s2)
396 {
397         if (s1 == NULL || s2 == NULL)
398                 return NULL;
399         else
400                 return strstr(s1, s2);
401 }
402 /* compare paths */
403 gint path_cmp(const gchar *s1, const gchar *s2)
404 {
405         gint len1, len2;
406         int rc;
407 #ifdef G_OS_WIN32
408         gchar *s1buf, *s2buf;
409 #endif
410
411         if (s1 == NULL || s2 == NULL) return -1;
412         if (*s1 == '\0' || *s2 == '\0') return -1;
413
414 #ifdef G_OS_WIN32
415         s1buf = g_strdup (s1);
416         s2buf = g_strdup (s2);
417         subst_char (s1buf, '/', G_DIR_SEPARATOR);
418         subst_char (s2buf, '/', G_DIR_SEPARATOR);
419         s1 = s1buf;
420         s2 = s2buf;
421 #endif /* !G_OS_WIN32 */
422
423         len1 = strlen(s1);
424         len2 = strlen(s2);
425
426         if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
427         if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
428
429         rc = strncmp(s1, s2, MAX(len1, len2));
430 #ifdef G_OS_WIN32
431         g_free (s1buf);
432         g_free (s2buf);
433 #endif /* !G_OS_WIN32 */
434         return rc;
435 }
436
437 /* remove trailing return code */
438 gchar *strretchomp(gchar *str)
439 {
440         register gchar *s;
441
442         if (!*str) return str;
443
444         for (s = str + strlen(str) - 1;
445              s >= str && (*s == '\n' || *s == '\r');
446              s--)
447                 *s = '\0';
448
449         return str;
450 }
451
452 /* remove trailing character */
453 gchar *strtailchomp(gchar *str, gchar tail_char)
454 {
455         register gchar *s;
456
457         if (!*str) return str;
458         if (tail_char == '\0') return str;
459
460         for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
461                 *s = '\0';
462
463         return str;
464 }
465
466 /* remove CR (carriage return) */
467 gchar *strcrchomp(gchar *str)
468 {
469         register gchar *s;
470
471         if (!*str) return str;
472
473         s = str + strlen(str) - 1;
474         if (*s == '\n' && s > str && *(s - 1) == '\r') {
475                 *(s - 1) = '\n';
476                 *s = '\0';
477         }
478
479         return str;
480 }
481
482 gint file_strip_crs(const gchar *file)
483 {
484         FILE *fp = NULL, *outfp = NULL;
485         gchar buf[4096];
486         gchar *out = get_tmp_file();
487         if (file == NULL)
488                 goto freeout;
489
490         fp = g_fopen(file, "rb");
491         if (!fp)
492                 goto freeout;
493
494         outfp = g_fopen(out, "wb");
495         if (!outfp) {
496                 fclose(fp);
497                 goto freeout;
498         }
499
500         while (fgets(buf, sizeof (buf), fp) != NULL) {
501                 strcrchomp(buf);
502                 if (fputs(buf, outfp) == EOF) {
503                         fclose(fp);
504                         fclose(outfp);
505                         goto unlinkout;
506                 }
507         }
508
509         fclose(fp);
510         if (fclose(outfp) == EOF) {
511                 goto unlinkout;
512         }
513         
514         if (move_file(out, file, TRUE) < 0)
515                 goto unlinkout;
516         
517         g_free(out);
518         return 0;
519 unlinkout:
520         claws_unlink(out);
521 freeout:
522         g_free(out);
523         return -1;
524 }
525
526 /* Similar to `strstr' but this function ignores the case of both strings.  */
527 gchar *strcasestr(const gchar *haystack, const gchar *needle)
528 {
529         register size_t haystack_len, needle_len;
530
531         haystack_len = strlen(haystack);
532         needle_len   = strlen(needle);
533
534         if (haystack_len < needle_len || needle_len == 0)
535                 return NULL;
536
537         while (haystack_len >= needle_len) {
538                 if (!g_ascii_strncasecmp(haystack, needle, needle_len))
539                         return (gchar *)haystack;
540                 else {
541                         haystack++;
542                         haystack_len--;
543                 }
544         }
545
546         return NULL;
547 }
548
549 gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
550                    gconstpointer needle, size_t needlelen)
551 {
552         const gchar *haystack_ = (const gchar *)haystack;
553         const gchar *needle_ = (const gchar *)needle;
554         const gchar *haystack_cur = (const gchar *)haystack;
555         size_t haystack_left = haystacklen;
556
557         if (needlelen == 1)
558                 return memchr(haystack_, *needle_, haystacklen);
559
560         while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left))
561                != NULL) {
562                 if (haystacklen - (haystack_cur - haystack_) < needlelen)
563                         break;
564                 if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
565                         return (gpointer)haystack_cur;
566                 else{
567                         haystack_cur++;
568                         haystack_left = haystacklen - (haystack_cur - haystack_);
569                 }
570         }
571
572         return NULL;
573 }
574
575 /* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
576 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
577 {
578         register const gchar *s = src;
579         register gchar *d = dest;
580
581         while (--n && *s)
582                 *d++ = *s++;
583         *d = '\0';
584
585         return dest;
586 }
587
588
589 /* Examine if next block is non-ASCII string */
590 gboolean is_next_nonascii(const gchar *s)
591 {
592         const gchar *p;
593
594         /* skip head space */
595         for (p = s; *p != '\0' && g_ascii_isspace(*p); p++)
596                 ;
597         for (; *p != '\0' && !g_ascii_isspace(*p); p++) {
598                 if (*(guchar *)p > 127 || *(guchar *)p < 32)
599                         return TRUE;
600         }
601
602         return FALSE;
603 }
604
605 gint get_next_word_len(const gchar *s)
606 {
607         gint len = 0;
608
609         for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++)
610                 ;
611
612         return len;
613 }
614
615 static void trim_subject_for_compare(gchar *str)
616 {
617         gchar *srcp;
618
619         eliminate_parenthesis(str, '[', ']');
620         eliminate_parenthesis(str, '(', ')');
621         g_strstrip(str);
622
623         srcp = str + subject_get_prefix_length(str);
624         if (srcp != str)
625                 memmove(str, srcp, strlen(srcp) + 1);
626 }
627
628 static void trim_subject_for_sort(gchar *str)
629 {
630         gchar *srcp;
631
632         g_strstrip(str);
633
634         srcp = str + subject_get_prefix_length(str);
635         if (srcp != str)
636                 memmove(str, srcp, strlen(srcp) + 1);
637 }
638
639 /* compare subjects */
640 gint subject_compare(const gchar *s1, const gchar *s2)
641 {
642         gchar *str1, *str2;
643
644         if (!s1 || !s2) return -1;
645         if (!*s1 || !*s2) return -1;
646
647         Xstrdup_a(str1, s1, return -1);
648         Xstrdup_a(str2, s2, return -1);
649
650         trim_subject_for_compare(str1);
651         trim_subject_for_compare(str2);
652
653         if (!*str1 || !*str2) return -1;
654
655         return strcmp(str1, str2);
656 }
657
658 gint subject_compare_for_sort(const gchar *s1, const gchar *s2)
659 {
660         gchar *str1, *str2;
661
662         if (!s1 || !s2) return -1;
663
664         Xstrdup_a(str1, s1, return -1);
665         Xstrdup_a(str2, s2, return -1);
666
667         trim_subject_for_sort(str1);
668         trim_subject_for_sort(str2);
669
670         return g_utf8_collate(str1, str2);
671 }
672
673 void trim_subject(gchar *str)
674 {
675         register gchar *srcp;
676         gchar op, cl;
677         gint in_brace;
678
679         g_strstrip(str);
680
681         srcp = str + subject_get_prefix_length(str);
682
683         if (*srcp == '[') {
684                 op = '[';
685                 cl = ']';
686         } else if (*srcp == '(') {
687                 op = '(';
688                 cl = ')';
689         } else
690                 op = 0;
691
692         if (op) {
693                 ++srcp;
694                 in_brace = 1;
695                 while (*srcp) {
696                         if (*srcp == op)
697                                 in_brace++;
698                         else if (*srcp == cl)
699                                 in_brace--;
700                         srcp++;
701                         if (in_brace == 0)
702                                 break;
703                 }
704         }
705         while (g_ascii_isspace(*srcp)) srcp++;
706         memmove(str, srcp, strlen(srcp) + 1);
707 }
708
709 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
710 {
711         register gchar *srcp, *destp;
712         gint in_brace;
713
714         srcp = destp = str;
715
716         while ((destp = strchr(destp, op))) {
717                 in_brace = 1;
718                 srcp = destp + 1;
719                 while (*srcp) {
720                         if (*srcp == op)
721                                 in_brace++;
722                         else if (*srcp == cl)
723                                 in_brace--;
724                         srcp++;
725                         if (in_brace == 0)
726                                 break;
727                 }
728                 while (g_ascii_isspace(*srcp)) srcp++;
729                 memmove(destp, srcp, strlen(srcp) + 1);
730         }
731 }
732
733 void extract_parenthesis(gchar *str, gchar op, gchar cl)
734 {
735         register gchar *srcp, *destp;
736         gint in_brace;
737
738         srcp = destp = str;
739
740         while ((srcp = strchr(destp, op))) {
741                 if (destp > str)
742                         *destp++ = ' ';
743                 memmove(destp, srcp + 1, strlen(srcp));
744                 in_brace = 1;
745                 while(*destp) {
746                         if (*destp == op)
747                                 in_brace++;
748                         else if (*destp == cl)
749                                 in_brace--;
750
751                         if (in_brace == 0)
752                                 break;
753
754                         destp++;
755                 }
756         }
757         *destp = '\0';
758 }
759
760 static void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
761                                          gchar op, gchar cl)
762 {
763         register gchar *srcp, *destp;
764         gint in_brace;
765         gboolean in_quote = FALSE;
766
767         srcp = destp = str;
768
769         while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
770                 if (destp > str)
771                         *destp++ = ' ';
772                 memmove(destp, srcp + 1, strlen(srcp));
773                 in_brace = 1;
774                 while(*destp) {
775                         if (*destp == op && !in_quote)
776                                 in_brace++;
777                         else if (*destp == cl && !in_quote)
778                                 in_brace--;
779                         else if (*destp == quote_chr)
780                                 in_quote ^= TRUE;
781
782                         if (in_brace == 0)
783                                 break;
784
785                         destp++;
786                 }
787         }
788         *destp = '\0';
789 }
790
791 void extract_quote(gchar *str, gchar quote_chr)
792 {
793         register gchar *p;
794
795         if ((str = strchr(str, quote_chr))) {
796                 p = str;
797                 while ((p = strchr(p + 1, quote_chr)) && (p[-1] == '\\')) {
798                         memmove(p - 1, p, strlen(p) + 1);
799                         p--;
800                 }
801                 if(p) {
802                         *p = '\0';
803                         memmove(str, str + 1, p - str);
804                 }
805         }
806 }
807
808 void eliminate_address_comment(gchar *str)
809 {
810         register gchar *srcp, *destp;
811         gint in_brace;
812
813         srcp = destp = str;
814
815         while ((destp = strchr(destp, '"'))) {
816                 if ((srcp = strchr(destp + 1, '"'))) {
817                         srcp++;
818                         if (*srcp == '@') {
819                                 destp = srcp + 1;
820                         } else {
821                                 while (g_ascii_isspace(*srcp)) srcp++;
822                                 memmove(destp, srcp, strlen(srcp) + 1);
823                         }
824                 } else {
825                         *destp = '\0';
826                         break;
827                 }
828         }
829
830         srcp = destp = str;
831
832         while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
833                 in_brace = 1;
834                 srcp = destp + 1;
835                 while (*srcp) {
836                         if (*srcp == '(')
837                                 in_brace++;
838                         else if (*srcp == ')')
839                                 in_brace--;
840                         srcp++;
841                         if (in_brace == 0)
842                                 break;
843                 }
844                 while (g_ascii_isspace(*srcp)) srcp++;
845                 memmove(destp, srcp, strlen(srcp) + 1);
846         }
847 }
848
849 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
850 {
851         gboolean in_quote = FALSE;
852
853         while (*str) {
854                 if (*str == c && !in_quote)
855                         return (gchar *)str;
856                 if (*str == quote_chr)
857                         in_quote ^= TRUE;
858                 str++;
859         }
860
861         return NULL;
862 }
863
864 void extract_address(gchar *str)
865 {
866         eliminate_address_comment(str);
867         if (strchr_with_skip_quote(str, '"', '<'))
868                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
869         g_strstrip(str);
870 }
871
872 void extract_list_id_str(gchar *str)
873 {
874         if (strchr_with_skip_quote(str, '"', '<'))
875                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
876         g_strstrip(str);
877 }
878
879 static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
880 {
881         gchar *work;
882         gchar *workp;
883
884         if (!str) return addr_list;
885
886         Xstrdup_a(work, str, return addr_list);
887
888         if (removecomments)
889                 eliminate_address_comment(work);
890         workp = work;
891
892         while (workp && *workp) {
893                 gchar *p, *next;
894
895                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
896                         *p = '\0';
897                         next = p + 1;
898                 } else
899                         next = NULL;
900
901                 if (removecomments && strchr_with_skip_quote(workp, '"', '<'))
902                         extract_parenthesis_with_skip_quote
903                                 (workp, '"', '<', '>');
904
905                 g_strstrip(workp);
906                 if (*workp)
907                         addr_list = g_slist_append(addr_list, g_strdup(workp));
908
909                 workp = next;
910         }
911
912         return addr_list;
913 }
914
915 GSList *address_list_append(GSList *addr_list, const gchar *str)
916 {
917         return address_list_append_real(addr_list, str, TRUE);
918 }
919
920 GSList *address_list_append_with_comments(GSList *addr_list, const gchar *str)
921 {
922         return address_list_append_real(addr_list, str, FALSE);
923 }
924
925 GSList *references_list_prepend(GSList *msgid_list, const gchar *str)
926 {
927         const gchar *strp;
928
929         if (!str) return msgid_list;
930         strp = str;
931
932         while (strp && *strp) {
933                 const gchar *start, *end;
934                 gchar *msgid;
935
936                 if ((start = strchr(strp, '<')) != NULL) {
937                         end = strchr(start + 1, '>');
938                         if (!end) break;
939                 } else
940                         break;
941
942                 msgid = g_strndup(start + 1, end - start - 1);
943                 g_strstrip(msgid);
944                 if (*msgid)
945                         msgid_list = g_slist_prepend(msgid_list, msgid);
946                 else
947                         g_free(msgid);
948
949                 strp = end + 1;
950         }
951
952         return msgid_list;
953 }
954
955 GSList *references_list_append(GSList *msgid_list, const gchar *str)
956 {
957         GSList *list;
958
959         list = references_list_prepend(NULL, str);
960         list = g_slist_reverse(list);
961         msgid_list = g_slist_concat(msgid_list, list);
962
963         return msgid_list;
964 }
965
966 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
967 {
968         gchar *work;
969         gchar *workp;
970
971         if (!str) return group_list;
972
973         Xstrdup_a(work, str, return group_list);
974
975         workp = work;
976
977         while (workp && *workp) {
978                 gchar *p, *next;
979
980                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
981                         *p = '\0';
982                         next = p + 1;
983                 } else
984                         next = NULL;
985
986                 g_strstrip(workp);
987                 if (*workp)
988                         group_list = g_slist_append(group_list,
989                                                     g_strdup(workp));
990
991                 workp = next;
992         }
993
994         return group_list;
995 }
996
997 GList *add_history(GList *list, const gchar *str)
998 {
999         GList *old;
1000
1001         cm_return_val_if_fail(str != NULL, list);
1002
1003         old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2);
1004         if (old) {
1005                 g_free(old->data);
1006                 list = g_list_remove(list, old->data);
1007         } else if (g_list_length(list) >= MAX_HISTORY_SIZE) {
1008                 GList *last;
1009
1010                 last = g_list_last(list);
1011                 if (last) {
1012                         g_free(last->data);
1013                         list = g_list_remove(list, last->data);
1014                 }
1015         }
1016
1017         list = g_list_prepend(list, g_strdup(str));
1018
1019         return list;
1020 }
1021
1022 void remove_return(gchar *str)
1023 {
1024         register gchar *p = str;
1025
1026         while (*p) {
1027                 if (*p == '\n' || *p == '\r')
1028                         memmove(p, p + 1, strlen(p));
1029                 else
1030                         p++;
1031         }
1032 }
1033
1034 void remove_space(gchar *str)
1035 {
1036         register gchar *p = str;
1037         register gint spc;
1038
1039         while (*p) {
1040                 spc = 0;
1041                 while (g_ascii_isspace(*(p + spc)))
1042                         spc++;
1043                 if (spc)
1044                         memmove(p, p + spc, strlen(p + spc) + 1);
1045                 else
1046                         p++;
1047         }
1048 }
1049
1050 void unfold_line(gchar *str)
1051 {
1052         register gchar *p = str;
1053         register gint spc;
1054
1055         while (*p) {
1056                 if (*p == '\n' || *p == '\r') {
1057                         *p++ = ' ';
1058                         spc = 0;
1059                         while (g_ascii_isspace(*(p + spc)))
1060                                 spc++;
1061                         if (spc)
1062                                 memmove(p, p + spc, strlen(p + spc) + 1);
1063                 } else
1064                         p++;
1065         }
1066 }
1067
1068 void subst_char(gchar *str, gchar orig, gchar subst)
1069 {
1070         register gchar *p = str;
1071
1072         while (*p) {
1073                 if (*p == orig)
1074                         *p = subst;
1075                 p++;
1076         }
1077 }
1078
1079 void subst_chars(gchar *str, gchar *orig, gchar subst)
1080 {
1081         register gchar *p = str;
1082
1083         while (*p) {
1084                 if (strchr(orig, *p) != NULL)
1085                         *p = subst;
1086                 p++;
1087         }
1088 }
1089
1090 void subst_for_filename(gchar *str)
1091 {
1092         if (!str)
1093                 return;
1094 #ifdef G_OS_WIN32
1095         subst_chars(str, "\t\r\n\\/*:", '_');
1096 #else
1097         subst_chars(str, "\t\r\n\\/*", '_');
1098 #endif
1099 }
1100
1101 void subst_for_shellsafe_filename(gchar *str)
1102 {
1103         if (!str)
1104                 return;
1105         subst_for_filename(str);
1106         subst_chars(str, " \"'|&;()<>'!{}[]",'_');
1107 }
1108
1109 gboolean is_ascii_str(const gchar *str)
1110 {
1111         const guchar *p = (const guchar *)str;
1112
1113         while (*p != '\0') {
1114                 if (*p != '\t' && *p != ' ' &&
1115                     *p != '\r' && *p != '\n' &&
1116                     (*p < 32 || *p >= 127))
1117                         return FALSE;
1118                 p++;
1119         }
1120
1121         return TRUE;
1122 }
1123
1124 static const gchar * line_has_quote_char_last(const gchar * str, const gchar *quote_chars)
1125 {
1126         gchar * position = NULL;
1127         gchar * tmp_pos = NULL;
1128         int i;
1129
1130         if (quote_chars == NULL)
1131                 return FALSE;
1132
1133         for (i = 0; i < strlen(quote_chars); i++) {
1134                 tmp_pos = strrchr (str, quote_chars[i]);
1135                 if(position == NULL
1136                    || (tmp_pos != NULL && position <= tmp_pos) )
1137                         position = tmp_pos;
1138         }
1139         return position;
1140 }
1141
1142 gint get_quote_level(const gchar *str, const gchar *quote_chars)
1143 {
1144         const gchar *first_pos;
1145         const gchar *last_pos;
1146         const gchar *p = str;
1147         gint quote_level = -1;
1148
1149         /* speed up line processing by only searching to the last '>' */
1150         if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
1151                 /* skip a line if it contains a '<' before the initial '>' */
1152                 if (memchr(str, '<', first_pos - str) != NULL)
1153                         return -1;
1154                 last_pos = line_has_quote_char_last(first_pos, quote_chars);
1155         } else
1156                 return -1;
1157
1158         while (p <= last_pos) {
1159                 while (p < last_pos) {
1160                         if (g_ascii_isspace(*p))
1161                                 p++;
1162                         else
1163                                 break;
1164                 }
1165
1166                 if (strchr(quote_chars, *p))
1167                         quote_level++;
1168                 else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) {
1169                         /* any characters are allowed except '-','<' and space */
1170                         while (*p != '-' && *p != '<'
1171                                && !strchr(quote_chars, *p)
1172                                && !g_ascii_isspace(*p)
1173                                && p < last_pos)
1174                                 p++;
1175                         if (strchr(quote_chars, *p))
1176                                 quote_level++;
1177                         else
1178                                 break;
1179                 }
1180
1181                 p++;
1182         }
1183
1184         return quote_level;
1185 }
1186
1187 gint check_line_length(const gchar *str, gint max_chars, gint *line)
1188 {
1189         const gchar *p = str, *q;
1190         gint cur_line = 0, len;
1191
1192         while ((q = strchr(p, '\n')) != NULL) {
1193                 len = q - p + 1;
1194                 if (len > max_chars) {
1195                         if (line)
1196                                 *line = cur_line;
1197                         return -1;
1198                 }
1199                 p = q + 1;
1200                 ++cur_line;
1201         }
1202
1203         len = strlen(p);
1204         if (len > max_chars) {
1205                 if (line)
1206                         *line = cur_line;
1207                 return -1;
1208         }
1209
1210         return 0;
1211 }
1212
1213 const gchar * line_has_quote_char(const gchar * str, const gchar *quote_chars)
1214 {
1215         gchar * position = NULL;
1216         gchar * tmp_pos = NULL;
1217         int i;
1218
1219         if (quote_chars == NULL)
1220                 return FALSE;
1221
1222         for (i = 0; i < strlen(quote_chars); i++) {
1223                 tmp_pos = strchr (str,  quote_chars[i]);
1224                 if(position == NULL
1225                    || (tmp_pos != NULL && position >= tmp_pos) )
1226                         position = tmp_pos;
1227         }
1228         return position;
1229 }
1230
1231 static gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1232 {
1233         register guint haystack_len, needle_len;
1234         gboolean in_squote = FALSE, in_dquote = FALSE;
1235
1236         haystack_len = strlen(haystack);
1237         needle_len   = strlen(needle);
1238
1239         if (haystack_len < needle_len || needle_len == 0)
1240                 return NULL;
1241
1242         while (haystack_len >= needle_len) {
1243                 if (!in_squote && !in_dquote &&
1244                     !strncmp(haystack, needle, needle_len))
1245                         return (gchar *)haystack;
1246
1247                 /* 'foo"bar"' -> foo"bar"
1248                    "foo'bar'" -> foo'bar' */
1249                 if (*haystack == '\'') {
1250                         if (in_squote)
1251                                 in_squote = FALSE;
1252                         else if (!in_dquote)
1253                                 in_squote = TRUE;
1254                 } else if (*haystack == '\"') {
1255                         if (in_dquote)
1256                                 in_dquote = FALSE;
1257                         else if (!in_squote)
1258                                 in_dquote = TRUE;
1259                 }
1260
1261                 haystack++;
1262                 haystack_len--;
1263         }
1264
1265         return NULL;
1266 }
1267
1268 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1269                             gint max_tokens)
1270 {
1271         GSList *string_list = NULL, *slist;
1272         gchar **str_array, *s, *new_str;
1273         guint i, n = 1, len;
1274
1275         cm_return_val_if_fail(str != NULL, NULL);
1276         cm_return_val_if_fail(delim != NULL, NULL);
1277
1278         if (max_tokens < 1)
1279                 max_tokens = G_MAXINT;
1280
1281         s = strstr_with_skip_quote(str, delim);
1282         if (s) {
1283                 guint delimiter_len = strlen(delim);
1284
1285                 do {
1286                         len = s - str;
1287                         new_str = g_strndup(str, len);
1288
1289                         if (new_str[0] == '\'' || new_str[0] == '\"') {
1290                                 if (new_str[len - 1] == new_str[0]) {
1291                                         new_str[len - 1] = '\0';
1292                                         memmove(new_str, new_str + 1, len - 1);
1293                                 }
1294                         }
1295                         string_list = g_slist_prepend(string_list, new_str);
1296                         n++;
1297                         str = s + delimiter_len;
1298                         s = strstr_with_skip_quote(str, delim);
1299                 } while (--max_tokens && s);
1300         }
1301
1302         if (*str) {
1303                 new_str = g_strdup(str);
1304                 if (new_str[0] == '\'' || new_str[0] == '\"') {
1305                         len = strlen(str);
1306                         if (new_str[len - 1] == new_str[0]) {
1307                                 new_str[len - 1] = '\0';
1308                                 memmove(new_str, new_str + 1, len - 1);
1309                         }
1310                 }
1311                 string_list = g_slist_prepend(string_list, new_str);
1312                 n++;
1313         }
1314
1315         str_array = g_new(gchar*, n);
1316
1317         i = n - 1;
1318
1319         str_array[i--] = NULL;
1320         for (slist = string_list; slist; slist = slist->next)
1321                 str_array[i--] = slist->data;
1322
1323         g_slist_free(string_list);
1324
1325         return str_array;
1326 }
1327
1328 gchar *get_abbrev_newsgroup_name(const gchar *group, gint len)
1329 {
1330         gchar *abbrev_group;
1331         gchar *ap;
1332         const gchar *p = group;
1333         const gchar *last;
1334
1335         cm_return_val_if_fail(group != NULL, NULL);
1336
1337         last = group + strlen(group);
1338         abbrev_group = ap = g_malloc(strlen(group) + 1);
1339
1340         while (*p) {
1341                 while (*p == '.')
1342                         *ap++ = *p++;
1343                 if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) {
1344                         *ap++ = *p++;
1345                         while (*p != '.') p++;
1346                 } else {
1347                         strcpy(ap, p);
1348                         return abbrev_group;
1349                 }
1350         }
1351
1352         *ap = '\0';
1353         return abbrev_group;
1354 }
1355
1356 gchar *trim_string(const gchar *str, gint len)
1357 {
1358         const gchar *p = str;
1359         gint mb_len;
1360         gchar *new_str;
1361         gint new_len = 0;
1362
1363         if (!str) return NULL;
1364         if (strlen(str) <= len)
1365                 return g_strdup(str);
1366         if (g_utf8_validate(str, -1, NULL) == FALSE)
1367                 return g_strdup(str);
1368
1369         while (*p != '\0') {
1370                 mb_len = g_utf8_skip[*(guchar *)p];
1371                 if (mb_len == 0)
1372                         break;
1373                 else if (new_len + mb_len > len)
1374                         break;
1375
1376                 new_len += mb_len;
1377                 p += mb_len;
1378         }
1379
1380         Xstrndup_a(new_str, str, new_len, return g_strdup(str));
1381         return g_strconcat(new_str, "...", NULL);
1382 }
1383
1384 GList *uri_list_extract_filenames(const gchar *uri_list)
1385 {
1386         GList *result = NULL;
1387         const gchar *p, *q;
1388         gchar *escaped_utf8uri;
1389
1390         p = uri_list;
1391
1392         while (p) {
1393                 if (*p != '#') {
1394                         while (g_ascii_isspace(*p)) p++;
1395                         if (!strncmp(p, "file:", 5)) {
1396                                 q = p;
1397                                 q += 5;
1398                                 while (*q && *q != '\n' && *q != '\r') q++;
1399
1400                                 if (q > p) {
1401                                         gchar *file, *locale_file = NULL;
1402                                         q--;
1403                                         while (q > p && g_ascii_isspace(*q))
1404                                                 q--;
1405                                         Xalloca(escaped_utf8uri, q - p + 2,
1406                                                 return result);
1407                                         Xalloca(file, q - p + 2,
1408                                                 return result);
1409                                         *file = '\0';
1410                                         strncpy(escaped_utf8uri, p, q - p + 1);
1411                                         escaped_utf8uri[q - p + 1] = '\0';
1412                                         decode_uri(file, escaped_utf8uri);
1413                     /*
1414                      * g_filename_from_uri() rejects escaped/locale encoded uri
1415                      * string which come from Nautilus.
1416                      */
1417 #ifndef G_OS_WIN32
1418                                         if (g_utf8_validate(file, -1, NULL))
1419                                                 locale_file
1420                                                         = conv_codeset_strdup(
1421                                                                 file + 5,
1422                                                                 CS_UTF_8,
1423                                                                 conv_get_locale_charset_str());
1424                                         if (!locale_file)
1425                                                 locale_file = g_strdup(file + 5);
1426 #else
1427                                         locale_file = g_filename_from_uri(file, NULL, NULL);
1428 #endif
1429                                         result = g_list_append(result, locale_file);
1430                                 }
1431                         }
1432                 }
1433                 p = strchr(p, '\n');
1434                 if (p) p++;
1435         }
1436
1437         return result;
1438 }
1439
1440 /* Converts two-digit hexadecimal to decimal.  Used for unescaping escaped
1441  * characters
1442  */
1443 static gint axtoi(const gchar *hexstr)
1444 {
1445         gint hi, lo, result;
1446
1447         hi = hexstr[0];
1448         if ('0' <= hi && hi <= '9') {
1449                 hi -= '0';
1450         } else
1451                 if ('a' <= hi && hi <= 'f') {
1452                         hi -= ('a' - 10);
1453                 } else
1454                         if ('A' <= hi && hi <= 'F') {
1455                                 hi -= ('A' - 10);
1456                         }
1457
1458         lo = hexstr[1];
1459         if ('0' <= lo && lo <= '9') {
1460                 lo -= '0';
1461         } else
1462                 if ('a' <= lo && lo <= 'f') {
1463                         lo -= ('a'-10);
1464                 } else
1465                         if ('A' <= lo && lo <= 'F') {
1466                                 lo -= ('A' - 10);
1467                         }
1468         result = lo + (16 * hi);
1469         return result;
1470 }
1471
1472 gboolean is_uri_string(const gchar *str)
1473 {
1474         while (str && *str && g_ascii_isspace(*str))
1475                 str++;
1476         return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
1477                 g_ascii_strncasecmp(str, "https://", 8) == 0 ||
1478                 g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
1479                 g_ascii_strncasecmp(str, "www.", 4) == 0);
1480 }
1481
1482 gchar *get_uri_path(const gchar *uri)
1483 {
1484         while (uri && *uri && g_ascii_isspace(*uri))
1485                 uri++;
1486         if (g_ascii_strncasecmp(uri, "http://", 7) == 0)
1487                 return (gchar *)(uri + 7);
1488         else if (g_ascii_strncasecmp(uri, "https://", 8) == 0)
1489                 return (gchar *)(uri + 8);
1490         else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
1491                 return (gchar *)(uri + 6);
1492         else
1493                 return (gchar *)uri;
1494 }
1495
1496 gint get_uri_len(const gchar *str)
1497 {
1498         const gchar *p;
1499
1500         if (is_uri_string(str)) {
1501                 for (p = str; *p != '\0'; p++) {
1502                         if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
1503                                 break;
1504                 }
1505                 return p - str;
1506         }
1507
1508         return 0;
1509 }
1510
1511 /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
1512  * plusses, and escape characters are used)
1513  */
1514 void decode_uri_with_plus(gchar *decoded_uri, const gchar *encoded_uri, gboolean with_plus)
1515 {
1516         gchar *dec = decoded_uri;
1517         const gchar *enc = encoded_uri;
1518
1519         while (*enc) {
1520                 if (*enc == '%') {
1521                         enc++;
1522                         if (isxdigit((guchar)enc[0]) &&
1523                             isxdigit((guchar)enc[1])) {
1524                                 *dec = axtoi(enc);
1525                                 dec++;
1526                                 enc += 2;
1527                         }
1528                 } else {
1529                         if (with_plus && *enc == '+')
1530                                 *dec = ' ';
1531                         else
1532                                 *dec = *enc;
1533                         dec++;
1534                         enc++;
1535                 }
1536         }
1537
1538         *dec = '\0';
1539 }
1540
1541 void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
1542 {
1543         decode_uri_with_plus(decoded_uri, encoded_uri, TRUE);
1544 }
1545
1546 static gchar *decode_uri_gdup(const gchar *encoded_uri)
1547 {
1548     gchar *buffer = g_malloc(strlen(encoded_uri)+1);
1549     decode_uri_with_plus(buffer, encoded_uri, FALSE);
1550     return buffer;
1551 }
1552
1553 gint scan_mailto_url(const gchar *mailto, gchar **from, gchar **to, gchar **cc, gchar **bcc,
1554                      gchar **subject, gchar **body, gchar ***attach, gchar **inreplyto)
1555 {
1556         gchar *tmp_mailto;
1557         gchar *p;
1558         const gchar *forbidden_uris[] = { ".gnupg/",
1559                                           "/etc/passwd",
1560                                           "/etc/shadow",
1561                                           ".ssh/",
1562                                           "../",
1563                                           NULL };
1564         gint num_attach = 0;
1565         gchar **my_att = NULL;
1566
1567         Xstrdup_a(tmp_mailto, mailto, return -1);
1568
1569         if (!strncmp(tmp_mailto, "mailto:", 7))
1570                 tmp_mailto += 7;
1571
1572         p = strchr(tmp_mailto, '?');
1573         if (p) {
1574                 *p = '\0';
1575                 p++;
1576         }
1577
1578         if (to && !*to)
1579                 *to = decode_uri_gdup(tmp_mailto);
1580
1581         my_att = g_malloc(sizeof(char *));
1582         my_att[0] = NULL;
1583
1584         while (p) {
1585                 gchar *field, *value;
1586
1587                 field = p;
1588
1589                 p = strchr(p, '=');
1590                 if (!p) break;
1591                 *p = '\0';
1592                 p++;
1593
1594                 value = p;
1595
1596                 p = strchr(p, '&');
1597                 if (p) {
1598                         *p = '\0';
1599                         p++;
1600                 }
1601
1602                 if (*value == '\0') continue;
1603
1604                 if (from && !g_ascii_strcasecmp(field, "from")) {
1605                         if (!*from) {
1606                                 *from = decode_uri_gdup(value);
1607                         } else {
1608                                 gchar *tmp = decode_uri_gdup(value);
1609                                 gchar *new_from = g_strdup_printf("%s, %s", *from, tmp);
1610                                 g_free(*from);
1611                                 *from = new_from;
1612                         }
1613                 } else if (cc && !g_ascii_strcasecmp(field, "cc")) {
1614                         if (!*cc) {
1615                                 *cc = decode_uri_gdup(value);
1616                         } else {
1617                                 gchar *tmp = decode_uri_gdup(value);
1618                                 gchar *new_cc = g_strdup_printf("%s, %s", *cc, tmp);
1619                                 g_free(*cc);
1620                                 *cc = new_cc;
1621                         }
1622                 } else if (bcc && !g_ascii_strcasecmp(field, "bcc")) {
1623                         if (!*bcc) {
1624                                 *bcc = decode_uri_gdup(value);
1625                         } else {
1626                                 gchar *tmp = decode_uri_gdup(value);
1627                                 gchar *new_bcc = g_strdup_printf("%s, %s", *bcc, tmp);
1628                                 g_free(*bcc);
1629                                 *bcc = new_bcc;
1630                         }
1631                 } else if (subject && !*subject &&
1632                            !g_ascii_strcasecmp(field, "subject")) {
1633                         *subject = decode_uri_gdup(value);
1634                 } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
1635                         *body = decode_uri_gdup(value);
1636                 } else if (body && !*body && !g_ascii_strcasecmp(field, "insert")) {
1637                         gchar *tmp = decode_uri_gdup(value);
1638                         if (!g_file_get_contents(tmp, body, NULL, NULL)) {
1639                                 g_warning("Error: couldn't set insert file '%s' in body\n", value);
1640                         }
1641                         g_free(tmp);
1642                         tmp = NULL;
1643                 } else if (attach && !g_ascii_strcasecmp(field, "attach")) {
1644                         int i = 0;
1645                         gchar *tmp = decode_uri_gdup(value);
1646                         for (; forbidden_uris[i]; i++) {
1647                                 if (strstr(tmp, forbidden_uris[i])) {
1648                                         g_print("Refusing to attach '%s', potential private data leak\n",
1649                                                         tmp);
1650                                         g_free(tmp);
1651                                         tmp = NULL;
1652                                         break;
1653                                 }
1654                         }
1655                         if (tmp) {
1656                                 /* attach is correct */
1657                                 num_attach++;
1658                                 my_att = g_realloc(my_att, (sizeof(char *))*(num_attach+1));
1659                                 my_att[num_attach-1] = tmp;
1660                                 my_att[num_attach] = NULL;
1661                         }
1662                 } else if (inreplyto && !*inreplyto &&
1663                            !g_ascii_strcasecmp(field, "in-reply-to")) {
1664                         *inreplyto = decode_uri_gdup(value);
1665                 }
1666         }
1667
1668         if (attach)
1669                 *attach = my_att;
1670         return 0;
1671 }
1672
1673
1674 #ifdef G_OS_WIN32
1675 #include <windows.h>
1676 #ifndef CSIDL_APPDATA
1677 #define CSIDL_APPDATA 0x001a
1678 #endif
1679 #ifndef CSIDL_LOCAL_APPDATA
1680 #define CSIDL_LOCAL_APPDATA 0x001c
1681 #endif
1682 #ifndef CSIDL_FLAG_CREATE
1683 #define CSIDL_FLAG_CREATE 0x8000
1684 #endif
1685 #define DIM(v)               (sizeof(v)/sizeof((v)[0]))
1686
1687 #define RTLD_LAZY 0
1688 const char *
1689 w32_strerror (int w32_errno)
1690 {
1691   static char strerr[256];
1692   int ec = (int)GetLastError ();
1693
1694   if (w32_errno == 0)
1695     w32_errno = ec;
1696   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1697                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1698                  strerr, DIM (strerr)-1, NULL);
1699   return strerr;
1700 }
1701
1702 static __inline__ void *
1703 dlopen (const char * name, int flag)
1704 {
1705   void * hd = LoadLibrary (name);
1706   return hd;
1707 }
1708
1709 static __inline__ void *
1710 dlsym (void * hd, const char * sym)
1711 {
1712   if (hd && sym)
1713     {
1714       void * fnc = GetProcAddress (hd, sym);
1715       if (!fnc)
1716         return NULL;
1717       return fnc;
1718     }
1719   return NULL;
1720 }
1721
1722
1723 static __inline__ const char *
1724 dlerror (void)
1725 {
1726   return w32_strerror (0);
1727 }
1728
1729
1730 static __inline__ int
1731 dlclose (void * hd)
1732 {
1733   if (hd)
1734     {
1735       FreeLibrary (hd);
1736       return 0;
1737     }
1738   return -1;
1739 }
1740
1741 static HRESULT
1742 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1743 {
1744   static int initialized;
1745   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1746
1747   if (!initialized)
1748     {
1749       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1750       void *handle;
1751       int i;
1752
1753       initialized = 1;
1754
1755       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1756         {
1757           handle = dlopen (dllnames[i], RTLD_LAZY);
1758           if (handle)
1759             {
1760               func = dlsym (handle, "SHGetFolderPathW");
1761               if (!func)
1762                 {
1763                   dlclose (handle);
1764                   handle = NULL;
1765                 }
1766             }
1767         }
1768     }
1769
1770   if (func)
1771     return func (a,b,c,d,e);
1772   else
1773     return -1;
1774 }
1775
1776 /* Returns a static string with the directroy from which the module
1777    has been loaded.  Returns an empty string on error. */
1778 static char *w32_get_module_dir(void)
1779 {
1780         static char *moddir;
1781
1782         if (!moddir) {
1783                 char name[MAX_PATH+10];
1784                 char *p;
1785
1786                 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1787                         *name = 0;
1788                 else {
1789                         p = strrchr (name, '\\');
1790                         if (p)
1791                                 *p = 0;
1792                         else
1793                                 *name = 0;
1794                 }
1795                 moddir = g_strdup (name);
1796         }
1797         return moddir;
1798 }
1799 #endif /* G_OS_WIN32 */
1800
1801 /* Return a static string with the locale dir. */
1802 const gchar *get_locale_dir(void)
1803 {
1804         static gchar *loc_dir;
1805
1806 #ifdef G_OS_WIN32
1807         if (!loc_dir)
1808                 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1809                                       "\\share\\locale", NULL);
1810 #endif
1811         if (!loc_dir)
1812                 loc_dir = LOCALEDIR;
1813         
1814         return loc_dir;
1815 }
1816
1817
1818 const gchar *get_home_dir(void)
1819 {
1820 #ifdef G_OS_WIN32
1821         static char home_dir_utf16[MAX_PATH] = "";
1822         static gchar *home_dir_utf8 = NULL;
1823         if (home_dir_utf16[0] == '\0') {
1824                 if (w32_shgetfolderpath
1825                             (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1826                              NULL, 0, home_dir_utf16) < 0)
1827                                 strcpy (home_dir_utf16, "C:\\Sylpheed");
1828                 home_dir_utf8 = g_utf16_to_utf8 ((const gunichar *)home_dir_utf16, -1, NULL, NULL, NULL);
1829         }
1830         return home_dir_utf8;
1831 #else
1832         static const gchar *homeenv = NULL;
1833
1834         if (homeenv)
1835                 return homeenv;
1836
1837         if (!homeenv && g_getenv("HOME") != NULL)
1838                 homeenv = g_strdup(g_getenv("HOME"));
1839         if (!homeenv)
1840                 homeenv = g_get_home_dir();
1841
1842         return homeenv;
1843 #endif
1844 }
1845
1846 static gchar *claws_rc_dir = NULL;
1847 static gboolean rc_dir_alt = FALSE;
1848 const gchar *get_rc_dir(void)
1849 {
1850
1851         if (!claws_rc_dir)
1852                 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1853                                      RC_DIR, NULL);
1854
1855         return claws_rc_dir;
1856 }
1857
1858 void set_rc_dir(const gchar *dir)
1859 {
1860         if (claws_rc_dir != NULL) {
1861                 g_print("Error: rc_dir already set\n");
1862         } else {
1863                 rc_dir_alt = TRUE;
1864                 if (g_path_is_absolute(dir))
1865                         claws_rc_dir = g_strdup(dir);
1866                 else {
1867                         claws_rc_dir = g_strconcat(g_get_current_dir(),
1868                                 G_DIR_SEPARATOR_S, dir, NULL);
1869                 }
1870                 debug_print("set rc_dir to %s\n", claws_rc_dir);
1871                 if (!is_dir_exist(claws_rc_dir)) {
1872                         if (make_dir_hier(claws_rc_dir) != 0) {
1873                                 g_print("Error: can't create %s\n",
1874                                 claws_rc_dir);
1875                         }
1876                 }
1877         }
1878 }
1879
1880 gboolean rc_dir_is_alt(void) {
1881         return rc_dir_alt;
1882 }
1883
1884 const gchar *get_mail_base_dir(void)
1885 {
1886         return get_home_dir();
1887 }
1888
1889 #ifdef MAEMO
1890 const gchar *prefs_common_get_data_root(void);
1891 gchar *last_data_root = NULL;
1892 #endif
1893
1894 const gchar *get_news_cache_dir(void)
1895 {
1896         static gchar *news_cache_dir = NULL;
1897 #ifdef MAEMO
1898         const gchar *data_root = prefs_common_get_data_root();
1899         if (strcmp2(data_root, last_data_root)) {
1900                 g_free(news_cache_dir);
1901                 news_cache_dir = NULL;
1902         }
1903 #endif
1904         if (!news_cache_dir)
1905 #ifndef MAEMO
1906                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1907                                              NEWS_CACHE_DIR, NULL);
1908 #else
1909         {
1910                 if (data_root) {
1911                         news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1912                                              "Claws", G_DIR_SEPARATOR_S, 
1913                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1914                                              NEWS_CACHE_DIR, NULL);
1915                         g_free(last_data_root);
1916                         last_data_root = g_strdup(last_data_root);
1917                 } else {
1918                         news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1919                                              NEWS_CACHE_DIR, NULL);
1920                         g_free(last_data_root);
1921                         last_data_root = NULL;
1922                 }
1923         }
1924 #endif
1925         return news_cache_dir;
1926 }
1927
1928 const gchar *get_imap_cache_dir(void)
1929 {
1930         static gchar *imap_cache_dir = NULL;
1931 #ifdef MAEMO
1932         const gchar *data_root = prefs_common_get_data_root();
1933         if (strcmp2(data_root, last_data_root)) {
1934                 g_free(imap_cache_dir);
1935                 imap_cache_dir = NULL;
1936         }
1937 #endif
1938
1939         if (!imap_cache_dir)
1940 #ifndef MAEMO
1941                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1942                                              IMAP_CACHE_DIR, NULL);
1943 #else
1944         {
1945                 if (data_root) {
1946                         imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1947                                              "Claws", G_DIR_SEPARATOR_S, 
1948                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1949                                              IMAP_CACHE_DIR, NULL);
1950                         g_free(last_data_root);
1951                         last_data_root = g_strdup(last_data_root);
1952                 } else {
1953                         imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1954                                              IMAP_CACHE_DIR, NULL);
1955                         g_free(last_data_root);
1956                         last_data_root = NULL;
1957                 }
1958         }
1959 #endif
1960
1961         return imap_cache_dir;
1962 }
1963
1964 const gchar *get_mime_tmp_dir(void)
1965 {
1966         static gchar *mime_tmp_dir = NULL;
1967
1968         if (!mime_tmp_dir)
1969                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1970                                            MIME_TMP_DIR, NULL);
1971
1972         return mime_tmp_dir;
1973 }
1974
1975 const gchar *get_template_dir(void)
1976 {
1977         static gchar *template_dir = NULL;
1978
1979         if (!template_dir)
1980                 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1981                                            TEMPLATE_DIR, NULL);
1982
1983         return template_dir;
1984 }
1985
1986 #ifdef G_OS_WIN32
1987 const gchar *get_cert_file(void)
1988 {
1989         const gchar *cert_file = NULL;
1990         if (!cert_file)
1991                 cert_file = g_strconcat(w32_get_module_dir(),
1992                                  "\\share\\claws-mail\\",
1993                                 "ca-certificates.crt",
1994                                 NULL);  
1995         return cert_file;
1996 }
1997 #endif
1998
1999 /* Return the filepath of the claws-mail.desktop file */
2000 const gchar *get_desktop_file(void)
2001 {
2002 #ifdef DESKTOPFILEPATH
2003   return DESKTOPFILEPATH;
2004 #else
2005   return NULL;
2006 #endif
2007 }
2008
2009 /* Return the default directory for Plugins. */
2010 const gchar *get_plugin_dir(void)
2011 {
2012 #ifdef G_OS_WIN32
2013         static gchar *plugin_dir = NULL;
2014
2015         if (!plugin_dir)
2016                 plugin_dir = g_strconcat(w32_get_module_dir(),
2017                                          "\\lib\\claws-mail\\plugins\\",
2018                                          NULL);
2019         return plugin_dir;
2020 #else
2021         if (is_dir_exist(PLUGINDIR))
2022                 return PLUGINDIR;
2023         else {
2024                 static gchar *plugin_dir = NULL;
2025                 if (!plugin_dir)
2026                         plugin_dir = g_strconcat(get_rc_dir(), 
2027                                 G_DIR_SEPARATOR_S, "plugins", 
2028                                 G_DIR_SEPARATOR_S, NULL);
2029                 return plugin_dir;                      
2030         }
2031 #endif
2032 }
2033
2034
2035 #ifdef G_OS_WIN32
2036 /* Return the default directory for Themes. */
2037 const gchar *get_themes_dir(void)
2038 {
2039         static gchar *themes_dir = NULL;
2040
2041         if (!themes_dir)
2042                 themes_dir = g_strconcat(w32_get_module_dir(),
2043                                          "\\share\\claws-mail\\themes",
2044                                          NULL);
2045         return themes_dir;
2046 }
2047 #endif
2048
2049 const gchar *get_tmp_dir(void)
2050 {
2051         static gchar *tmp_dir = NULL;
2052
2053         if (!tmp_dir)
2054                 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2055                                       TMP_DIR, NULL);
2056
2057         return tmp_dir;
2058 }
2059
2060 gchar *get_tmp_file(void)
2061 {
2062         gchar *tmp_file;
2063         static guint32 id = 0;
2064
2065         tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
2066                                    get_tmp_dir(), G_DIR_SEPARATOR, id++);
2067
2068         return tmp_file;
2069 }
2070
2071 const gchar *get_domain_name(void)
2072 {
2073 #ifdef G_OS_UNIX
2074         static gchar *domain_name = NULL;
2075
2076         if (!domain_name) {
2077                 struct hostent *hp;
2078                 char hostname[256];
2079
2080                 if (gethostname(hostname, sizeof(hostname)) != 0) {
2081                         perror("gethostname");
2082                         domain_name = "unknown";
2083                 } else {
2084                         hostname[sizeof(hostname) - 1] = '\0';
2085                         if ((hp = my_gethostbyname(hostname)) == NULL) {
2086                                 perror("gethostbyname");
2087                                 domain_name = g_strdup(hostname);
2088                         } else {
2089                                 domain_name = g_strdup(hp->h_name);
2090                         }
2091                 }
2092                 debug_print("domain name = %s\n", domain_name);
2093         }
2094
2095         return domain_name;
2096 #else
2097         return "unknown";
2098 #endif
2099 }
2100
2101 off_t get_file_size(const gchar *file)
2102 {
2103         struct stat s;
2104
2105         if (g_stat(file, &s) < 0) {
2106                 FILE_OP_ERROR(file, "stat");
2107                 return -1;
2108         }
2109
2110         return s.st_size;
2111 }
2112
2113 time_t get_file_mtime(const gchar *file)
2114 {
2115         struct stat s;
2116
2117         if (g_stat(file, &s) < 0) {
2118                 FILE_OP_ERROR(file, "stat");
2119                 return -1;
2120         }
2121
2122         return s.st_mtime;
2123 }
2124
2125 off_t get_file_size_as_crlf(const gchar *file)
2126 {
2127         FILE *fp;
2128         off_t size = 0;
2129         gchar buf[BUFFSIZE];
2130
2131         if ((fp = g_fopen(file, "rb")) == NULL) {
2132                 FILE_OP_ERROR(file, "g_fopen");
2133                 return -1;
2134         }
2135
2136         while (fgets(buf, sizeof(buf), fp) != NULL) {
2137                 strretchomp(buf);
2138                 size += strlen(buf) + 2;
2139         }
2140
2141         if (ferror(fp)) {
2142                 FILE_OP_ERROR(file, "fgets");
2143                 size = -1;
2144         }
2145
2146         fclose(fp);
2147
2148         return size;
2149 }
2150
2151 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2152 {
2153         struct stat s;
2154
2155         if (file == NULL)
2156                 return FALSE;
2157
2158         if (g_stat(file, &s) < 0) {
2159                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2160                 return FALSE;
2161         }
2162
2163         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2164                 return TRUE;
2165
2166         return FALSE;
2167 }
2168
2169
2170 /* Test on whether FILE is a relative file name. This is
2171  * straightforward for Unix but more complex for Windows. */
2172 gboolean is_relative_filename(const gchar *file)
2173 {
2174         if (!file)
2175                 return TRUE;
2176 #ifdef G_OS_WIN32
2177         if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2178                 return FALSE; /* Prefixed with a hostname - this can't
2179                                * be a relative name. */
2180
2181         if ( ((*file >= 'a' && *file <= 'z')
2182               || (*file >= 'A' && *file <= 'Z'))
2183              && file[1] == ':')
2184                 file += 2;  /* Skip drive letter. */
2185
2186         return !(*file == '\\' || *file == '/');
2187 #else
2188         return !(*file == G_DIR_SEPARATOR);
2189 #endif
2190 }
2191
2192
2193 gboolean is_dir_exist(const gchar *dir)
2194 {
2195         if (dir == NULL)
2196                 return FALSE;
2197
2198         return g_file_test(dir, G_FILE_TEST_IS_DIR);
2199 }
2200
2201 gboolean is_file_entry_exist(const gchar *file)
2202 {
2203         if (file == NULL)
2204                 return FALSE;
2205
2206         return g_file_test(file, G_FILE_TEST_EXISTS);
2207 }
2208
2209 gboolean dirent_is_regular_file(struct dirent *d)
2210 {
2211 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2212         if (d->d_type == DT_REG)
2213                 return TRUE;
2214         else if (d->d_type != DT_UNKNOWN)
2215                 return FALSE;
2216 #endif
2217
2218         return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2219 }
2220
2221 gint change_dir(const gchar *dir)
2222 {
2223         gchar *prevdir = NULL;
2224
2225         if (debug_mode)
2226                 prevdir = g_get_current_dir();
2227
2228         if (g_chdir(dir) < 0) {
2229                 FILE_OP_ERROR(dir, "chdir");
2230                 if (debug_mode) g_free(prevdir);
2231                 return -1;
2232         } else if (debug_mode) {
2233                 gchar *cwd;
2234
2235                 cwd = g_get_current_dir();
2236                 if (strcmp(prevdir, cwd) != 0)
2237                         g_print("current dir: %s\n", cwd);
2238                 g_free(cwd);
2239                 g_free(prevdir);
2240         }
2241
2242         return 0;
2243 }
2244
2245 gint make_dir(const gchar *dir)
2246 {
2247         if (g_mkdir(dir, S_IRWXU) < 0) {
2248                 FILE_OP_ERROR(dir, "mkdir");
2249                 return -1;
2250         }
2251         if (g_chmod(dir, S_IRWXU) < 0)
2252                 FILE_OP_ERROR(dir, "chmod");
2253
2254         return 0;
2255 }
2256
2257 gint make_dir_hier(const gchar *dir)
2258 {
2259         gchar *parent_dir;
2260         const gchar *p;
2261
2262         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2263                 parent_dir = g_strndup(dir, p - dir);
2264                 if (*parent_dir != '\0') {
2265                         if (!is_dir_exist(parent_dir)) {
2266                                 if (make_dir(parent_dir) < 0) {
2267                                         g_free(parent_dir);
2268                                         return -1;
2269                                 }
2270                         }
2271                 }
2272                 g_free(parent_dir);
2273         }
2274
2275         if (!is_dir_exist(dir)) {
2276                 if (make_dir(dir) < 0)
2277                         return -1;
2278         }
2279
2280         return 0;
2281 }
2282
2283 gint remove_all_files(const gchar *dir)
2284 {
2285         GDir *dp;
2286         const gchar *dir_name;
2287         gchar *prev_dir;
2288
2289         prev_dir = g_get_current_dir();
2290
2291         if (g_chdir(dir) < 0) {
2292                 FILE_OP_ERROR(dir, "chdir");
2293                 g_free(prev_dir);
2294                 return -1;
2295         }
2296
2297         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2298                 g_warning("failed to open directory: %s\n", dir);
2299                 g_free(prev_dir);
2300                 return -1;
2301         }
2302
2303         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2304                 if (claws_unlink(dir_name) < 0)
2305                         FILE_OP_ERROR(dir_name, "unlink");
2306         }
2307
2308         g_dir_close(dp);
2309
2310         if (g_chdir(prev_dir) < 0) {
2311                 FILE_OP_ERROR(prev_dir, "chdir");
2312                 g_free(prev_dir);
2313                 return -1;
2314         }
2315
2316         g_free(prev_dir);
2317
2318         return 0;
2319 }
2320
2321 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2322 {
2323         GDir *dp;
2324         const gchar *dir_name;
2325         gchar *prev_dir;
2326         gint file_no;
2327
2328         prev_dir = g_get_current_dir();
2329
2330         if (g_chdir(dir) < 0) {
2331                 FILE_OP_ERROR(dir, "chdir");
2332                 g_free(prev_dir);
2333                 return -1;
2334         }
2335
2336         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2337                 g_warning("failed to open directory: %s\n", dir);
2338                 g_free(prev_dir);
2339                 return -1;
2340         }
2341
2342         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2343                 file_no = to_number(dir_name);
2344                 if (file_no > 0 && first <= file_no && file_no <= last) {
2345                         if (is_dir_exist(dir_name))
2346                                 continue;
2347                         if (claws_unlink(dir_name) < 0)
2348                                 FILE_OP_ERROR(dir_name, "unlink");
2349                 }
2350         }
2351
2352         g_dir_close(dp);
2353
2354         if (g_chdir(prev_dir) < 0) {
2355                 FILE_OP_ERROR(prev_dir, "chdir");
2356                 g_free(prev_dir);
2357                 return -1;
2358         }
2359
2360         g_free(prev_dir);
2361
2362         return 0;
2363 }
2364
2365 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2366 {
2367         GDir *dp;
2368         const gchar *dir_name;
2369         gchar *prev_dir;
2370         gint file_no;
2371
2372         prev_dir = g_get_current_dir();
2373
2374         if (g_chdir(dir) < 0) {
2375                 FILE_OP_ERROR(dir, "chdir");
2376                 g_free(prev_dir);
2377                 return -1;
2378         }
2379
2380         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2381                 FILE_OP_ERROR(dir, "opendir");
2382                 g_free(prev_dir);
2383                 return -1;
2384         }
2385
2386         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2387                 file_no = to_number(dir_name);
2388                 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2389                         debug_print("removing unwanted file %d from %s\n", file_no, dir);
2390                         if (is_dir_exist(dir_name))
2391                                 continue;
2392                         if (claws_unlink(dir_name) < 0)
2393                                 FILE_OP_ERROR(dir_name, "unlink");
2394                 }
2395         }
2396
2397         g_dir_close(dp);
2398
2399         if (g_chdir(prev_dir) < 0) {
2400                 FILE_OP_ERROR(prev_dir, "chdir");
2401                 g_free(prev_dir);
2402                 return -1;
2403         }
2404
2405         g_free(prev_dir);
2406
2407         return 0;
2408 }
2409
2410 gint remove_all_numbered_files(const gchar *dir)
2411 {
2412         return remove_numbered_files(dir, 0, UINT_MAX);
2413 }
2414
2415 gint remove_dir_recursive(const gchar *dir)
2416 {
2417         struct stat s;
2418         GDir *dp;
2419         const gchar *dir_name;
2420         gchar *prev_dir;
2421
2422         if (g_stat(dir, &s) < 0) {
2423                 FILE_OP_ERROR(dir, "stat");
2424                 if (ENOENT == errno) return 0;
2425                 return -1;
2426         }
2427
2428         if (!S_ISDIR(s.st_mode)) {
2429                 if (claws_unlink(dir) < 0) {
2430                         FILE_OP_ERROR(dir, "unlink");
2431                         return -1;
2432                 }
2433
2434                 return 0;
2435         }
2436
2437         prev_dir = g_get_current_dir();
2438         /* g_print("prev_dir = %s\n", prev_dir); */
2439
2440         if (!path_cmp(prev_dir, dir)) {
2441                 g_free(prev_dir);
2442                 if (g_chdir("..") < 0) {
2443                         FILE_OP_ERROR(dir, "chdir");
2444                         return -1;
2445                 }
2446                 prev_dir = g_get_current_dir();
2447         }
2448
2449         if (g_chdir(dir) < 0) {
2450                 FILE_OP_ERROR(dir, "chdir");
2451                 g_free(prev_dir);
2452                 return -1;
2453         }
2454
2455         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2456                 g_warning("failed to open directory: %s\n", dir);
2457                 g_chdir(prev_dir);
2458                 g_free(prev_dir);
2459                 return -1;
2460         }
2461
2462         /* remove all files in the directory */
2463         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2464                 /* g_print("removing %s\n", dir_name); */
2465
2466                 if (is_dir_exist(dir_name)) {
2467                         if (remove_dir_recursive(dir_name) < 0) {
2468                                 g_warning("can't remove directory\n");
2469                                 return -1;
2470                         }
2471                 } else {
2472                         if (claws_unlink(dir_name) < 0)
2473                                 FILE_OP_ERROR(dir_name, "unlink");
2474                 }
2475         }
2476
2477         g_dir_close(dp);
2478
2479         if (g_chdir(prev_dir) < 0) {
2480                 FILE_OP_ERROR(prev_dir, "chdir");
2481                 g_free(prev_dir);
2482                 return -1;
2483         }
2484
2485         g_free(prev_dir);
2486
2487         if (g_rmdir(dir) < 0) {
2488                 FILE_OP_ERROR(dir, "rmdir");
2489                 return -1;
2490         }
2491
2492         return 0;
2493 }
2494
2495 gint rename_force(const gchar *oldpath, const gchar *newpath)
2496 {
2497 #ifndef G_OS_UNIX
2498         if (!is_file_entry_exist(oldpath)) {
2499                 errno = ENOENT;
2500                 return -1;
2501         }
2502         if (is_file_exist(newpath)) {
2503                 if (claws_unlink(newpath) < 0)
2504                         FILE_OP_ERROR(newpath, "unlink");
2505         }
2506 #endif
2507         return g_rename(oldpath, newpath);
2508 }
2509
2510 /*
2511  * Append src file body to the tail of dest file.
2512  * Now keep_backup has no effects.
2513  */
2514 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2515 {
2516         FILE *src_fp, *dest_fp;
2517         gint n_read;
2518         gchar buf[BUFSIZ];
2519
2520         gboolean err = FALSE;
2521
2522         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2523                 FILE_OP_ERROR(src, "g_fopen");
2524                 return -1;
2525         }
2526
2527         if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2528                 FILE_OP_ERROR(dest, "g_fopen");
2529                 fclose(src_fp);
2530                 return -1;
2531         }
2532
2533         if (change_file_mode_rw(dest_fp, dest) < 0) {
2534                 FILE_OP_ERROR(dest, "chmod");
2535                 g_warning("can't change file mode\n");
2536         }
2537
2538         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2539                 if (n_read < sizeof(buf) && ferror(src_fp))
2540                         break;
2541                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2542                         g_warning("writing to %s failed.\n", dest);
2543                         fclose(dest_fp);
2544                         fclose(src_fp);
2545                         claws_unlink(dest);
2546                         return -1;
2547                 }
2548         }
2549
2550         if (ferror(src_fp)) {
2551                 FILE_OP_ERROR(src, "fread");
2552                 err = TRUE;
2553         }
2554         fclose(src_fp);
2555         if (fclose(dest_fp) == EOF) {
2556                 FILE_OP_ERROR(dest, "fclose");
2557                 err = TRUE;
2558         }
2559
2560         if (err) {
2561                 claws_unlink(dest);
2562                 return -1;
2563         }
2564
2565         return 0;
2566 }
2567
2568 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2569 {
2570         FILE *src_fp, *dest_fp;
2571         gint n_read;
2572         gchar buf[BUFSIZ];
2573         gchar *dest_bak = NULL;
2574         gboolean err = FALSE;
2575
2576         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2577                 FILE_OP_ERROR(src, "g_fopen");
2578                 return -1;
2579         }
2580         if (is_file_exist(dest)) {
2581                 dest_bak = g_strconcat(dest, ".bak", NULL);
2582                 if (rename_force(dest, dest_bak) < 0) {
2583                         FILE_OP_ERROR(dest, "rename");
2584                         fclose(src_fp);
2585                         g_free(dest_bak);
2586                         return -1;
2587                 }
2588         }
2589
2590         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2591                 FILE_OP_ERROR(dest, "g_fopen");
2592                 fclose(src_fp);
2593                 if (dest_bak) {
2594                         if (rename_force(dest_bak, dest) < 0)
2595                                 FILE_OP_ERROR(dest_bak, "rename");
2596                         g_free(dest_bak);
2597                 }
2598                 return -1;
2599         }
2600
2601         if (change_file_mode_rw(dest_fp, dest) < 0) {
2602                 FILE_OP_ERROR(dest, "chmod");
2603                 g_warning("can't change file mode\n");
2604         }
2605
2606         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2607                 if (n_read < sizeof(buf) && ferror(src_fp))
2608                         break;
2609                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2610                         g_warning("writing to %s failed.\n", dest);
2611                         fclose(dest_fp);
2612                         fclose(src_fp);
2613                         claws_unlink(dest);
2614                         if (dest_bak) {
2615                                 if (rename_force(dest_bak, dest) < 0)
2616                                         FILE_OP_ERROR(dest_bak, "rename");
2617                                 g_free(dest_bak);
2618                         }
2619                         return -1;
2620                 }
2621         }
2622
2623         if (ferror(src_fp)) {
2624                 FILE_OP_ERROR(src, "fread");
2625                 err = TRUE;
2626         }
2627         fclose(src_fp);
2628         if (fclose(dest_fp) == EOF) {
2629                 FILE_OP_ERROR(dest, "fclose");
2630                 err = TRUE;
2631         }
2632
2633         if (err) {
2634                 claws_unlink(dest);
2635                 if (dest_bak) {
2636                         if (rename_force(dest_bak, dest) < 0)
2637                                 FILE_OP_ERROR(dest_bak, "rename");
2638                         g_free(dest_bak);
2639                 }
2640                 return -1;
2641         }
2642
2643         if (keep_backup == FALSE && dest_bak)
2644                 claws_unlink(dest_bak);
2645
2646         g_free(dest_bak);
2647
2648         return 0;
2649 }
2650
2651 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2652 {
2653         if (overwrite == FALSE && is_file_exist(dest)) {
2654                 g_warning("move_file(): file %s already exists.", dest);
2655                 return -1;
2656         }
2657
2658         if (rename_force(src, dest) == 0) return 0;
2659
2660         if (EXDEV != errno) {
2661                 FILE_OP_ERROR(src, "rename");
2662                 return -1;
2663         }
2664
2665         if (copy_file(src, dest, FALSE) < 0) return -1;
2666
2667         claws_unlink(src);
2668
2669         return 0;
2670 }
2671
2672 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2673 {
2674         gint n_read;
2675         gint bytes_left, to_read;
2676         gchar buf[BUFSIZ];
2677
2678         if (fseek(fp, offset, SEEK_SET) < 0) {
2679                 perror("fseek");
2680                 return -1;
2681         }
2682
2683         bytes_left = length;
2684         to_read = MIN(bytes_left, sizeof(buf));
2685
2686         while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2687                 if (n_read < to_read && ferror(fp))
2688                         break;
2689                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2690                         return -1;
2691                 }
2692                 bytes_left -= n_read;
2693                 if (bytes_left == 0)
2694                         break;
2695                 to_read = MIN(bytes_left, sizeof(buf));
2696         }
2697
2698         if (ferror(fp)) {
2699                 perror("fread");
2700                 return -1;
2701         }
2702
2703         return 0;
2704 }
2705
2706 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2707 {
2708         FILE *dest_fp;
2709         gboolean err = FALSE;
2710
2711         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2712                 FILE_OP_ERROR(dest, "g_fopen");
2713                 return -1;
2714         }
2715
2716         if (change_file_mode_rw(dest_fp, dest) < 0) {
2717                 FILE_OP_ERROR(dest, "chmod");
2718                 g_warning("can't change file mode\n");
2719         }
2720
2721         if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2722                 err = TRUE;
2723
2724         if (!err && fclose(dest_fp) == EOF) {
2725                 FILE_OP_ERROR(dest, "fclose");
2726                 err = TRUE;
2727         }
2728
2729         if (err) {
2730                 g_warning("writing to %s failed.\n", dest);
2731                 claws_unlink(dest);
2732                 return -1;
2733         }
2734
2735         return 0;
2736 }
2737
2738 /* convert line endings into CRLF. If the last line doesn't end with
2739  * linebreak, add it.
2740  */
2741 gchar *canonicalize_str(const gchar *str)
2742 {
2743         const gchar *p;
2744         guint new_len = 0;
2745         gchar *out, *outp;
2746
2747         for (p = str; *p != '\0'; ++p) {
2748                 if (*p != '\r') {
2749                         ++new_len;
2750                         if (*p == '\n')
2751                                 ++new_len;
2752                 }
2753         }
2754         if (p == str || *(p - 1) != '\n')
2755                 new_len += 2;
2756
2757         out = outp = g_malloc(new_len + 1);
2758         for (p = str; *p != '\0'; ++p) {
2759                 if (*p != '\r') {
2760                         if (*p == '\n')
2761                                 *outp++ = '\r';
2762                         *outp++ = *p;
2763                 }
2764         }
2765         if (p == str || *(p - 1) != '\n') {
2766                 *outp++ = '\r';
2767                 *outp++ = '\n';
2768         }
2769         *outp = '\0';
2770
2771         return out;
2772 }
2773
2774 gint canonicalize_file(const gchar *src, const gchar *dest)
2775 {
2776         FILE *src_fp, *dest_fp;
2777         gchar buf[BUFFSIZE];
2778         gint len;
2779         gboolean err = FALSE;
2780         gboolean last_linebreak = FALSE;
2781
2782         if (src == NULL || dest == NULL)
2783                 return -1;
2784
2785         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2786                 FILE_OP_ERROR(src, "g_fopen");
2787                 return -1;
2788         }
2789
2790         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2791                 FILE_OP_ERROR(dest, "g_fopen");
2792                 fclose(src_fp);
2793                 return -1;
2794         }
2795
2796         if (change_file_mode_rw(dest_fp, dest) < 0) {
2797                 FILE_OP_ERROR(dest, "chmod");
2798                 g_warning("can't change file mode\n");
2799         }
2800
2801         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2802                 gint r = 0;
2803
2804                 len = strlen(buf);
2805                 if (len == 0) break;
2806                 last_linebreak = FALSE;
2807
2808                 if (buf[len - 1] != '\n') {
2809                         last_linebreak = TRUE;
2810                         r = fputs(buf, dest_fp);
2811                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2812                         r = fputs(buf, dest_fp);
2813                 } else {
2814                         if (len > 1) {
2815                                 r = fwrite(buf, 1, len - 1, dest_fp);
2816                                 if (r != (len -1))
2817                                         r = EOF;
2818                         }
2819                         if (r != EOF)
2820                                 r = fputs("\r\n", dest_fp);
2821                 }
2822
2823                 if (r == EOF) {
2824                         g_warning("writing to %s failed.\n", dest);
2825                         fclose(dest_fp);
2826                         fclose(src_fp);
2827                         claws_unlink(dest);
2828                         return -1;
2829                 }
2830         }
2831
2832         if (last_linebreak == TRUE) {
2833                 if (fputs("\r\n", dest_fp) == EOF)
2834                         err = TRUE;
2835         }
2836
2837         if (ferror(src_fp)) {
2838                 FILE_OP_ERROR(src, "fgets");
2839                 err = TRUE;
2840         }
2841         fclose(src_fp);
2842         if (fclose(dest_fp) == EOF) {
2843                 FILE_OP_ERROR(dest, "fclose");
2844                 err = TRUE;
2845         }
2846
2847         if (err) {
2848                 claws_unlink(dest);
2849                 return -1;
2850         }
2851
2852         return 0;
2853 }
2854
2855 gint canonicalize_file_replace(const gchar *file)
2856 {
2857         gchar *tmp_file;
2858
2859         tmp_file = get_tmp_file();
2860
2861         if (canonicalize_file(file, tmp_file) < 0) {
2862                 g_free(tmp_file);
2863                 return -1;
2864         }
2865
2866         if (move_file(tmp_file, file, TRUE) < 0) {
2867                 g_warning("can't replace %s .\n", file);
2868                 claws_unlink(tmp_file);
2869                 g_free(tmp_file);
2870                 return -1;
2871         }
2872
2873         g_free(tmp_file);
2874         return 0;
2875 }
2876
2877 gchar *normalize_newlines(const gchar *str)
2878 {
2879         const gchar *p = str;
2880         gchar *out, *outp;
2881
2882         out = outp = g_malloc(strlen(str) + 1);
2883         for (p = str; *p != '\0'; ++p) {
2884                 if (*p == '\r') {
2885                         if (*(p + 1) != '\n')
2886                                 *outp++ = '\n';
2887                 } else
2888                         *outp++ = *p;
2889         }
2890
2891         *outp = '\0';
2892
2893         return out;
2894 }
2895
2896 gchar *get_outgoing_rfc2822_str(FILE *fp)
2897 {
2898         gchar buf[BUFFSIZE];
2899         GString *str;
2900         gchar *ret;
2901
2902         str = g_string_new(NULL);
2903
2904         /* output header part */
2905         while (fgets(buf, sizeof(buf), fp) != NULL) {
2906                 strretchomp(buf);
2907                 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2908                         gint next;
2909
2910                         for (;;) {
2911                                 next = fgetc(fp);
2912                                 if (next == EOF)
2913                                         break;
2914                                 else if (next != ' ' && next != '\t') {
2915                                         ungetc(next, fp);
2916                                         break;
2917                                 }
2918                                 if (fgets(buf, sizeof(buf), fp) == NULL)
2919                                         break;
2920                         }
2921                 } else {
2922                         g_string_append(str, buf);
2923                         g_string_append(str, "\r\n");
2924                         if (buf[0] == '\0')
2925                                 break;
2926                 }
2927         }
2928
2929         /* output body part */
2930         while (fgets(buf, sizeof(buf), fp) != NULL) {
2931                 strretchomp(buf);
2932                 if (buf[0] == '.')
2933                         g_string_append_c(str, '.');
2934                 g_string_append(str, buf);
2935                 g_string_append(str, "\r\n");
2936         }
2937
2938         ret = str->str;
2939         g_string_free(str, FALSE);
2940
2941         return ret;
2942 }
2943
2944 /*
2945  * Create a new boundary in a way that it is very unlikely that this
2946  * will occur in the following text.  It would be easy to ensure
2947  * uniqueness if everything is either quoted-printable or base64
2948  * encoded (note that conversion is allowed), but because MIME bodies
2949  * may be nested, it may happen that the same boundary has already
2950  * been used.
2951  *
2952  *   boundary := 0*69<bchars> bcharsnospace
2953  *   bchars := bcharsnospace / " "
2954  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2955  *                  "+" / "_" / "," / "-" / "." /
2956  *                  "/" / ":" / "=" / "?"
2957  *
2958  * some special characters removed because of buggy MTAs
2959  */
2960
2961 gchar *generate_mime_boundary(const gchar *prefix)
2962 {
2963         static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2964                              "abcdefghijklmnopqrstuvwxyz"
2965                              "1234567890+_./=";
2966         gchar buf_uniq[24];
2967         gint i;
2968
2969         for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2970                 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2971         buf_uniq[i] = '\0';
2972
2973         return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2974                                buf_uniq);
2975 }
2976
2977 gint change_file_mode_rw(FILE *fp, const gchar *file)
2978 {
2979 #if HAVE_FCHMOD
2980         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2981 #else
2982         return g_chmod(file, S_IRUSR|S_IWUSR);
2983 #endif
2984 }
2985
2986 FILE *my_tmpfile(void)
2987 {
2988 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2989         const gchar suffix[] = ".XXXXXX";
2990         const gchar *tmpdir;
2991         guint tmplen;
2992         const gchar *progname;
2993         guint proglen;
2994         gchar *fname;
2995         gint fd;
2996         FILE *fp;
2997 #ifndef G_OS_WIN32
2998         gchar buf[2]="\0";
2999 #endif
3000
3001         tmpdir = get_tmp_dir();
3002         tmplen = strlen(tmpdir);
3003         progname = g_get_prgname();
3004         if (progname == NULL)
3005                 progname = "claws-mail";
3006         proglen = strlen(progname);
3007         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
3008                 return tmpfile());
3009
3010         memcpy(fname, tmpdir, tmplen);
3011         fname[tmplen] = G_DIR_SEPARATOR;
3012         memcpy(fname + tmplen + 1, progname, proglen);
3013         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
3014
3015         fd = mkstemp(fname);
3016         if (fd < 0)
3017                 return tmpfile();
3018
3019 #ifndef G_OS_WIN32
3020         claws_unlink(fname);
3021         
3022         /* verify that we can write in the file after unlinking */
3023         if (write(fd, buf, 1) < 0) {
3024                 close(fd);
3025                 return tmpfile();
3026         }
3027         
3028 #endif
3029
3030         fp = fdopen(fd, "w+b");
3031         if (!fp)
3032                 close(fd);
3033         else {
3034                 rewind(fp);
3035                 return fp;
3036         }
3037
3038 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
3039
3040         return tmpfile();
3041 }
3042
3043 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
3044 {
3045         int fd;
3046 #ifdef G_OS_WIN32
3047         char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
3048                                           dir, G_DIR_SEPARATOR);
3049         fd = mkstemp_name(template, filename);
3050         g_free(template);
3051 #else
3052         *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
3053         fd = mkstemp(*filename);
3054 #endif
3055         return fdopen(fd, "w+");
3056 }
3057
3058 FILE *str_open_as_stream(const gchar *str)
3059 {
3060         FILE *fp;
3061         size_t len;
3062
3063         cm_return_val_if_fail(str != NULL, NULL);
3064
3065         fp = my_tmpfile();
3066         if (!fp) {
3067                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
3068                 return NULL;
3069         }
3070
3071         len = strlen(str);
3072         if (len == 0) return fp;
3073
3074         if (fwrite(str, 1, len, fp) != len) {
3075                 FILE_OP_ERROR("str_open_as_stream", "fwrite");
3076                 fclose(fp);
3077                 return NULL;
3078         }
3079
3080         rewind(fp);
3081         return fp;
3082 }
3083
3084 gint str_write_to_file(const gchar *str, const gchar *file)
3085 {
3086         FILE *fp;
3087         size_t len;
3088
3089         cm_return_val_if_fail(str != NULL, -1);
3090         cm_return_val_if_fail(file != NULL, -1);
3091
3092         if ((fp = g_fopen(file, "wb")) == NULL) {
3093                 FILE_OP_ERROR(file, "g_fopen");
3094                 return -1;
3095         }
3096
3097         len = strlen(str);
3098         if (len == 0) {
3099                 fclose(fp);
3100                 return 0;
3101         }
3102
3103         if (fwrite(str, 1, len, fp) != len) {
3104                 FILE_OP_ERROR(file, "fwrite");
3105                 fclose(fp);
3106                 claws_unlink(file);
3107                 return -1;
3108         }
3109
3110         if (fclose(fp) == EOF) {
3111                 FILE_OP_ERROR(file, "fclose");
3112                 claws_unlink(file);
3113                 return -1;
3114         }
3115
3116         return 0;
3117 }
3118
3119 static gchar *file_read_stream_to_str_full(FILE *fp, gboolean recode)
3120 {
3121         GByteArray *array;
3122         guchar buf[BUFSIZ];
3123         gint n_read;
3124         gchar *str;
3125
3126         cm_return_val_if_fail(fp != NULL, NULL);
3127
3128         array = g_byte_array_new();
3129
3130         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3131                 if (n_read < sizeof(buf) && ferror(fp))
3132                         break;
3133                 g_byte_array_append(array, buf, n_read);
3134         }
3135
3136         if (ferror(fp)) {
3137                 FILE_OP_ERROR("file stream", "fread");
3138                 g_byte_array_free(array, TRUE);
3139                 return NULL;
3140         }
3141
3142         buf[0] = '\0';
3143         g_byte_array_append(array, buf, 1);
3144         str = (gchar *)array->data;
3145         g_byte_array_free(array, FALSE);
3146
3147         if (recode && !g_utf8_validate(str, -1, NULL)) {
3148                 const gchar *src_codeset, *dest_codeset;
3149                 gchar *tmp = NULL;
3150                 src_codeset = conv_get_locale_charset_str();
3151                 dest_codeset = CS_UTF_8;
3152                 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3153                 g_free(str);
3154                 str = tmp;
3155         }
3156
3157         return str;
3158 }
3159
3160 static gchar *file_read_to_str_full(const gchar *file, gboolean recode)
3161 {
3162         FILE *fp;
3163         gchar *str;
3164         struct stat s;
3165 #ifndef G_OS_WIN32
3166         gint fd, err;
3167         struct timeval timeout = {1, 0};
3168         fd_set fds;
3169         int fflags = 0;
3170 #endif
3171
3172         cm_return_val_if_fail(file != NULL, NULL);
3173
3174         if (g_stat(file, &s) != 0) {
3175                 FILE_OP_ERROR(file, "stat");
3176                 return NULL;
3177         }
3178         if (S_ISDIR(s.st_mode)) {
3179                 g_warning("%s: is a directory\n", file);
3180                 return NULL;
3181         }
3182
3183 #ifdef G_OS_WIN32
3184         fp = g_fopen (file, "rb");
3185         if (fp == NULL) {
3186                 FILE_OP_ERROR(file, "open");
3187                 return NULL;
3188         }
3189 #else     
3190         /* test whether the file is readable without blocking */
3191         fd = g_open(file, O_RDONLY | O_NONBLOCK, 0);
3192         if (fd == -1) {
3193                 FILE_OP_ERROR(file, "open");
3194                 return NULL;
3195         }
3196
3197         FD_ZERO(&fds);
3198         FD_SET(fd, &fds);
3199
3200         /* allow for one second */
3201         err = select(fd+1, &fds, NULL, NULL, &timeout);
3202         if (err <= 0 || !FD_ISSET(fd, &fds)) {
3203                 if (err < 0) {
3204                         FILE_OP_ERROR(file, "select");
3205                 } else {
3206                         g_warning("%s: doesn't seem readable\n", file);
3207                 }
3208                 close(fd);
3209                 return NULL;
3210         }
3211         
3212         /* Now clear O_NONBLOCK */
3213         if ((fflags = fcntl(fd, F_GETFL)) < 0) {
3214                 FILE_OP_ERROR(file, "fcntl (F_GETFL)");
3215                 close(fd);
3216                 return NULL;
3217         }
3218         if (fcntl(fd, F_SETFL, (fflags & ~O_NONBLOCK)) < 0) {
3219                 FILE_OP_ERROR(file, "fcntl (F_SETFL)");
3220                 close(fd);
3221                 return NULL;
3222         }
3223         
3224         /* get the FILE pointer */
3225         fp = fdopen(fd, "rb");
3226
3227         if (fp == NULL) {
3228                 FILE_OP_ERROR(file, "fdopen");
3229                 close(fd); /* if fp isn't NULL, we'll use fclose instead! */
3230                 return NULL;
3231         }
3232 #endif
3233
3234         str = file_read_stream_to_str_full(fp, recode);
3235
3236         fclose(fp);
3237
3238         return str;
3239 }
3240
3241 gchar *file_read_to_str(const gchar *file)
3242 {
3243         return file_read_to_str_full(file, TRUE);
3244 }
3245 gchar *file_read_stream_to_str(FILE *fp)
3246 {
3247         return file_read_stream_to_str_full(fp, TRUE);
3248 }
3249
3250 gchar *file_read_to_str_no_recode(const gchar *file)
3251 {
3252         return file_read_to_str_full(file, FALSE);
3253 }
3254 gchar *file_read_stream_to_str_no_recode(FILE *fp)
3255 {
3256         return file_read_stream_to_str_full(fp, FALSE);
3257 }
3258
3259 char *fgets_crlf(char *buf, int size, FILE *stream)
3260 {
3261         gboolean is_cr = FALSE;
3262         gboolean last_was_cr = FALSE;
3263         int c = 0;
3264         char *cs;
3265
3266         cs = buf;
3267         while (--size > 0 && (c = getc(stream)) != EOF)
3268         {
3269                 *cs++ = c;
3270                 is_cr = (c == '\r');
3271                 if (c == '\n') {
3272                         break;
3273                 }
3274                 if (last_was_cr) {
3275                         *(--cs) = '\n';
3276                         cs++;
3277                         ungetc(c, stream);
3278                         break;
3279                 }
3280                 last_was_cr = is_cr;
3281         }
3282         if (c == EOF && cs == buf)
3283                 return NULL;
3284
3285         *cs = '\0';
3286
3287         return buf;     
3288 }
3289
3290 static gint execute_async(gchar *const argv[])
3291 {
3292         cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3293
3294         if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3295                           NULL, NULL, NULL, FALSE) == FALSE) {
3296                 g_warning("Couldn't execute command: %s\n", argv[0]);
3297                 return -1;
3298         }
3299
3300         return 0;
3301 }
3302
3303 static gint execute_sync(gchar *const argv[])
3304 {
3305         gint status;
3306
3307         cm_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3308
3309 #ifdef G_OS_UNIX
3310         if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3311                          NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3312                 g_warning("Couldn't execute command: %s\n", argv[0]);
3313                 return -1;
3314         }
3315
3316         if (WIFEXITED(status))
3317                 return WEXITSTATUS(status);
3318         else
3319                 return -1;
3320 #else
3321         if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH| 
3322                          G_SPAWN_CHILD_INHERITS_STDIN|G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
3323                          NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3324                 g_warning("Couldn't execute command: %s\n", argv[0]);
3325                 return -1;
3326         }
3327
3328         return status;
3329 #endif
3330 }
3331
3332 gint execute_command_line(const gchar *cmdline, gboolean async)
3333 {
3334         gchar **argv;
3335         gint ret;
3336
3337         debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3338
3339         argv = strsplit_with_quote(cmdline, " ", 0);
3340
3341         if (async)
3342                 ret = execute_async(argv);
3343         else
3344                 ret = execute_sync(argv);
3345
3346         g_strfreev(argv);
3347
3348         return ret;
3349 }
3350
3351 gchar *get_command_output(const gchar *cmdline)
3352 {
3353         gchar *child_stdout;
3354         gint status;
3355
3356         cm_return_val_if_fail(cmdline != NULL, NULL);
3357
3358         debug_print("get_command_output(): executing: %s\n", cmdline);
3359
3360         if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3361                                       NULL) == FALSE) {
3362                 g_warning("Couldn't execute command: %s\n", cmdline);
3363                 return NULL;
3364         }
3365
3366         return child_stdout;
3367 }
3368 #ifndef MAEMO
3369 static gint is_unchanged_uri_char(char c)
3370 {
3371         switch (c) {
3372                 case '(':
3373                 case ')':
3374                         return 0;
3375                 default:
3376                         return 1;
3377         }
3378 }
3379
3380 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3381 {
3382         int i;
3383         int k;
3384
3385         k = 0;
3386         for(i = 0; i < strlen(uri) ; i++) {
3387                 if (is_unchanged_uri_char(uri[i])) {
3388                         if (k + 2 >= bufsize)
3389                                 break;
3390                         encoded_uri[k++] = uri[i];
3391                 }
3392                 else {
3393                         char * hexa = "0123456789ABCDEF";
3394
3395                         if (k + 4 >= bufsize)
3396                                 break;
3397                         encoded_uri[k++] = '%';
3398                         encoded_uri[k++] = hexa[uri[i] / 16];
3399                         encoded_uri[k++] = hexa[uri[i] % 16];
3400                 }
3401         }
3402         encoded_uri[k] = 0;
3403 }
3404 #endif
3405 gint open_uri(const gchar *uri, const gchar *cmdline)
3406 {
3407 #ifndef MAEMO
3408 #ifndef G_OS_WIN32
3409         gchar buf[BUFFSIZE];
3410         gchar *p;
3411         gchar encoded_uri[BUFFSIZE];
3412         cm_return_val_if_fail(uri != NULL, -1);
3413
3414         /* an option to choose whether to use encode_uri or not ? */
3415         encode_uri(encoded_uri, BUFFSIZE, uri);
3416
3417         if (cmdline &&
3418             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3419             !strchr(p + 2, '%'))
3420                 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3421         else {
3422                 if (cmdline)
3423                         g_warning("Open URI command-line is invalid "
3424                                   "(there must be only one '%%s'): %s",
3425                                   cmdline);
3426                 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3427         }
3428
3429         execute_command_line(buf, TRUE);
3430 #else
3431         ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOW);
3432 #endif
3433 #else
3434         extern osso_context_t *get_osso_context(void);
3435         osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3436                                         OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL, 
3437                                         DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3438 #endif
3439         return 0;
3440 }
3441
3442 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3443 {
3444         gchar buf[BUFFSIZE];
3445         gchar *p;
3446
3447         cm_return_val_if_fail(filepath != NULL, -1);
3448
3449         if (cmdline &&
3450             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3451             !strchr(p + 2, '%'))
3452                 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3453         else {
3454                 if (cmdline)
3455                         g_warning("Open Text Editor command-line is invalid "
3456                                   "(there must be only one '%%s'): %s",
3457                                   cmdline);
3458                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3459         }
3460
3461         execute_command_line(buf, TRUE);
3462
3463         return 0;
3464 }
3465
3466 time_t remote_tzoffset_sec(const gchar *zone)
3467 {
3468         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3469         gchar zone3[4];
3470         gchar *p;
3471         gchar c;
3472         gint iustz;
3473         gint offset;
3474         time_t remoteoffset;
3475
3476         strncpy(zone3, zone, 3);
3477         zone3[3] = '\0';
3478         remoteoffset = 0;
3479
3480         if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3481             (c == '+' || c == '-')) {
3482                 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3483                 if (c == '-')
3484                         remoteoffset = -remoteoffset;
3485         } else if (!strncmp(zone, "UT" , 2) ||
3486                    !strncmp(zone, "GMT", 2)) {
3487                 remoteoffset = 0;
3488         } else if (strlen(zone3) == 3) {
3489                 for (p = ustzstr; *p != '\0'; p += 3) {
3490                         if (!g_ascii_strncasecmp(p, zone3, 3)) {
3491                                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3492                                 remoteoffset = iustz * 3600;
3493                                 break;
3494                         }
3495                 }
3496                 if (*p == '\0')
3497                         return -1;
3498         } else if (strlen(zone3) == 1) {
3499                 switch (zone[0]) {
3500                 case 'Z': remoteoffset =   0; break;
3501                 case 'A': remoteoffset =  -1; break;
3502                 case 'B': remoteoffset =  -2; break;
3503                 case 'C': remoteoffset =  -3; break;
3504                 case 'D': remoteoffset =  -4; break;
3505                 case 'E': remoteoffset =  -5; break;
3506                 case 'F': remoteoffset =  -6; break;
3507                 case 'G': remoteoffset =  -7; break;
3508                 case 'H': remoteoffset =  -8; break;
3509                 case 'I': remoteoffset =  -9; break;
3510                 case 'K': remoteoffset = -10; break; /* J is not used */
3511                 case 'L': remoteoffset = -11; break;
3512                 case 'M': remoteoffset = -12; break;
3513                 case 'N': remoteoffset =   1; break;
3514                 case 'O': remoteoffset =   2; break;
3515                 case 'P': remoteoffset =   3; break;
3516                 case 'Q': remoteoffset =   4; break;
3517                 case 'R': remoteoffset =   5; break;
3518                 case 'S': remoteoffset =   6; break;
3519                 case 'T': remoteoffset =   7; break;
3520                 case 'U': remoteoffset =   8; break;
3521                 case 'V': remoteoffset =   9; break;
3522                 case 'W': remoteoffset =  10; break;
3523                 case 'X': remoteoffset =  11; break;
3524                 case 'Y': remoteoffset =  12; break;
3525                 default:  remoteoffset =   0; break;
3526                 }
3527                 remoteoffset = remoteoffset * 3600;
3528         } else
3529                 return -1;
3530
3531         return remoteoffset;
3532 }
3533
3534 time_t tzoffset_sec(time_t *now)
3535 {
3536         struct tm gmt, *lt;
3537         gint off;
3538 #ifndef G_OS_WIN32
3539         struct tm buf1, buf2;
3540 #endif
3541 #ifdef G_OS_WIN32
3542         if (now && *now < 0)
3543                 return 0;
3544 #endif  
3545         gmt = *gmtime_r(now, &buf1);
3546         lt = localtime_r(now, &buf2);
3547
3548         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3549
3550         if (lt->tm_year < gmt.tm_year)
3551                 off -= 24 * 60;
3552         else if (lt->tm_year > gmt.tm_year)
3553                 off += 24 * 60;
3554         else if (lt->tm_yday < gmt.tm_yday)
3555                 off -= 24 * 60;
3556         else if (lt->tm_yday > gmt.tm_yday)
3557                 off += 24 * 60;
3558
3559         if (off >= 24 * 60)             /* should be impossible */
3560                 off = 23 * 60 + 59;     /* if not, insert silly value */
3561         if (off <= -24 * 60)
3562                 off = -(23 * 60 + 59);
3563
3564         return off * 60;
3565 }
3566
3567 /* calculate timezone offset */
3568 gchar *tzoffset(time_t *now)
3569 {
3570         static gchar offset_string[6];
3571         struct tm gmt, *lt;
3572         gint off;
3573         gchar sign = '+';
3574 #ifndef G_OS_WIN32
3575         struct tm buf1, buf2;
3576 #endif
3577 #ifdef G_OS_WIN32
3578         if (now && *now < 0)
3579                 return 0;
3580 #endif
3581         gmt = *gmtime_r(now, &buf1);
3582         lt = localtime_r(now, &buf2);
3583
3584         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3585
3586         if (lt->tm_year < gmt.tm_year)
3587                 off -= 24 * 60;
3588         else if (lt->tm_year > gmt.tm_year)
3589                 off += 24 * 60;
3590         else if (lt->tm_yday < gmt.tm_yday)
3591                 off -= 24 * 60;
3592         else if (lt->tm_yday > gmt.tm_yday)
3593                 off += 24 * 60;
3594
3595         if (off < 0) {
3596                 sign = '-';
3597                 off = -off;
3598         }
3599
3600         if (off >= 24 * 60)             /* should be impossible */
3601                 off = 23 * 60 + 59;     /* if not, insert silly value */
3602
3603         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3604
3605         return offset_string;
3606 }
3607
3608 void get_rfc822_date(gchar *buf, gint len)
3609 {
3610         struct tm *lt;
3611         time_t t;
3612         gchar&n