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