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