2007-11-16 [wwp] 3.0.2cvs141
[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         if (attach)
1610                 *attach = my_att;
1611         return 0;
1612 }
1613
1614
1615 #ifdef G_OS_WIN32
1616 #include <windows.h>
1617 #ifndef CSIDL_APPDATA
1618 #define CSIDL_APPDATA 0x001a
1619 #endif
1620 #ifndef CSIDL_LOCAL_APPDATA
1621 #define CSIDL_LOCAL_APPDATA 0x001c
1622 #endif
1623 #ifndef CSIDL_FLAG_CREATE
1624 #define CSIDL_FLAG_CREATE 0x8000
1625 #endif
1626 #define DIM(v)               (sizeof(v)/sizeof((v)[0]))
1627
1628 #define RTLD_LAZY 0
1629 const char *
1630 w32_strerror (int w32_errno)
1631 {
1632   static char strerr[256];
1633   int ec = (int)GetLastError ();
1634
1635   if (w32_errno == 0)
1636     w32_errno = ec;
1637   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
1638                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1639                  strerr, DIM (strerr)-1, NULL);
1640   return strerr;
1641 }
1642
1643 static __inline__ void *
1644 dlopen (const char * name, int flag)
1645 {
1646   void * hd = LoadLibrary (name);
1647   return hd;
1648 }
1649
1650 static __inline__ void *
1651 dlsym (void * hd, const char * sym)
1652 {
1653   if (hd && sym)
1654     {
1655       void * fnc = GetProcAddress (hd, sym);
1656       if (!fnc)
1657         return NULL;
1658       return fnc;
1659     }
1660   return NULL;
1661 }
1662
1663
1664 static __inline__ const char *
1665 dlerror (void)
1666 {
1667   return w32_strerror (0);
1668 }
1669
1670
1671 static __inline__ int
1672 dlclose (void * hd)
1673 {
1674   if (hd)
1675     {
1676       FreeLibrary (hd);
1677       return 0;
1678     }
1679   return -1;
1680 }
1681
1682 static HRESULT
1683 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
1684 {
1685   static int initialized;
1686   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
1687
1688   if (!initialized)
1689     {
1690       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
1691       void *handle;
1692       int i;
1693
1694       initialized = 1;
1695
1696       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
1697         {
1698           handle = dlopen (dllnames[i], RTLD_LAZY);
1699           if (handle)
1700             {
1701               func = dlsym (handle, "SHGetFolderPathA");
1702               if (!func)
1703                 {
1704                   dlclose (handle);
1705                   handle = NULL;
1706                 }
1707             }
1708         }
1709     }
1710
1711   if (func)
1712     return func (a,b,c,d,e);
1713   else
1714     return -1;
1715 }
1716
1717 /* Returns a static string with the directroy from which the module
1718    has been loaded.  Returns an empty string on error. */
1719 static char *w32_get_module_dir(void)
1720 {
1721         static char *moddir;
1722
1723         if (!moddir) {
1724                 char name[MAX_PATH+10];
1725                 char *p;
1726
1727                 if ( !GetModuleFileNameA (0, name, sizeof (name)-10) )
1728                         *name = 0;
1729                 else {
1730                         p = strrchr (name, '\\');
1731                         if (p)
1732                                 *p = 0;
1733                         else
1734                                 *name = 0;
1735                 }
1736                 moddir = g_strdup (name);
1737         }
1738         return moddir;
1739 }
1740 #endif /* G_OS_WIN32 */
1741
1742 /* Return a static string with the locale dir. */
1743 const gchar *get_locale_dir(void)
1744 {
1745         static gchar *loc_dir;
1746
1747 #ifdef G_OS_WIN32
1748         if (!loc_dir)
1749                 loc_dir = g_strconcat(w32_get_module_dir(), G_DIR_SEPARATOR_S,
1750                                       "\\share\\locale", NULL);
1751 #endif
1752         if (!loc_dir)
1753                 loc_dir = LOCALEDIR;
1754         
1755         return loc_dir;
1756 }
1757
1758
1759 const gchar *get_home_dir(void)
1760 {
1761 #ifdef G_OS_WIN32
1762         static char home_dir[MAX_PATH] = "";
1763
1764         if (home_dir[0] == '\0') {
1765                 if (w32_shgetfolderpath
1766                             (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
1767                              NULL, 0, home_dir) < 0)
1768                                 strcpy (home_dir, "C:\\Sylpheed");
1769         }
1770         return home_dir;
1771 #else
1772         static const gchar *homeenv = NULL;
1773
1774         if (homeenv)
1775                 return homeenv;
1776
1777         if (!homeenv && g_getenv("HOME") != NULL)
1778                 homeenv = g_strdup(g_getenv("HOME"));
1779         if (!homeenv)
1780                 homeenv = g_get_home_dir();
1781
1782         return homeenv;
1783 #endif
1784 }
1785
1786 static gchar *claws_rc_dir = NULL;
1787 static gboolean rc_dir_alt = FALSE;
1788 const gchar *get_rc_dir(void)
1789 {
1790
1791         if (!claws_rc_dir)
1792                 claws_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1793                                      RC_DIR, NULL);
1794
1795         return claws_rc_dir;
1796 }
1797
1798 void set_rc_dir(const gchar *dir)
1799 {
1800         if (claws_rc_dir != NULL) {
1801                 g_print("Error: rc_dir already set\n");
1802         } else {
1803                 rc_dir_alt = TRUE;
1804                 if (g_path_is_absolute(dir))
1805                         claws_rc_dir = g_strdup(dir);
1806                 else {
1807                         claws_rc_dir = g_strconcat(g_get_current_dir(),
1808                                 G_DIR_SEPARATOR_S, dir, NULL);
1809                 }
1810                 debug_print("set rc_dir to %s\n", claws_rc_dir);
1811                 if (!is_dir_exist(claws_rc_dir)) {
1812                         if (make_dir_hier(claws_rc_dir) != 0) {
1813                                 g_print("Error: can't create %s\n",
1814                                 claws_rc_dir);
1815                         }
1816                 }
1817         }
1818 }
1819
1820 gboolean rc_dir_is_alt(void) {
1821         return rc_dir_alt;
1822 }
1823
1824 const gchar *get_mail_base_dir(void)
1825 {
1826 #ifdef G_OS_WIN32
1827         static gchar *mail_base_dir = NULL;
1828
1829         if (!mail_base_dir)
1830                 mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1831                                             "Mailboxes", NULL);
1832
1833         return mail_base_dir;
1834 #else
1835         return get_home_dir();
1836 #endif
1837 }
1838
1839 #ifdef MAEMO
1840 const gchar *prefs_common_get_data_root(void);
1841 gchar *last_data_root = NULL;
1842 #endif
1843
1844 const gchar *get_news_cache_dir(void)
1845 {
1846         static gchar *news_cache_dir = NULL;
1847 #ifdef MAEMO
1848         const gchar *data_root = prefs_common_get_data_root();
1849         if (strcmp2(data_root, last_data_root)) {
1850                 g_free(news_cache_dir);
1851                 news_cache_dir = NULL;
1852         }
1853 #endif
1854         if (!news_cache_dir)
1855 #ifndef MAEMO
1856                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1857                                              NEWS_CACHE_DIR, NULL);
1858 #else
1859         {
1860                 if (data_root) {
1861                         news_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1862                                              "Claws", G_DIR_SEPARATOR_S, 
1863                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1864                                              NEWS_CACHE_DIR, NULL);
1865                         g_free(last_data_root);
1866                         last_data_root = g_strdup(last_data_root);
1867                 } else {
1868                         news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1869                                              NEWS_CACHE_DIR, NULL);
1870                         g_free(last_data_root);
1871                         last_data_root = NULL;
1872                 }
1873         }
1874 #endif
1875         return news_cache_dir;
1876 }
1877
1878 const gchar *get_imap_cache_dir(void)
1879 {
1880         static gchar *imap_cache_dir = NULL;
1881 #ifdef MAEMO
1882         const gchar *data_root = prefs_common_get_data_root();
1883         if (strcmp2(data_root, last_data_root)) {
1884                 g_free(imap_cache_dir);
1885                 imap_cache_dir = NULL;
1886         }
1887 #endif
1888
1889         if (!imap_cache_dir)
1890 #ifndef MAEMO
1891                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1892                                              IMAP_CACHE_DIR, NULL);
1893 #else
1894         {
1895                 if (data_root) {
1896                         imap_cache_dir = g_strconcat(data_root, G_DIR_SEPARATOR_S,
1897                                              "Claws", G_DIR_SEPARATOR_S, 
1898                                              g_get_user_name(), G_DIR_SEPARATOR_S,
1899                                              IMAP_CACHE_DIR, NULL);
1900                         g_free(last_data_root);
1901                         last_data_root = g_strdup(last_data_root);
1902                 } else {
1903                         imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1904                                              IMAP_CACHE_DIR, NULL);
1905                         g_free(last_data_root);
1906                         last_data_root = NULL;
1907                 }
1908         }
1909 #endif
1910
1911         return imap_cache_dir;
1912 }
1913
1914 const gchar *get_mime_tmp_dir(void)
1915 {
1916         static gchar *mime_tmp_dir = NULL;
1917
1918         if (!mime_tmp_dir)
1919                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1920                                            MIME_TMP_DIR, NULL);
1921
1922         return mime_tmp_dir;
1923 }
1924
1925 const gchar *get_template_dir(void)
1926 {
1927         static gchar *template_dir = NULL;
1928
1929         if (!template_dir)
1930                 template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1931                                            TEMPLATE_DIR, NULL);
1932
1933         return template_dir;
1934 }
1935
1936 /* Return the default directory for Plugins. */
1937 const gchar *get_plugin_dir(void)
1938 {
1939 #ifdef G_OS_WIN32
1940         static gchar *plugin_dir = NULL;
1941
1942         if (!plugin_dir)
1943                 plugin_dir = g_strconcat(w32_get_module_dir(),
1944                                          "\\lib\\claws-mail\\plugins\\",
1945                                          NULL);
1946         return plugin_dir;
1947 #else
1948         if (is_dir_exist(PLUGINDIR))
1949                 return PLUGINDIR;
1950         else {
1951                 static gchar *plugin_dir = NULL;
1952                 if (!plugin_dir)
1953                         plugin_dir = g_strconcat(get_rc_dir(), 
1954                                 G_DIR_SEPARATOR_S, "plugins", 
1955                                 G_DIR_SEPARATOR_S, NULL);
1956                 return plugin_dir;                      
1957         }
1958 #endif
1959 }
1960
1961 const gchar *get_tmp_dir(void)
1962 {
1963         static gchar *tmp_dir = NULL;
1964
1965         if (!tmp_dir)
1966                 tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1967                                       TMP_DIR, NULL);
1968
1969         return tmp_dir;
1970 }
1971
1972 gchar *get_tmp_file(void)
1973 {
1974         gchar *tmp_file;
1975         static guint32 id = 0;
1976
1977         tmp_file = g_strdup_printf("%s%ctmpfile.%08x",
1978                                    get_tmp_dir(), G_DIR_SEPARATOR, id++);
1979
1980         return tmp_file;
1981 }
1982
1983 const gchar *get_domain_name(void)
1984 {
1985 #ifdef G_OS_UNIX
1986         static gchar *domain_name = NULL;
1987
1988         if (!domain_name) {
1989                 struct hostent *hp;
1990                 char hostname[256];
1991
1992                 if (gethostname(hostname, sizeof(hostname)) != 0) {
1993                         perror("gethostname");
1994                         domain_name = "unknown";
1995                 } else {
1996                         hostname[sizeof(hostname) - 1] = '\0';
1997                         if ((hp = my_gethostbyname(hostname)) == NULL) {
1998                                 perror("gethostbyname");
1999                                 domain_name = g_strdup(hostname);
2000                         } else {
2001                                 domain_name = g_strdup(hp->h_name);
2002                         }
2003                 }
2004                 debug_print("domain name = %s\n", domain_name);
2005         }
2006
2007         return domain_name;
2008 #else
2009         return "unknown";
2010 #endif
2011 }
2012
2013 off_t get_file_size(const gchar *file)
2014 {
2015         struct stat s;
2016
2017         if (g_stat(file, &s) < 0) {
2018                 FILE_OP_ERROR(file, "stat");
2019                 return -1;
2020         }
2021
2022         return s.st_size;
2023 }
2024
2025 time_t get_file_mtime(const gchar *file)
2026 {
2027         struct stat s;
2028
2029         if (g_stat(file, &s) < 0) {
2030                 FILE_OP_ERROR(file, "stat");
2031                 return -1;
2032         }
2033
2034         return s.st_mtime;
2035 }
2036
2037 off_t get_file_size_as_crlf(const gchar *file)
2038 {
2039         FILE *fp;
2040         off_t size = 0;
2041         gchar buf[BUFFSIZE];
2042
2043         if ((fp = g_fopen(file, "rb")) == NULL) {
2044                 FILE_OP_ERROR(file, "fopen");
2045                 return -1;
2046         }
2047
2048         while (fgets(buf, sizeof(buf), fp) != NULL) {
2049                 strretchomp(buf);
2050                 size += strlen(buf) + 2;
2051         }
2052
2053         if (ferror(fp)) {
2054                 FILE_OP_ERROR(file, "fgets");
2055                 size = -1;
2056         }
2057
2058         fclose(fp);
2059
2060         return size;
2061 }
2062
2063 gboolean file_exist(const gchar *file, gboolean allow_fifo)
2064 {
2065         struct stat s;
2066
2067         if (file == NULL)
2068                 return FALSE;
2069
2070         if (g_stat(file, &s) < 0) {
2071                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
2072                 return FALSE;
2073         }
2074
2075         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
2076                 return TRUE;
2077
2078         return FALSE;
2079 }
2080
2081
2082 /* Test on whether FILE is a relative file name. This is
2083  * straightforward for Unix but more complex for Windows. */
2084 gboolean is_relative_filename(const gchar *file)
2085 {
2086         if (!file)
2087                 return TRUE;
2088 #ifdef G_OS_WIN32
2089         if ( *file == '\\' && file[1] == '\\' && strchr (file+2, '\\') )
2090                 return FALSE; /* Prefixed with a hostname - this can't
2091                                * be a relative name. */
2092
2093         if ( ((*file >= 'a' && *file <= 'z')
2094               || (*file >= 'A' && *file <= 'Z'))
2095              && file[1] == ':')
2096                 file += 2;  /* Skip drive letter. */
2097
2098         return !(*file == '\\' || *file == '/');
2099 #else
2100         return !(*file == G_DIR_SEPARATOR);
2101 #endif
2102 }
2103
2104
2105 gboolean is_dir_exist(const gchar *dir)
2106 {
2107         if (dir == NULL)
2108                 return FALSE;
2109
2110         return g_file_test(dir, G_FILE_TEST_IS_DIR);
2111 }
2112
2113 gboolean is_file_entry_exist(const gchar *file)
2114 {
2115         if (file == NULL)
2116                 return FALSE;
2117
2118         return g_file_test(file, G_FILE_TEST_EXISTS);
2119 }
2120
2121 gboolean dirent_is_regular_file(struct dirent *d)
2122 {
2123 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
2124         if (d->d_type == DT_REG)
2125                 return TRUE;
2126         else if (d->d_type != DT_UNKNOWN)
2127                 return FALSE;
2128 #endif
2129
2130         return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
2131 }
2132
2133 gint change_dir(const gchar *dir)
2134 {
2135         gchar *prevdir = NULL;
2136
2137         if (debug_mode)
2138                 prevdir = g_get_current_dir();
2139
2140         if (g_chdir(dir) < 0) {
2141                 FILE_OP_ERROR(dir, "chdir");
2142                 if (debug_mode) g_free(prevdir);
2143                 return -1;
2144         } else if (debug_mode) {
2145                 gchar *cwd;
2146
2147                 cwd = g_get_current_dir();
2148                 if (strcmp(prevdir, cwd) != 0)
2149                         g_print("current dir: %s\n", cwd);
2150                 g_free(cwd);
2151                 g_free(prevdir);
2152         }
2153
2154         return 0;
2155 }
2156
2157 gint make_dir(const gchar *dir)
2158 {
2159         if (g_mkdir(dir, S_IRWXU) < 0) {
2160                 FILE_OP_ERROR(dir, "mkdir");
2161                 return -1;
2162         }
2163         if (g_chmod(dir, S_IRWXU) < 0)
2164                 FILE_OP_ERROR(dir, "chmod");
2165
2166         return 0;
2167 }
2168
2169 gint make_dir_hier(const gchar *dir)
2170 {
2171         gchar *parent_dir;
2172         const gchar *p;
2173
2174         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
2175                 parent_dir = g_strndup(dir, p - dir);
2176                 if (*parent_dir != '\0') {
2177                         if (!is_dir_exist(parent_dir)) {
2178                                 if (make_dir(parent_dir) < 0) {
2179                                         g_free(parent_dir);
2180                                         return -1;
2181                                 }
2182                         }
2183                 }
2184                 g_free(parent_dir);
2185         }
2186
2187         if (!is_dir_exist(dir)) {
2188                 if (make_dir(dir) < 0)
2189                         return -1;
2190         }
2191
2192         return 0;
2193 }
2194
2195 gint remove_all_files(const gchar *dir)
2196 {
2197         GDir *dp;
2198         const gchar *dir_name;
2199         gchar *prev_dir;
2200
2201         prev_dir = g_get_current_dir();
2202
2203         if (g_chdir(dir) < 0) {
2204                 FILE_OP_ERROR(dir, "chdir");
2205                 g_free(prev_dir);
2206                 return -1;
2207         }
2208
2209         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2210                 g_warning("failed to open directory: %s\n", dir);
2211                 g_free(prev_dir);
2212                 return -1;
2213         }
2214
2215         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2216                 if (g_unlink(dir_name) < 0)
2217                         FILE_OP_ERROR(dir_name, "unlink");
2218         }
2219
2220         g_dir_close(dp);
2221
2222         if (g_chdir(prev_dir) < 0) {
2223                 FILE_OP_ERROR(prev_dir, "chdir");
2224                 g_free(prev_dir);
2225                 return -1;
2226         }
2227
2228         g_free(prev_dir);
2229
2230         return 0;
2231 }
2232
2233 gint remove_numbered_files(const gchar *dir, guint first, guint last)
2234 {
2235         GDir *dp;
2236         const gchar *dir_name;
2237         gchar *prev_dir;
2238         gint file_no;
2239
2240         prev_dir = g_get_current_dir();
2241
2242         if (g_chdir(dir) < 0) {
2243                 FILE_OP_ERROR(dir, "chdir");
2244                 g_free(prev_dir);
2245                 return -1;
2246         }
2247
2248         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2249                 g_warning("failed to open directory: %s\n", dir);
2250                 g_free(prev_dir);
2251                 return -1;
2252         }
2253
2254         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2255                 file_no = to_number(dir_name);
2256                 if (file_no > 0 && first <= file_no && file_no <= last) {
2257                         if (is_dir_exist(dir_name))
2258                                 continue;
2259                         if (g_unlink(dir_name) < 0)
2260                                 FILE_OP_ERROR(dir_name, "unlink");
2261                 }
2262         }
2263
2264         g_dir_close(dp);
2265
2266         if (g_chdir(prev_dir) < 0) {
2267                 FILE_OP_ERROR(prev_dir, "chdir");
2268                 g_free(prev_dir);
2269                 return -1;
2270         }
2271
2272         g_free(prev_dir);
2273
2274         return 0;
2275 }
2276
2277 gint remove_numbered_files_not_in_list(const gchar *dir, GSList *numberlist)
2278 {
2279         GDir *dp;
2280         const gchar *dir_name;
2281         gchar *prev_dir;
2282         gint file_no;
2283
2284         prev_dir = g_get_current_dir();
2285
2286         if (g_chdir(dir) < 0) {
2287                 FILE_OP_ERROR(dir, "chdir");
2288                 g_free(prev_dir);
2289                 return -1;
2290         }
2291
2292         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2293                 FILE_OP_ERROR(dir, "opendir");
2294                 g_free(prev_dir);
2295                 return -1;
2296         }
2297
2298         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2299                 file_no = to_number(dir_name);
2300                 if (file_no > 0 && (g_slist_find(numberlist, GINT_TO_POINTER(file_no)) == NULL)) {
2301                         debug_print("removing unwanted file %d from %s\n", file_no, dir);
2302                         if (is_dir_exist(dir_name))
2303                                 continue;
2304                         if (g_unlink(dir_name) < 0)
2305                                 FILE_OP_ERROR(dir_name, "unlink");
2306                 }
2307         }
2308
2309         g_dir_close(dp);
2310
2311         if (g_chdir(prev_dir) < 0) {
2312                 FILE_OP_ERROR(prev_dir, "chdir");
2313                 g_free(prev_dir);
2314                 return -1;
2315         }
2316
2317         g_free(prev_dir);
2318
2319         return 0;
2320 }
2321
2322 gint remove_all_numbered_files(const gchar *dir)
2323 {
2324         return remove_numbered_files(dir, 0, UINT_MAX);
2325 }
2326
2327 gint remove_dir_recursive(const gchar *dir)
2328 {
2329         struct stat s;
2330         GDir *dp;
2331         const gchar *dir_name;
2332         gchar *prev_dir;
2333
2334         if (g_stat(dir, &s) < 0) {
2335                 FILE_OP_ERROR(dir, "stat");
2336                 if (ENOENT == errno) return 0;
2337                 return -1;
2338         }
2339
2340         if (!S_ISDIR(s.st_mode)) {
2341                 if (g_unlink(dir) < 0) {
2342                         FILE_OP_ERROR(dir, "unlink");
2343                         return -1;
2344                 }
2345
2346                 return 0;
2347         }
2348
2349         prev_dir = g_get_current_dir();
2350         /* g_print("prev_dir = %s\n", prev_dir); */
2351
2352         if (!path_cmp(prev_dir, dir)) {
2353                 g_free(prev_dir);
2354                 if (g_chdir("..") < 0) {
2355                         FILE_OP_ERROR(dir, "chdir");
2356                         return -1;
2357                 }
2358                 prev_dir = g_get_current_dir();
2359         }
2360
2361         if (g_chdir(dir) < 0) {
2362                 FILE_OP_ERROR(dir, "chdir");
2363                 g_free(prev_dir);
2364                 return -1;
2365         }
2366
2367         if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
2368                 g_warning("failed to open directory: %s\n", dir);
2369                 g_chdir(prev_dir);
2370                 g_free(prev_dir);
2371                 return -1;
2372         }
2373
2374         /* remove all files in the directory */
2375         while ((dir_name = g_dir_read_name(dp)) != NULL) {
2376                 /* g_print("removing %s\n", dir_name); */
2377
2378                 if (is_dir_exist(dir_name)) {
2379                         if (remove_dir_recursive(dir_name) < 0) {
2380                                 g_warning("can't remove directory\n");
2381                                 return -1;
2382                         }
2383                 } else {
2384                         if (g_unlink(dir_name) < 0)
2385                                 FILE_OP_ERROR(dir_name, "unlink");
2386                 }
2387         }
2388
2389         g_dir_close(dp);
2390
2391         if (g_chdir(prev_dir) < 0) {
2392                 FILE_OP_ERROR(prev_dir, "chdir");
2393                 g_free(prev_dir);
2394                 return -1;
2395         }
2396
2397         g_free(prev_dir);
2398
2399         if (g_rmdir(dir) < 0) {
2400                 FILE_OP_ERROR(dir, "rmdir");
2401                 return -1;
2402         }
2403
2404         return 0;
2405 }
2406
2407 gint rename_force(const gchar *oldpath, const gchar *newpath)
2408 {
2409 #ifndef G_OS_UNIX
2410         if (!is_file_entry_exist(oldpath)) {
2411                 errno = ENOENT;
2412                 return -1;
2413         }
2414         if (is_file_exist(newpath)) {
2415                 if (g_unlink(newpath) < 0)
2416                         FILE_OP_ERROR(newpath, "unlink");
2417         }
2418 #endif
2419         return g_rename(oldpath, newpath);
2420 }
2421
2422 /*
2423  * Append src file body to the tail of dest file.
2424  * Now keep_backup has no effects.
2425  */
2426 gint append_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2427 {
2428         FILE *src_fp, *dest_fp;
2429         gint n_read;
2430         gchar buf[BUFSIZ];
2431
2432         gboolean err = FALSE;
2433
2434         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2435                 FILE_OP_ERROR(src, "fopen");
2436                 return -1;
2437         }
2438
2439         if ((dest_fp = g_fopen(dest, "ab")) == NULL) {
2440                 FILE_OP_ERROR(dest, "fopen");
2441                 fclose(src_fp);
2442                 return -1;
2443         }
2444
2445         if (change_file_mode_rw(dest_fp, dest) < 0) {
2446                 FILE_OP_ERROR(dest, "chmod");
2447                 g_warning("can't change file mode\n");
2448         }
2449
2450         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2451                 if (n_read < sizeof(buf) && ferror(src_fp))
2452                         break;
2453                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2454                         g_warning("writing to %s failed.\n", dest);
2455                         fclose(dest_fp);
2456                         fclose(src_fp);
2457                         g_unlink(dest);
2458                         return -1;
2459                 }
2460         }
2461
2462         if (ferror(src_fp)) {
2463                 FILE_OP_ERROR(src, "fread");
2464                 err = TRUE;
2465         }
2466         fclose(src_fp);
2467         if (fclose(dest_fp) == EOF) {
2468                 FILE_OP_ERROR(dest, "fclose");
2469                 err = TRUE;
2470         }
2471
2472         if (err) {
2473                 g_unlink(dest);
2474                 return -1;
2475         }
2476
2477         return 0;
2478 }
2479
2480 gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup)
2481 {
2482         FILE *src_fp, *dest_fp;
2483         gint n_read;
2484         gchar buf[BUFSIZ];
2485         gchar *dest_bak = NULL;
2486         gboolean err = FALSE;
2487
2488         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2489                 FILE_OP_ERROR(src, "fopen");
2490                 return -1;
2491         }
2492         if (is_file_exist(dest)) {
2493                 dest_bak = g_strconcat(dest, ".bak", NULL);
2494                 if (rename_force(dest, dest_bak) < 0) {
2495                         FILE_OP_ERROR(dest, "rename");
2496                         fclose(src_fp);
2497                         g_free(dest_bak);
2498                         return -1;
2499                 }
2500         }
2501
2502         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2503                 FILE_OP_ERROR(dest, "fopen");
2504                 fclose(src_fp);
2505                 if (dest_bak) {
2506                         if (rename_force(dest_bak, dest) < 0)
2507                                 FILE_OP_ERROR(dest_bak, "rename");
2508                         g_free(dest_bak);
2509                 }
2510                 return -1;
2511         }
2512
2513         if (change_file_mode_rw(dest_fp, dest) < 0) {
2514                 FILE_OP_ERROR(dest, "chmod");
2515                 g_warning("can't change file mode\n");
2516         }
2517
2518         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
2519                 if (n_read < sizeof(buf) && ferror(src_fp))
2520                         break;
2521                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2522                         g_warning("writing to %s failed.\n", dest);
2523                         fclose(dest_fp);
2524                         fclose(src_fp);
2525                         g_unlink(dest);
2526                         if (dest_bak) {
2527                                 if (rename_force(dest_bak, dest) < 0)
2528                                         FILE_OP_ERROR(dest_bak, "rename");
2529                                 g_free(dest_bak);
2530                         }
2531                         return -1;
2532                 }
2533         }
2534
2535         if (ferror(src_fp)) {
2536                 FILE_OP_ERROR(src, "fread");
2537                 err = TRUE;
2538         }
2539         fclose(src_fp);
2540         if (fclose(dest_fp) == EOF) {
2541                 FILE_OP_ERROR(dest, "fclose");
2542                 err = TRUE;
2543         }
2544
2545         if (err) {
2546                 g_unlink(dest);
2547                 if (dest_bak) {
2548                         if (rename_force(dest_bak, dest) < 0)
2549                                 FILE_OP_ERROR(dest_bak, "rename");
2550                         g_free(dest_bak);
2551                 }
2552                 return -1;
2553         }
2554
2555         if (keep_backup == FALSE && dest_bak)
2556                 g_unlink(dest_bak);
2557
2558         g_free(dest_bak);
2559
2560         return 0;
2561 }
2562
2563 gint move_file(const gchar *src, const gchar *dest, gboolean overwrite)
2564 {
2565         if (overwrite == FALSE && is_file_exist(dest)) {
2566                 g_warning("move_file(): file %s already exists.", dest);
2567                 return -1;
2568         }
2569
2570         if (rename_force(src, dest) == 0) return 0;
2571
2572         if (EXDEV != errno) {
2573                 FILE_OP_ERROR(src, "rename");
2574                 return -1;
2575         }
2576
2577         if (copy_file(src, dest, FALSE) < 0) return -1;
2578
2579         g_unlink(src);
2580
2581         return 0;
2582 }
2583
2584 gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
2585 {
2586         gint n_read;
2587         gint bytes_left, to_read;
2588         gchar buf[BUFSIZ];
2589
2590         if (fseek(fp, offset, SEEK_SET) < 0) {
2591                 perror("fseek");
2592                 return -1;
2593         }
2594
2595         bytes_left = length;
2596         to_read = MIN(bytes_left, sizeof(buf));
2597
2598         while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) {
2599                 if (n_read < to_read && ferror(fp))
2600                         break;
2601                 if (fwrite(buf, 1, n_read, dest_fp) < n_read) {
2602                         return -1;
2603                 }
2604                 bytes_left -= n_read;
2605                 if (bytes_left == 0)
2606                         break;
2607                 to_read = MIN(bytes_left, sizeof(buf));
2608         }
2609
2610         if (ferror(fp)) {
2611                 perror("fread");
2612                 return -1;
2613         }
2614
2615         return 0;
2616 }
2617
2618 gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
2619 {
2620         FILE *dest_fp;
2621         gboolean err = FALSE;
2622
2623         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2624                 FILE_OP_ERROR(dest, "fopen");
2625                 return -1;
2626         }
2627
2628         if (change_file_mode_rw(dest_fp, dest) < 0) {
2629                 FILE_OP_ERROR(dest, "chmod");
2630                 g_warning("can't change file mode\n");
2631         }
2632
2633         if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
2634                 err = TRUE;
2635
2636         if (!err && fclose(dest_fp) == EOF) {
2637                 FILE_OP_ERROR(dest, "fclose");
2638                 err = TRUE;
2639         }
2640
2641         if (err) {
2642                 g_warning("writing to %s failed.\n", dest);
2643                 g_unlink(dest);
2644                 return -1;
2645         }
2646
2647         return 0;
2648 }
2649
2650 /* convert line endings into CRLF. If the last line doesn't end with
2651  * linebreak, add it.
2652  */
2653 gchar *canonicalize_str(const gchar *str)
2654 {
2655         const gchar *p;
2656         guint new_len = 0;
2657         gchar *out, *outp;
2658
2659         for (p = str; *p != '\0'; ++p) {
2660                 if (*p != '\r') {
2661                         ++new_len;
2662                         if (*p == '\n')
2663                                 ++new_len;
2664                 }
2665         }
2666         if (p == str || *(p - 1) != '\n')
2667                 new_len += 2;
2668
2669         out = outp = g_malloc(new_len + 1);
2670         for (p = str; *p != '\0'; ++p) {
2671                 if (*p != '\r') {
2672                         if (*p == '\n')
2673                                 *outp++ = '\r';
2674                         *outp++ = *p;
2675                 }
2676         }
2677         if (p == str || *(p - 1) != '\n') {
2678                 *outp++ = '\r';
2679                 *outp++ = '\n';
2680         }
2681         *outp = '\0';
2682
2683         return out;
2684 }
2685
2686 gint canonicalize_file(const gchar *src, const gchar *dest)
2687 {
2688         FILE *src_fp, *dest_fp;
2689         gchar buf[BUFFSIZE];
2690         gint len;
2691         gboolean err = FALSE;
2692         gboolean last_linebreak = FALSE;
2693
2694         if ((src_fp = g_fopen(src, "rb")) == NULL) {
2695                 FILE_OP_ERROR(src, "fopen");
2696                 return -1;
2697         }
2698
2699         if ((dest_fp = g_fopen(dest, "wb")) == NULL) {
2700                 FILE_OP_ERROR(dest, "fopen");
2701                 fclose(src_fp);
2702                 return -1;
2703         }
2704
2705         if (change_file_mode_rw(dest_fp, dest) < 0) {
2706                 FILE_OP_ERROR(dest, "chmod");
2707                 g_warning("can't change file mode\n");
2708         }
2709
2710         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
2711                 gint r = 0;
2712
2713                 len = strlen(buf);
2714                 if (len == 0) break;
2715                 last_linebreak = FALSE;
2716
2717                 if (buf[len - 1] != '\n') {
2718                         last_linebreak = TRUE;
2719                         r = fputs(buf, dest_fp);
2720                 } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') {
2721                         r = fputs(buf, dest_fp);
2722                 } else {
2723                         if (len > 1) {
2724                                 r = fwrite(buf, 1, len - 1, dest_fp);
2725                                 if (r != (len -1))
2726                                         r = EOF;
2727                         }
2728                         if (r != EOF)
2729                                 r = fputs("\r\n", dest_fp);
2730                 }
2731
2732                 if (r == EOF) {
2733                         g_warning("writing to %s failed.\n", dest);
2734                         fclose(dest_fp);
2735                         fclose(src_fp);
2736                         g_unlink(dest);
2737                         return -1;
2738                 }
2739         }
2740
2741         if (last_linebreak == TRUE) {
2742                 if (fputs("\r\n", dest_fp) == EOF)
2743                         err = TRUE;
2744         }
2745
2746         if (ferror(src_fp)) {
2747                 FILE_OP_ERROR(src, "fgets");
2748                 err = TRUE;
2749         }
2750         fclose(src_fp);
2751         if (fclose(dest_fp) == EOF) {
2752                 FILE_OP_ERROR(dest, "fclose");
2753                 err = TRUE;
2754         }
2755
2756         if (err) {
2757                 g_unlink(dest);
2758                 return -1;
2759         }
2760
2761         return 0;
2762 }
2763
2764 gint canonicalize_file_replace(const gchar *file)
2765 {
2766         gchar *tmp_file;
2767
2768         tmp_file = get_tmp_file();
2769
2770         if (canonicalize_file(file, tmp_file) < 0) {
2771                 g_free(tmp_file);
2772                 return -1;
2773         }
2774
2775         if (move_file(tmp_file, file, TRUE) < 0) {
2776                 g_warning("can't replace %s .\n", file);
2777                 g_unlink(tmp_file);
2778                 g_free(tmp_file);
2779                 return -1;
2780         }
2781
2782         g_free(tmp_file);
2783         return 0;
2784 }
2785
2786 gchar *normalize_newlines(const gchar *str)
2787 {
2788         const gchar *p = str;
2789         gchar *out, *outp;
2790
2791         out = outp = g_malloc(strlen(str) + 1);
2792         for (p = str; *p != '\0'; ++p) {
2793                 if (*p == '\r') {
2794                         if (*(p + 1) != '\n')
2795                                 *outp++ = '\n';
2796                 } else
2797                         *outp++ = *p;
2798         }
2799
2800         *outp = '\0';
2801
2802         return out;
2803 }
2804
2805 gchar *get_outgoing_rfc2822_str(FILE *fp)
2806 {
2807         gchar buf[BUFFSIZE];
2808         GString *str;
2809         gchar *ret;
2810
2811         str = g_string_new(NULL);
2812
2813         /* output header part */
2814         while (fgets(buf, sizeof(buf), fp) != NULL) {
2815                 strretchomp(buf);
2816                 if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) {
2817                         gint next;
2818
2819                         for (;;) {
2820                                 next = fgetc(fp);
2821                                 if (next == EOF)
2822                                         break;
2823                                 else if (next != ' ' && next != '\t') {
2824                                         ungetc(next, fp);
2825                                         break;
2826                                 }
2827                                 if (fgets(buf, sizeof(buf), fp) == NULL)
2828                                         break;
2829                         }
2830                 } else {
2831                         g_string_append(str, buf);
2832                         g_string_append(str, "\r\n");
2833                         if (buf[0] == '\0')
2834                                 break;
2835                 }
2836         }
2837
2838         /* output body part */
2839         while (fgets(buf, sizeof(buf), fp) != NULL) {
2840                 strretchomp(buf);
2841                 if (buf[0] == '.')
2842                         g_string_append_c(str, '.');
2843                 g_string_append(str, buf);
2844                 g_string_append(str, "\r\n");
2845         }
2846
2847         ret = str->str;
2848         g_string_free(str, FALSE);
2849
2850         return ret;
2851 }
2852
2853 /*
2854  * Create a new boundary in a way that it is very unlikely that this
2855  * will occur in the following text.  It would be easy to ensure
2856  * uniqueness if everything is either quoted-printable or base64
2857  * encoded (note that conversion is allowed), but because MIME bodies
2858  * may be nested, it may happen that the same boundary has already
2859  * been used.
2860  *
2861  *   boundary := 0*69<bchars> bcharsnospace
2862  *   bchars := bcharsnospace / " "
2863  *   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
2864  *                  "+" / "_" / "," / "-" / "." /
2865  *                  "/" / ":" / "=" / "?"
2866  *
2867  * some special characters removed because of buggy MTAs
2868  */
2869
2870 gchar *generate_mime_boundary(const gchar *prefix)
2871 {
2872         static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2873                              "abcdefghijklmnopqrstuvwxyz"
2874                              "1234567890+_./=";
2875         gchar buf_uniq[24];
2876         gint i;
2877
2878         for (i = 0; i < sizeof(buf_uniq) - 1; i++)
2879                 buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)];
2880         buf_uniq[i] = '\0';
2881
2882         return g_strdup_printf("%s_/%s", prefix ? prefix : "MP",
2883                                buf_uniq);
2884 }
2885
2886 gint change_file_mode_rw(FILE *fp, const gchar *file)
2887 {
2888 #if HAVE_FCHMOD
2889         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
2890 #else
2891         return g_chmod(file, S_IRUSR|S_IWUSR);
2892 #endif
2893 }
2894
2895 FILE *my_tmpfile(void)
2896 {
2897 #if HAVE_MKSTEMP || defined(G_OS_WIN32)
2898         const gchar suffix[] = ".XXXXXX";
2899         const gchar *tmpdir;
2900         guint tmplen;
2901         const gchar *progname;
2902         guint proglen;
2903         gchar *fname;
2904         gint fd;
2905         FILE *fp;
2906         gchar buf[2]="\0";
2907
2908         tmpdir = get_tmp_dir();
2909         tmplen = strlen(tmpdir);
2910         progname = g_get_prgname();
2911         if (progname == NULL)
2912                 progname = "claws-mail";
2913         proglen = strlen(progname);
2914         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
2915                 return tmpfile());
2916
2917         memcpy(fname, tmpdir, tmplen);
2918         fname[tmplen] = G_DIR_SEPARATOR;
2919         memcpy(fname + tmplen + 1, progname, proglen);
2920         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
2921
2922         fd = mkstemp(fname);
2923         if (fd < 0)
2924                 return tmpfile();
2925
2926 #ifndef G_OS_WIN32
2927         g_unlink(fname);
2928         
2929         /* verify that we can write in the file after unlinking */
2930         if (write(fd, buf, 1) < 0) {
2931                 close(fd);
2932                 return tmpfile();
2933         }
2934         
2935 #endif
2936
2937         fp = fdopen(fd, "w+b");
2938         if (!fp)
2939                 close(fd);
2940         else {
2941                 rewind(fp);
2942                 return fp;
2943         }
2944
2945 #endif /* HAVE_MKSTEMP || G_OS_WIN32 */
2946
2947         return tmpfile();
2948 }
2949
2950 FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
2951 {
2952         int fd;
2953 #ifdef G_OS_WIN32
2954         char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
2955                                           dir, G_DIR_SEPARATOR);
2956         fd = mkstemp_name(template, filename);
2957         g_free(template);
2958 #else
2959         *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
2960         fd = mkstemp(*filename);
2961 #endif
2962         return fdopen(fd, "w+");
2963 }
2964
2965 FILE *str_open_as_stream(const gchar *str)
2966 {
2967         FILE *fp;
2968         size_t len;
2969
2970         g_return_val_if_fail(str != NULL, NULL);
2971
2972         fp = my_tmpfile();
2973         if (!fp) {
2974                 FILE_OP_ERROR("str_open_as_stream", "my_tmpfile");
2975                 return NULL;
2976         }
2977
2978         len = strlen(str);
2979         if (len == 0) return fp;
2980
2981         if (fwrite(str, 1, len, fp) != len) {
2982                 FILE_OP_ERROR("str_open_as_stream", "fwrite");
2983                 fclose(fp);
2984                 return NULL;
2985         }
2986
2987         rewind(fp);
2988         return fp;
2989 }
2990
2991 gint str_write_to_file(const gchar *str, const gchar *file)
2992 {
2993         FILE *fp;
2994         size_t len;
2995
2996         g_return_val_if_fail(str != NULL, -1);
2997         g_return_val_if_fail(file != NULL, -1);
2998
2999         if ((fp = g_fopen(file, "wb")) == NULL) {
3000                 FILE_OP_ERROR(file, "fopen");
3001                 return -1;
3002         }
3003
3004         len = strlen(str);
3005         if (len == 0) {
3006                 fclose(fp);
3007                 return 0;
3008         }
3009
3010         if (fwrite(str, 1, len, fp) != len) {
3011                 FILE_OP_ERROR(file, "fwrite");
3012                 fclose(fp);
3013                 g_unlink(file);
3014                 return -1;
3015         }
3016
3017         if (fclose(fp) == EOF) {
3018                 FILE_OP_ERROR(file, "fclose");
3019                 g_unlink(file);
3020                 return -1;
3021         }
3022
3023         return 0;
3024 }
3025
3026 gchar *file_read_to_str(const gchar *file)
3027 {
3028         FILE *fp;
3029         gchar *str;
3030
3031         g_return_val_if_fail(file != NULL, NULL);
3032
3033         if ((fp = g_fopen(file, "rb")) == NULL) {
3034                 FILE_OP_ERROR(file, "fopen");
3035                 return NULL;
3036         }
3037
3038         str = file_read_stream_to_str(fp);
3039
3040         fclose(fp);
3041
3042         return str;
3043 }
3044
3045 gchar *file_read_stream_to_str(FILE *fp)
3046 {
3047         GByteArray *array;
3048         guchar buf[BUFSIZ];
3049         gint n_read;
3050         gchar *str;
3051
3052         g_return_val_if_fail(fp != NULL, NULL);
3053
3054         array = g_byte_array_new();
3055
3056         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3057                 if (n_read < sizeof(buf) && ferror(fp))
3058                         break;
3059                 g_byte_array_append(array, buf, n_read);
3060         }
3061
3062         if (ferror(fp)) {
3063                 FILE_OP_ERROR("file stream", "fread");
3064                 g_byte_array_free(array, TRUE);
3065                 return NULL;
3066         }
3067
3068         buf[0] = '\0';
3069         g_byte_array_append(array, buf, 1);
3070         str = (gchar *)array->data;
3071         g_byte_array_free(array, FALSE);
3072
3073         if (!g_utf8_validate(str, -1, NULL)) {
3074                 const gchar *src_codeset, *dest_codeset;
3075                 gchar *tmp = NULL;
3076                 src_codeset = conv_get_locale_charset_str();
3077                 dest_codeset = CS_UTF_8;
3078                 tmp = conv_codeset_strdup(str, src_codeset, dest_codeset);
3079                 g_free(str);
3080                 str = tmp;
3081         }
3082
3083         return str;
3084 }
3085
3086
3087 char *fgets_crlf(char *buf, int size, FILE *stream)
3088 {
3089         gboolean is_cr = FALSE;
3090         gboolean last_was_cr = FALSE;
3091         int c = 0;
3092         char *cs;
3093
3094         cs = buf;
3095         while (--size > 0 && (c = getc(stream)) != EOF)
3096         {
3097                 *cs++ = c;
3098                 is_cr = (c == '\r');
3099                 if (c == '\n') {
3100                         break;
3101                 }
3102                 if (last_was_cr) {
3103                         *(--cs) = '\n';
3104                         cs++;
3105                         ungetc(c, stream);
3106                         break;
3107                 }
3108                 last_was_cr = is_cr;
3109         }
3110         if (c == EOF && cs == buf)
3111                 return NULL;
3112
3113         *cs = '\0';
3114
3115         return buf;     
3116 }
3117
3118 static gint execute_async(gchar *const argv[])
3119 {
3120         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3121
3122         if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3123                           NULL, NULL, NULL, FALSE) == FALSE) {
3124                 g_warning("Couldn't execute command: %s\n", argv[0]);
3125                 return -1;
3126         }
3127
3128         return 0;
3129 }
3130
3131 static gint execute_sync(gchar *const argv[])
3132 {
3133         gint status;
3134
3135         g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
3136
3137         if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH,
3138                          NULL, NULL, NULL, NULL, &status, NULL) == FALSE) {
3139                 g_warning("Couldn't execute command: %s\n", argv[0]);
3140                 return -1;
3141         }
3142
3143 #ifdef G_OS_UNIX
3144         if (WIFEXITED(status))
3145                 return WEXITSTATUS(status);
3146         else
3147                 return -1;
3148 #else
3149         return status;
3150 #endif
3151 }
3152
3153 gint execute_command_line(const gchar *cmdline, gboolean async)
3154 {
3155         gchar **argv;
3156         gint ret;
3157
3158         debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
3159
3160         argv = strsplit_with_quote(cmdline, " ", 0);
3161
3162         if (async)
3163                 ret = execute_async(argv);
3164         else
3165                 ret = execute_sync(argv);
3166
3167         g_strfreev(argv);
3168
3169         return ret;
3170 }
3171
3172 gchar *get_command_output(const gchar *cmdline)
3173 {
3174         gchar *child_stdout;
3175         gint status;
3176
3177         g_return_val_if_fail(cmdline != NULL, NULL);
3178
3179         debug_print("get_command_output(): executing: %s\n", cmdline);
3180
3181         if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status,
3182                                       NULL) == FALSE) {
3183                 g_warning("Couldn't execute command: %s\n", cmdline);
3184                 return NULL;
3185         }
3186
3187         return child_stdout;
3188 }
3189 #ifndef MAEMO
3190 static gint is_unchanged_uri_char(char c)
3191 {
3192         switch (c) {
3193                 case '(':
3194                 case ')':
3195                         return 0;
3196                 default:
3197                         return 1;
3198         }
3199 }
3200
3201 static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
3202 {
3203         int i;
3204         int k;
3205
3206         k = 0;
3207         for(i = 0; i < strlen(uri) ; i++) {
3208                 if (is_unchanged_uri_char(uri[i])) {
3209                         if (k + 2 >= bufsize)
3210                                 break;
3211                         encoded_uri[k++] = uri[i];
3212                 }
3213                 else {
3214                         char * hexa = "0123456789ABCDEF";
3215
3216                         if (k + 4 >= bufsize)
3217                                 break;
3218                         encoded_uri[k++] = '%';
3219                         encoded_uri[k++] = hexa[uri[i] / 16];
3220                         encoded_uri[k++] = hexa[uri[i] % 16];
3221                 }
3222         }
3223         encoded_uri[k] = 0;
3224 }
3225 #endif
3226 gint open_uri(const gchar *uri, const gchar *cmdline)
3227 {
3228 #ifndef MAEMO
3229         gchar buf[BUFFSIZE];
3230         gchar *p;
3231         gchar encoded_uri[BUFFSIZE];
3232         g_return_val_if_fail(uri != NULL, -1);
3233
3234         /* an option to choose whether to use encode_uri or not ? */
3235         encode_uri(encoded_uri, BUFFSIZE, uri);
3236
3237         if (cmdline &&
3238             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3239             !strchr(p + 2, '%'))
3240                 g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
3241         else {
3242                 if (cmdline)
3243                         g_warning("Open URI command line is invalid "
3244                                   "(there must be only one '%%s'): %s",
3245                                   cmdline);
3246                 g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
3247         }
3248
3249         execute_command_line(buf, TRUE);
3250 #else
3251         extern osso_context_t *get_osso_context(void);
3252         osso_rpc_run_with_defaults(get_osso_context(), "osso_browser",
3253                                         OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL, 
3254                                         DBUS_TYPE_STRING, uri, DBUS_TYPE_INVALID);
3255 #endif
3256         return 0;
3257 }
3258
3259 gint open_txt_editor(const gchar *filepath, const gchar *cmdline)
3260 {
3261         gchar buf[BUFFSIZE];
3262         gchar *p;
3263
3264         g_return_val_if_fail(filepath != NULL, -1);
3265
3266         if (cmdline &&
3267             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
3268             !strchr(p + 2, '%'))
3269                 g_snprintf(buf, sizeof(buf), cmdline, filepath);
3270         else {
3271                 if (cmdline)
3272                         g_warning("Open Text Editor command line is invalid "
3273                                   "(there must be only one '%%s'): %s",
3274                                   cmdline);
3275                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, filepath);
3276         }
3277
3278         execute_command_line(buf, TRUE);
3279
3280         return 0;
3281 }
3282
3283 time_t remote_tzoffset_sec(const gchar *zone)
3284 {
3285         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
3286         gchar zone3[4];
3287         gchar *p;
3288         gchar c;
3289         gint iustz;
3290         gint offset;
3291         time_t remoteoffset;
3292
3293         strncpy(zone3, zone, 3);
3294         zone3[3] = '\0';
3295         remoteoffset = 0;
3296
3297         if (sscanf(zone, "%c%d", &c, &offset) == 2 &&
3298             (c == '+' || c == '-')) {
3299                 remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60;
3300                 if (c == '-')
3301                         remoteoffset = -remoteoffset;
3302         } else if (!strncmp(zone, "UT" , 2) ||
3303                    !strncmp(zone, "GMT", 2)) {
3304                 remoteoffset = 0;
3305         } else if (strlen(zone3) == 3) {
3306                 for (p = ustzstr; *p != '\0'; p += 3) {
3307                         if (!g_ascii_strncasecmp(p, zone3, 3)) {
3308                                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
3309                                 remoteoffset = iustz * 3600;
3310                                 break;
3311                         }
3312                 }
3313                 if (*p == '\0')
3314                         return -1;
3315         } else if (strlen(zone3) == 1) {
3316                 switch (zone[0]) {
3317                 case 'Z': remoteoffset =   0; break;
3318                 case 'A': remoteoffset =  -1; break;
3319                 case 'B': remoteoffset =  -2; break;
3320                 case 'C': remoteoffset =  -3; break;
3321                 case 'D': remoteoffset =  -4; break;
3322                 case 'E': remoteoffset =  -5; break;
3323                 case 'F': remoteoffset =  -6; break;
3324                 case 'G': remoteoffset =  -7; break;
3325                 case 'H': remoteoffset =  -8; break;
3326                 case 'I': remoteoffset =  -9; break;
3327                 case 'K': remoteoffset = -10; break; /* J is not used */
3328                 case 'L': remoteoffset = -11; break;
3329                 case 'M': remoteoffset = -12; break;
3330                 case 'N': remoteoffset =   1; break;
3331                 case 'O': remoteoffset =   2; break;
3332                 case 'P': remoteoffset =   3; break;
3333                 case 'Q': remoteoffset =   4; break;
3334                 case 'R': remoteoffset =   5; break;
3335                 case 'S': remoteoffset =   6; break;
3336                 case 'T': remoteoffset =   7; break;
3337                 case 'U': remoteoffset =   8; break;
3338                 case 'V': remoteoffset =   9; break;
3339                 case 'W': remoteoffset =  10; break;
3340                 case 'X': remoteoffset =  11; break;
3341                 case 'Y': remoteoffset =  12; break;
3342                 default:  remoteoffset =   0; break;
3343                 }
3344                 remoteoffset = remoteoffset * 3600;
3345         } else
3346                 return -1;
3347
3348         return remoteoffset;
3349 }
3350
3351 time_t tzoffset_sec(time_t *now)
3352 {
3353         struct tm gmt, *lt;
3354         gint off;
3355         struct tm buf1, buf2;
3356         
3357         gmt = *gmtime_r(now, &buf1);
3358         lt = localtime_r(now, &buf2);
3359
3360         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3361
3362         if (lt->tm_year < gmt.tm_year)
3363                 off -= 24 * 60;
3364         else if (lt->tm_year > gmt.tm_year)
3365                 off += 24 * 60;
3366         else if (lt->tm_yday < gmt.tm_yday)
3367                 off -= 24 * 60;
3368         else if (lt->tm_yday > gmt.tm_yday)
3369                 off += 24 * 60;
3370
3371         if (off >= 24 * 60)             /* should be impossible */
3372                 off = 23 * 60 + 59;     /* if not, insert silly value */
3373         if (off <= -24 * 60)
3374                 off = -(23 * 60 + 59);
3375
3376         return off * 60;
3377 }
3378
3379 /* calculate timezone offset */
3380 gchar *tzoffset(time_t *now)
3381 {
3382         static gchar offset_string[6];
3383         struct tm gmt, *lt;
3384         gint off;
3385         gchar sign = '+';
3386         struct tm buf1, buf2;
3387
3388         gmt = *gmtime_r(now, &buf1);
3389         lt = localtime_r(now, &buf2);
3390
3391         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
3392
3393         if (lt->tm_year < gmt.tm_year)
3394                 off -= 24 * 60;
3395         else if (lt->tm_year > gmt.tm_year)
3396                 off += 24 * 60;
3397         else if (lt->tm_yday < gmt.tm_yday)
3398                 off -= 24 * 60;
3399         else if (lt->tm_yday > gmt.tm_yday)
3400                 off += 24 * 60;
3401
3402         if (off < 0) {
3403                 sign = '-';
3404                 off = -off;
3405         }
3406
3407         if (off >= 24 * 60)             /* should be impossible */
3408                 off = 23 * 60 + 59;     /* if not, insert silly value */
3409
3410         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
3411
3412         return offset_string;
3413 }
3414
3415 void get_rfc822_date(gchar *buf, gint len)
3416 {
3417         struct tm *lt;
3418         time_t t;
3419         gchar day[4], mon[4];
3420         gint dd, hh, mm, ss, yyyy;
3421         struct tm buf1;
3422         gchar buf2[BUFFSIZE];
3423
3424         t = time(NULL);
3425         lt = localtime_r(&t, &buf1);
3426
3427         sscanf(asctime_r(lt, buf2), "%3s %3s %d %d:%d:%d %d\n",
3428                day, mon, &dd, &hh, &mm, &ss, &yyyy);
3429
3430         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
3431                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
3432 }
3433
3434 void debug_set_mode(gboolean mode)
3435 {
3436         debug_mode = mode;
3437 }
3438
3439 gboolean debug_get_mode(void)
3440 {
3441         return debug_mode;
3442 }
3443
3444 void debug_print_real(const gchar *format, ...)
3445 {
3446         va_list args;
3447         gchar buf[BUFFSIZE];
3448
3449         if (!debug_mode) return;
3450
3451         va_start(args, format);
3452         g_vsnprintf(buf, sizeof(buf), format, args);
3453         va_end(args);
3454
3455         g_print("%s", buf);
3456 }
3457
3458
3459 const char * debug_srcname(const char *file)
3460 {
3461         const char *s = strrchr (file, '/');
3462         return s? s+1:file;
3463 }
3464
3465
3466 void * subject_table_lookup(GHashTable *subject_table, gchar * subject)
3467 {
3468         if (subject == NULL)
3469                 subject = "";
3470         else
3471                 subject += subject_get_prefix_length(subject);
3472
3473         return g_hash_table_lookup(subject_table, subject);
3474 }
3475
3476 void subject_table_insert(GHashTable *subject_table, gchar * subject,
3477                           void * data)
3478 {
3479         if (subject == NULL || *subject == 0)
3480                 return;
3481         subject += subject_get_prefix_length(subject);
3482         g_hash_table_insert(subject_table, subject, data);
3483 }
3484
3485 void subject_table_remove(GHashTable *subject_table, gchar * subject)
3486 {
3487         if (subject == NULL)
3488                 return;
3489
3490         subject += subject_get_prefix_length(subject);
3491         g_hash_table_remove(subject_table, subject);
3492 }
3493
3494 /*!
3495  *\brief        Check if a string is prefixed with known (combinations)
3496  *              of prefixes. The function assumes that each prefix
3497  *              is terminated by zero or exactly _one_ space.
3498  *
3499  *\param        str String to check for a prefixes
3500  *
3501  *\return       int Number of chars in the prefix that should be skipped
3502  *              for a "clean" subject line. If no prefix was found, 0
3503  *              is returned.
3504  */
3505 int subject_get_prefix_length(const gchar *subject)
3506 {
3507         /*!< Array with allowable reply prefixes regexps. */
3508         static const gchar * const prefixes[] = {
3509                 "Re\\:",                        /* "Re:" */
3510                 "Re\\[[1-9][0-9]*\\]\\:",       /* "Re[XXX]:" (non-conforming news mail clients) */
3511                 "Antw\\:",                      /* "Antw:" (Dutch / German Outlook) */
3512                 "Aw\\:",                        /* "Aw:"   (German) */
3513                 "Antwort\\:",                   /* "Antwort:" (German Lotus Notes) */
3514                 "Res\\:",                       /* "Res:" (Brazilian Outlook) */
3515                 "Fw\\:",                        /* "Fw:" Forward */
3516                 "Enc\\:",                       /* "Enc:" Forward (Brazilian Outlook) */
3517                 "Odp\\:",                       /* "Odp:" Re (Polish Outlook) */
3518                 "Rif\\:",                       /* "Rif:" (Italian Outlook) */
3519                 "Sv\\:",                        /* "Sv" (Norwegian) */
3520                 "Vs\\:",                        /* "Vs" (Norwegian) */
3521                 "Ad\\:",                        /* "Ad" (Norwegian) */
3522                 "\347\255\224\345\244\215\\:"   /* "Re" (Chinese, UTF-8) */
3523                 /* add more */
3524         };
3525         const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
3526         int n;
3527         regmatch_t pos;
3528         static regex_t regex;
3529         static gboolean init_;
3530
3531         if (!subject) return 0;
3532         if (!*subject) return 0;
3533
3534         if (!init_) {
3535                 GString *s = g_string_new("");
3536
3537                 for (n = 0; n < PREFIXES; n++)
3538                         /* Terminate each prefix regexpression by a
3539                          * "\ ?" (zero or ONE space), and OR them */
3540                         g_string_append_printf(s, "(%s\\ ?)%s",
3541                                           prefixes[n],
3542                                           n < PREFIXES - 1 ?
3543                                           "|" : "");
3544
3545                 g_string_prepend(s, "(");
3546                 g_string_append(s, ")+");       /* match at least once */
3547                 g_string_prepend(s, "^\\ *");   /* from beginning of line */
3548
3549
3550                 /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
3551                  * TODO: Should this be       "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
3552                 if (regcomp(&regex, s->str, REG_EXTENDED | REG_ICASE)) {
3553                         debug_print("Error compiling regexp %s\n", s->str);
3554                         g_string_free(s, TRUE);
3555                         return 0;
3556                 } else {
3557                         init_ = TRUE;
3558                         g_string_free(s, TRUE);
3559                 }
3560         }
3561
3562         if (!regexec(&regex, subject, 1, &pos, 0) && pos.rm_so != -1)
3563                 return pos.rm_eo;
3564         else
3565                 return 0;
3566 }
3567
3568 static guint g_stricase_hash(gconstpointer gptr)
3569 {
3570         guint hash_result = 0;
3571         const char *str;
3572
3573         for (str = gptr; str && *str; str++) {
3574                 hash_result += toupper(*str);
3575         }
3576
3577         return hash_result;
3578 }
3579
3580 static gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
3581 {
3582         const char *str1 = gptr1;
3583         const char *str2 = gptr2;
3584
3585         return !strcasecmp(str1, str2);
3586 }
3587
3588 gint g_int_compare(gconstpointer a, gconstpointer b)
3589 {
3590         return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
3591 }
3592
3593 gchar *generate_msgid(gchar *buf, gint len)
3594 {
3595         struct tm *lt;
3596         time_t t;
3597         gchar *addr;
3598         struct tm buft;
3599
3600         t = time(NULL);
3601         lt = localtime_r(&t, &buft);
3602
3603         if (strcmp(buf, "") == 0) {
3604                 addr = g_strconcat("@", get_domain_name(), NULL);
3605         }
3606         else {
3607                 addr = g_strconcat("@", buf, NULL);
3608         }
3609
3610    &nbs