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