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