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